summaryrefslogtreecommitdiff
path: root/services/java/com/android/server/AppOpsService.java
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2013-07-17 17:23:25 -0700
committerDianne Hackborn <hackbod@google.com>2013-07-17 17:25:13 -0700
commite98f5dbe6b6f9f2cb6a73ee750faacda2596b34f (patch)
tree37817f45c1efd4f1338c2ec190e58e30d4f1c541 /services/java/com/android/server/AppOpsService.java
parent209bede6b9edb9171e5bee4077b48e35004a37b4 (diff)
Make it safe to use start/stop app ops outside of system proc
We now keep track of all of the active start operations per non-system process, so they can be cleaned up if the process goes away. Change-Id: I9d05f1e0281c47dbe1213de014f0491f1359685c
Diffstat (limited to 'services/java/com/android/server/AppOpsService.java')
-rw-r--r--services/java/com/android/server/AppOpsService.java182
1 files changed, 154 insertions, 28 deletions
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 7a107e7760df..6b4d24843d5b 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -40,6 +40,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -97,6 +98,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public final static class Op {
+ public final int uid;
+ public final String packageName;
public final int op;
public int mode;
public int duration;
@@ -104,7 +107,9 @@ public class AppOpsService extends IAppOpsService.Stub {
public long rejectTime;
public int nesting;
- public Op(int _op) {
+ public Op(int _uid, String _packageName, int _op) {
+ uid = _uid;
+ packageName = _packageName;
op = _op;
mode = AppOpsManager.MODE_ALLOWED;
}
@@ -112,10 +117,10 @@ public class AppOpsService extends IAppOpsService.Stub {
final SparseArray<ArrayList<Callback>> mOpModeWatchers
= new SparseArray<ArrayList<Callback>>();
- final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
- = new HashMap<String, ArrayList<Callback>>();
- final HashMap<IBinder, Callback> mModeWatchers
- = new HashMap<IBinder, Callback>();
+ final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers
+ = new ArrayMap<String, ArrayList<Callback>>();
+ final ArrayMap<IBinder, Callback> mModeWatchers
+ = new ArrayMap<IBinder, Callback>();
public final class Callback implements DeathRecipient {
final IAppOpsCallback mCallback;
@@ -138,6 +143,47 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
+
+ public final class ClientState extends Binder implements DeathRecipient {
+ final IBinder mAppToken;
+ final int mPid;
+ final ArrayList<Op> mStartedOps;
+
+ public ClientState(IBinder appToken) {
+ mAppToken = appToken;
+ mPid = Binder.getCallingPid();
+ if (appToken instanceof Binder) {
+ // For local clients, there is no reason to track them.
+ mStartedOps = null;
+ } else {
+ mStartedOps = new ArrayList<Op>();
+ try {
+ mAppToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClientState{" +
+ "mAppToken=" + mAppToken +
+ ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
+ '}';
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (AppOpsService.this) {
+ for (int i=mStartedOps.size()-1; i>=0; i--) {
+ finishOperationLocked(mStartedOps.get(i));
+ }
+ mClients.remove(mAppToken);
+ }
+ }
+ }
+
public AppOpsService(File storagePath) {
mFile = new AtomicFile(storagePath);
mHandler = new Handler();
@@ -380,21 +426,18 @@ public class AppOpsService extends IAppOpsService.Stub {
Callback cb = mModeWatchers.remove(callback.asBinder());
if (cb != null) {
cb.unlinkToDeath();
- for (int i=0; i<mOpModeWatchers.size(); i++) {
+ for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mOpModeWatchers.removeAt(i);
}
}
- if (mPackageModeWatchers.size() > 0) {
- Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
- while (it.hasNext()) {
- ArrayList<Callback> cbs = it.next();
- cbs.remove(cb);
- if (cbs.size() <= 0) {
- it.remove();
- }
+ for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
+ ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
+ cbs.remove(cb);
+ if (cbs.size() <= 0) {
+ mPackageModeWatchers.removeAt(i);
}
}
}
@@ -402,6 +445,18 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
+ public IBinder getToken(IBinder clientToken) {
+ synchronized (this) {
+ ClientState cs = mClients.get(clientToken);
+ if (cs == null) {
+ cs = new ClientState(clientToken);
+ mClients.put(clientToken, cs);
+ }
+ return cs;
+ }
+ }
+
+ @Override
public int checkOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
@@ -448,9 +503,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
- public int startOperation(int code, int uid, String packageName) {
+ public int startOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ ClientState client = (ClientState)token;
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
@@ -475,32 +531,46 @@ public class AppOpsService extends IAppOpsService.Stub {
op.duration = -1;
}
op.nesting++;
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
return AppOpsManager.MODE_ALLOWED;
}
}
@Override
- public void finishOperation(int code, int uid, String packageName) {
+ public void finishOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ ClientState client = (ClientState)token;
synchronized (this) {
Op op = getOpLocked(code, uid, packageName, true);
if (op == null) {
return;
}
- 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
- + " nesting=" + op.nesting);
+ if (client.mStartedOps != null) {
+ if (!client.mStartedOps.remove(op)) {
+ throw new IllegalStateException("Operation not started: uid" + op.uid
+ + " pkg=" + op.packageName + " op=" + op.op);
}
- op.nesting = 0;
+ }
+ finishOperationLocked(op);
+ }
+ }
+
+ void finishOperationLocked(Op op) {
+ if (op.nesting <= 1) {
+ if (op.nesting == 1) {
+ op.duration = (int)(System.currentTimeMillis() - op.time);
+ op.time += op.duration;
} else {
- op.nesting--;
+ Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
+ + op.packageName + " code " + op.op + " time=" + op.time
+ + " duration=" + op.duration + " nesting=" + op.nesting);
}
+ op.nesting = 0;
+ } else {
+ op.nesting--;
}
}
@@ -601,7 +671,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (!edit) {
return null;
}
- op = new Op(code);
+ op = new Op(ops.uid, ops.packageName, code);
ops.put(code, op);
}
if (edit) {
@@ -711,7 +781,7 @@ public class AppOpsService extends IAppOpsService.Stub {
String tagName = parser.getName();
if (tagName.equals("op")) {
- Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
+ Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
String mode = parser.getAttributeValue(null, "m");
if (mode != null) {
op.mode = Integer.parseInt(mode);
@@ -831,6 +901,62 @@ public class AppOpsService extends IAppOpsService.Stub {
synchronized (this) {
pw.println("Current AppOps Service state:");
final long now = System.currentTimeMillis();
+ boolean needSep = false;
+ if (mOpModeWatchers.size() > 0) {
+ needSep = true;
+ pw.println(" Op mode watchers:");
+ for (int i=0; i<mOpModeWatchers.size(); i++) {
+ pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
+ pw.println(":");
+ ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
+ for (int j=0; j<callbacks.size(); j++) {
+ pw.print(" #"); pw.print(j); pw.print(": ");
+ pw.println(callbacks.get(j));
+ }
+ }
+ }
+ if (mPackageModeWatchers.size() > 0) {
+ needSep = true;
+ pw.println(" Package mode watchers:");
+ for (int i=0; i<mPackageModeWatchers.size(); i++) {
+ pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
+ pw.println(":");
+ ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
+ for (int j=0; j<callbacks.size(); j++) {
+ pw.print(" #"); pw.print(j); pw.print(": ");
+ pw.println(callbacks.get(j));
+ }
+ }
+ }
+ if (mModeWatchers.size() > 0) {
+ needSep = true;
+ pw.println(" All mode watchers:");
+ for (int i=0; i<mModeWatchers.size(); i++) {
+ pw.print(" "); pw.print(mModeWatchers.keyAt(i));
+ pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
+ }
+ }
+ if (mClients.size() > 0) {
+ needSep = true;
+ pw.println(" Clients:");
+ for (int i=0; i<mClients.size(); i++) {
+ pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
+ ClientState cs = mClients.valueAt(i);
+ pw.print(" "); pw.println(cs);
+ if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
+ pw.println(" Started ops:");
+ for (int j=0; j<cs.mStartedOps.size(); j++) {
+ Op op = cs.mStartedOps.get(j);
+ pw.print(" "); pw.print("uid="); pw.print(op.uid);
+ pw.print(" pkg="); pw.print(op.packageName);
+ pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
+ }
+ }
+ }
+ }
+ if (needSep) {
+ pw.println();
+ }
for (int i=0; i<mUidOps.size(); i++) {
pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);