App ops: cleanup, handle root and shell, perms.
Rework how the shell user is defined so that it is
associated with an actual apk, instead of being a free
roaming uid with special permissions assigned to it.
This allows us to correctly account for its operations
in app ops.
Implement a special case for the root user in app ops --
it is always allowed, always with the package name "root".
Add various code to take care of cleaning up package state
from app ops -- when packages are uninstalled, and during
boot if any packages currently being stored no longer exist.
Also fix a bug in the activity manager to correctly grant
permissions in all cases when onNewIntent() is being called.
Change-Id: Iae9f6d793ee48b93518c984ad957e46ae4582581
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index e94d03c..a402642 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -149,6 +149,61 @@
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
}
+ public void systemReady() {
+ synchronized (this) {
+ boolean changed = false;
+ for (int i=0; i<mUidOps.size(); i++) {
+ HashMap<String, Ops> pkgs = mUidOps.valueAt(i);
+ Iterator<Ops> it = pkgs.values().iterator();
+ while (it.hasNext()) {
+ Ops ops = it.next();
+ int curUid;
+ try {
+ curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
+ UserHandle.getUserId(ops.uid));
+ } catch (NameNotFoundException e) {
+ curUid = -1;
+ }
+ if (curUid != ops.uid) {
+ Slog.i(TAG, "Pruning old package " + ops.packageName
+ + "/" + ops.uid + ": new uid=" + curUid);
+ it.remove();
+ changed = true;
+ }
+ }
+ if (pkgs.size() <= 0) {
+ mUidOps.removeAt(i);
+ }
+ }
+ if (changed) {
+ scheduleWriteLocked();
+ }
+ }
+ }
+
+ public void packageRemoved(int uid, String packageName) {
+ synchronized (this) {
+ HashMap<String, Ops> pkgs = mUidOps.get(uid);
+ if (pkgs != null) {
+ if (pkgs.remove(packageName) != null) {
+ if (pkgs.size() <= 0) {
+ mUidOps.remove(uid);
+ }
+ scheduleWriteLocked();
+ }
+ }
+ }
+ }
+
+ public void uidRemoved(int uid) {
+ synchronized (this) {
+ if (mUidOps.indexOfKey(uid) >= 0) {
+ mUidOps.remove(uid);
+ scheduleWriteLocked();
+ }
+ }
+ }
+
public void shutdown() {
Slog.w(TAG, "Writing app ops before shutdown...");
boolean doWrite = false;
@@ -258,6 +313,25 @@
}
repCbs.addAll(cbs);
}
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ // If going into the default mode, prune this op
+ // if there is nothing else interesting in it.
+ if (op.time == 0 && op.rejectTime == 0) {
+ Ops ops = getOpsLocked(uid, packageName, false);
+ if (ops != null) {
+ ops.remove(op.op);
+ if (ops.size() <= 0) {
+ HashMap<String, Ops> pkgOps = mUidOps.get(uid);
+ if (pkgOps != null) {
+ pkgOps.remove(ops.packageName);
+ if (pkgOps.size() <= 0) {
+ mUidOps.remove(uid);
+ }
+ }
+ }
+ }
+ }
+ }
scheduleWriteNowLocked();
}
}
@@ -368,6 +442,7 @@
if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ " package " + packageName);
op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
return AppOpsManager.MODE_ALLOWED;
}
}
@@ -396,6 +471,7 @@
+ " package " + packageName);
if (op.nesting == 0) {
op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
op.duration = -1;
}
op.nesting++;
@@ -415,6 +491,7 @@
if (op.nesting <= 1) {
if (op.nesting == 1) {
op.duration = (int)(System.currentTimeMillis() - op.time);
+ op.time += op.duration;
} else {
Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
+ " code " + code + " time=" + op.time + " duration=" + op.duration
@@ -454,6 +531,11 @@
pkgOps = new HashMap<String, Ops>();
mUidOps.put(uid, pkgOps);
}
+ if (uid == 0) {
+ packageName = "root";
+ } else if (uid == Process.SHELL_UID) {
+ packageName = "com.android.shell";
+ }
Ops ops = pkgOps.get(packageName);
if (ops == null) {
if (!edit) {
@@ -461,23 +543,25 @@
}
// This is the first time we have seen this package name under this uid,
// so let's make sure it is valid.
- final long ident = Binder.clearCallingIdentity();
- try {
- int pkgUid = -1;
+ if (uid != 0) {
+ final long ident = Binder.clearCallingIdentity();
try {
- pkgUid = mContext.getPackageManager().getPackageUid(packageName,
- UserHandle.getUserId(uid));
- } catch (NameNotFoundException e) {
+ int pkgUid = -1;
+ try {
+ pkgUid = mContext.getPackageManager().getPackageUid(packageName,
+ UserHandle.getUserId(uid));
+ } catch (NameNotFoundException e) {
+ }
+ if (pkgUid != uid) {
+ // Oops! The package name is not valid for the uid they are calling
+ // under. Abort.
+ Slog.w(TAG, "Bad call: specified package " + packageName
+ + " under uid " + uid + " but it is really " + pkgUid);
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- if (pkgUid != uid) {
- // Oops! The package name is not valid for the uid they are calling
- // under. Abort.
- Slog.w(TAG, "Bad call: specified package " + packageName
- + " under uid " + uid + " but it is really " + pkgUid);
- return null;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
}
ops = new Ops(packageName, uid);
pkgOps.put(packageName, ops);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7b6e79e..4fbacb8 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7925,7 +7925,8 @@
}
mDidUpdate = true;
}
-
+
+ mAppOpsService.systemReady();
mSystemReady = true;
if (!mStartRunning) {
return;
@@ -11779,6 +11780,7 @@
synchronized (bs) {
bs.removeUidStatsLocked(uid);
}
+ mAppOpsService.uidRemoved(uid);
}
} else {
// If resources are unavailable just force stop all
@@ -11804,6 +11806,10 @@
if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
new String[] {ssp}, userId);
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ mAppOpsService.packageRemoved(
+ intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
+ }
}
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index ba2e47a..cde17c9 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -574,6 +574,9 @@
*/
final void deliverNewIntentLocked(int callingUid, Intent intent) {
boolean sent = false;
+ // The activity now gets access to the data associated with this Intent.
+ service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+ intent, getUriPermissionsLocked());
// We want to immediately deliver the intent to the activity if
// it is currently the top resumed activity... however, if the
// device is sleeping, then all activities are stopped, so in that
@@ -586,8 +589,6 @@
ArrayList<Intent> ar = new ArrayList<Intent>();
intent = new Intent(intent);
ar.add(intent);
- service.grantUriPermissionFromIntentLocked(callingUid, packageName,
- intent, getUriPermissionsLocked());
app.thread.scheduleNewIntent(ar, appToken);
sent = true;
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 46d2cca..e26f8fd 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -185,6 +185,7 @@
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
+ private static final int SHELL_UID = Process.SHELL_UID;
private static final boolean GET_CERTIFICATES = true;
@@ -974,6 +975,7 @@
mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {