diff options
Diffstat (limited to 'core/java/android/app/AppOpsManager.java')
| -rw-r--r-- | core/java/android/app/AppOpsManager.java | 225 |
1 files changed, 120 insertions, 105 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index efeef1b07c0e..36d161dfe7b4 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2783,14 +2783,24 @@ public class AppOpsManager { private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>(); /** - * If a thread is currently executing a two-way binder transaction, this stores the op-codes of - * the app-ops that were noted during this transaction. + * Optimization: we need to propagate to IPCs whether the current thread is collecting + * app ops but using only the thread local above is too slow as it requires a map lookup + * on every IPC. We add this static var that is lockless and stores an OR-ed mask of the + * thread id's currently collecting ops, thus reducing the map lookup to a simple bit + * operation except the extremely unlikely case when threads with overlapping id bits + * execute op collecting ops. + */ + private static volatile long sThreadsListeningForOpNotedInBinderTransaction = 0L; + + /** + * If a thread is currently executing a two-way binder transaction, this stores the + * ops that were noted blaming any app (the caller, the caller of the caller, etc). * * @see #getNotedOpCollectionMode * @see #collectNotedOpSync */ - private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction = - new ThreadLocal<>(); + private static final ThreadLocal<ArrayMap<String, ArrayMap<String, long[]>>> + sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>(); /** Whether noting for an appop should be collected */ private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP]; @@ -8105,7 +8115,7 @@ public class AppOpsManager { SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag, collectionMode == COLLECT_ASYNC, message, shouldCollectMessage); - if (syncOp.getOpMode()== MODE_ALLOWED) { + if (syncOp.getOpMode() == MODE_ALLOWED) { if (collectionMode == COLLECT_SELF) { collectNotedOpForSelf(syncOp); } else if (collectionMode == COLLECT_SYNC) { @@ -8872,70 +8882,11 @@ public class AppOpsManager { * @hide */ public static void startNotedAppOpsCollection(int callingUid) { + sThreadsListeningForOpNotedInBinderTransaction |= Thread.currentThread().getId(); sBinderThreadCallingUid.set(callingUid); } /** - * State of a temporarily paused noted app-ops collection. - * - * @see #pauseNotedAppOpsCollection() - * - * @hide - */ - public static class PausedNotedAppOpsCollection { - final int mUid; - final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps; - - PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String, - long[]> collectedNotedAppOps) { - mUid = uid; - mCollectedNotedAppOps = collectedNotedAppOps; - } - } - - /** - * Temporarily suspend collection of noted app-ops when binder-thread calls into the other - * process. During such a call there might be call-backs coming back on the same thread which - * should not be accounted to the current collection. - * - * @return a state needed to resume the collection - * - * @hide - */ - public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() { - Integer previousUid = sBinderThreadCallingUid.get(); - if (previousUid != null) { - ArrayMap<String, long[]> previousCollectedNotedAppOps = - sAppOpsNotedInThisBinderTransaction.get(); - - sBinderThreadCallingUid.remove(); - sAppOpsNotedInThisBinderTransaction.remove(); - - return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps); - } - - return null; - } - - /** - * Resume a collection paused via {@link #pauseNotedAppOpsCollection}. - * - * @param prevCollection The state of the previous collection - * - * @hide - */ - public static void resumeNotedAppOpsCollection( - @Nullable PausedNotedAppOpsCollection prevCollection) { - if (prevCollection != null) { - sBinderThreadCallingUid.set(prevCollection.mUid); - - if (prevCollection.mCollectedNotedAppOps != null) { - sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps); - } - } - } - - /** * Finish collection of noted appops on this thread. * * <p>Called at the end of a two way binder transaction. @@ -8946,6 +8897,7 @@ public class AppOpsManager { */ public static void finishNotedAppOpsCollection() { sBinderThreadCallingUid.remove(); + sThreadsListeningForOpNotedInBinderTransaction &= ~Thread.currentThread().getId(); sAppOpsNotedInThisBinderTransaction.remove(); } @@ -8970,28 +8922,52 @@ public class AppOpsManager { * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded} * * @param syncOp the op and attribution tag to note for + * + * @hide */ - private void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) { + @TestApi + public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) { + collectNotedOpSync(sOpStrToOp.get(syncOp.getOp()), syncOp.getAttributionTag(), + syncOp.getPackageName()); + } + + /** + * Collect a noted op when inside of a two-way binder call. + * + * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded} + * + * @param code the op code to note for + * @param attributionTag the attribution tag to note for + * @param packageName the package to note for + */ + private static void collectNotedOpSync(int code, @Nullable String attributionTag, + @NonNull String packageName) { // If this is inside of a two-way binder call: // We are inside of a two-way binder call. Delivered to caller via // {@link #prefixParcelWithAppOpsIfNeeded} - int op = sOpStrToOp.get(syncOp.getOp()); - ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get(); + ArrayMap<String, ArrayMap<String, long[]>> appOpsNoted = + sAppOpsNotedInThisBinderTransaction.get(); if (appOpsNoted == null) { appOpsNoted = new ArrayMap<>(1); sAppOpsNotedInThisBinderTransaction.set(appOpsNoted); } - long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag()); + ArrayMap<String, long[]> packageAppOpsNotedForAttribution = appOpsNoted.get(packageName); + if (packageAppOpsNotedForAttribution == null) { + packageAppOpsNotedForAttribution = new ArrayMap<>(1); + appOpsNoted.put(packageName, packageAppOpsNotedForAttribution); + } + + long[] appOpsNotedForAttribution = packageAppOpsNotedForAttribution.get(attributionTag); if (appOpsNotedForAttribution == null) { appOpsNotedForAttribution = new long[2]; - appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution); + packageAppOpsNotedForAttribution.put(attributionTag, appOpsNotedForAttribution); } - if (op < 64) { - appOpsNotedForAttribution[0] |= 1L << op; + if (code < 64) { + appOpsNotedForAttribution[0] |= 1L << code; } else { - appOpsNotedForAttribution[1] |= 1L << (op - 64); + appOpsNotedForAttribution[1] |= 1L << (code - 64); } } @@ -9045,9 +9021,7 @@ public class AppOpsManager { } } - Integer binderUid = sBinderThreadCallingUid.get(); - - if (binderUid != null && binderUid == uid) { + if (isListeningForOpNotedInBinderTransaction()) { return COLLECT_SYNC; } else { return COLLECT_ASYNC; @@ -9064,21 +9038,31 @@ public class AppOpsManager { * * @hide */ + // TODO (b/186872903) Refactor how sync noted ops are propagaged. public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) { - ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get(); + final ArrayMap<String, ArrayMap<String, long[]>> notedAppOps = + sAppOpsNotedInThisBinderTransaction.get(); if (notedAppOps == null) { return; } p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER); - int numAttributionWithNotesAppOps = notedAppOps.size(); - p.writeInt(numAttributionWithNotesAppOps); + final int packageCount = notedAppOps.size(); + p.writeInt(packageCount); - for (int i = 0; i < numAttributionWithNotesAppOps; i++) { + for (int i = 0; i < packageCount; i++) { p.writeString(notedAppOps.keyAt(i)); - p.writeLong(notedAppOps.valueAt(i)[0]); - p.writeLong(notedAppOps.valueAt(i)[1]); + + final ArrayMap<String, long[]> notedTagAppOps = notedAppOps.valueAt(i); + final int tagCount = notedTagAppOps.size(); + p.writeInt(tagCount); + + for (int j = 0; j < tagCount; j++) { + p.writeString(notedTagAppOps.keyAt(j)); + p.writeLong(notedTagAppOps.valueAt(j)[0]); + p.writeLong(notedTagAppOps.valueAt(j)[1]); + } } } @@ -9093,36 +9077,57 @@ public class AppOpsManager { * @hide */ public static void readAndLogNotedAppops(@NonNull Parcel p) { - int numAttributionsWithNotedAppOps = p.readInt(); + final int packageCount = p.readInt(); + if (packageCount <= 0) { + return; + } + + final String myPackageName = ActivityThread.currentPackageName(); + if (myPackageName == null) { + return; + } - for (int i = 0; i < numAttributionsWithNotedAppOps; i++) { - String attributionTag = p.readString(); - long[] rawNotedAppOps = new long[2]; - rawNotedAppOps[0] = p.readLong(); - rawNotedAppOps[1] = p.readLong(); + synchronized (sLock) { + for (int i = 0; i < packageCount; i++) { + final String packageName = p.readString(); - if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) { - BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps); + final int tagCount = p.readInt(); + for (int j = 0; j < tagCount; j++) { + final String attributionTag = p.readString(); + final long[] rawNotedAppOps = new long[2]; + rawNotedAppOps[0] = p.readLong(); + rawNotedAppOps[1] = p.readLong(); - synchronized (sLock) { + if (rawNotedAppOps[0] == 0 && rawNotedAppOps[1] == 0) { + continue; + } + + final BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps); for (int code = notedAppOps.nextSetBit(0); code != -1; code = notedAppOps.nextSetBit(code + 1)) { - if (sOnOpNotedCallback != null) { - sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag)); - } else { - String message = getFormattedStackTrace(); - sUnforwardedOps.add( - new AsyncNotedAppOp(code, Process.myUid(), attributionTag, - message, System.currentTimeMillis())); - if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) { - sUnforwardedOps.remove(0); + if (myPackageName.equals(packageName)) { + if (sOnOpNotedCallback != null) { + sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, + attributionTag, packageName)); + } else { + String message = getFormattedStackTrace(); + sUnforwardedOps.add(new AsyncNotedAppOp(code, Process.myUid(), + attributionTag, message, System.currentTimeMillis())); + if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) { + sUnforwardedOps.remove(0); + } } + } else if (isListeningForOpNotedInBinderTransaction()) { + collectNotedOpSync(code, attributionTag, packageName); + } + } + for (int code = notedAppOps.nextSetBit(0); code != -1; + code = notedAppOps.nextSetBit(code + 1)) { + if (myPackageName.equals(packageName)) { + sMessageCollector.onNoted(new SyncNotedAppOp(code, + attributionTag, packageName)); } } - } - for (int code = notedAppOps.nextSetBit(0); code != -1; - code = notedAppOps.nextSetBit(code + 1)) { - sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag)); } } } @@ -9229,7 +9234,17 @@ public class AppOpsManager { * @hide */ public static boolean isListeningForOpNoted() { - return sOnOpNotedCallback != null || isCollectingStackTraces(); + return sOnOpNotedCallback != null || isListeningForOpNotedInBinderTransaction() + || isCollectingStackTraces(); + } + + /** + * @return whether we are in a binder transaction and collecting appops. + */ + private static boolean isListeningForOpNotedInBinderTransaction() { + return (sThreadsListeningForOpNotedInBinderTransaction + & Thread.currentThread().getId()) != 0 + && sBinderThreadCallingUid.get() != null; } /** |
