From bf3a714aea1f75da8762307e3af3478393fe8a18 Mon Sep 17 00:00:00 2001 From: Pyuli Naithani Date: Mon, 25 Jul 2022 16:18:21 +0000 Subject: [PATCH] Partial App-ops service refactor. Added a new Interface for AppOpsService. Currently this interface has funtions for accessing and modifying app-ops modes,i.e. package and Uid modes. In future CLs we'll be moving the callback for watching app-op mode changes to this interface as well as op restrictions(user and global). Added a new interface for Scheduling app-ops state persistence, so that there is only one class for persisting the app-ops state, reducing complexity. This CL also includes implementation of the interface, moving the current implementation of app-ops mode management to the impl. BUG: 240200048 Test: Manual Change-Id: I79a48a0afa9489b9281a6b41d92004a3f69e2e95 --- .../android/server/appop/AppOpsService.java | 246 ++++++++++-------- .../server/appop/AppOpsServiceInterface.java | 98 +++++++ .../LegacyAppOpsServiceInterfaceImpl.java | 198 ++++++++++++++ .../server/appop/PersistenceScheduler.java | 35 +++ .../server/appop/AppOpsUpgradeTest.java | 8 +- 5 files changed, 469 insertions(+), 116 deletions(-) create mode 100644 services/core/java/com/android/server/appop/AppOpsServiceInterface.java create mode 100644 services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java create mode 100644 services/core/java/com/android/server/appop/PersistenceScheduler.java diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index d38cd8ef61814..1d0adb86fd5c6 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -217,7 +217,7 @@ import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Consumer; -public class AppOpsService extends IAppOpsService.Stub { +public class AppOpsService extends IAppOpsService.Stub implements PersistenceScheduler { static final String TAG = "AppOps"; static final boolean DEBUG = false; @@ -402,6 +402,9 @@ public class AppOpsService extends IAppOpsService.Stub { /** Package Manager internal. Access via {@link #getPackageManagerInternal()} */ private @Nullable PackageManagerInternal mPackageManagerInternal; + /** Interface for app-op modes.*/ + @VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface; + /** * An unsynchronized pool of {@link OpEventProxyInfo} objects. */ @@ -558,7 +561,6 @@ public class AppOpsService extends IAppOpsService.Stub { public boolean pendingAppWidgetVisible; public ArrayMap pkgOps; - public SparseIntArray opModes; // true indicates there is an interested observer, false there isn't but it has such an op public SparseBooleanArray foregroundOps; @@ -569,15 +571,43 @@ public class AppOpsService extends IAppOpsService.Stub { } public void clear() { + mAppOpsServiceInterface.removeUid(uid); + if (pkgOps != null) { + for (String packageName : pkgOps.keySet()) { + mAppOpsServiceInterface.removePackage(packageName); + } + } pkgOps = null; - opModes = null; } public boolean isDefault() { + boolean areAllPackageModesDefault = true; + if (pkgOps != null) { + for (String packageName : pkgOps.keySet()) { + if (!mAppOpsServiceInterface.arePackageModesDefault(packageName)) { + areAllPackageModesDefault = false; + break; + } + } + } return (pkgOps == null || pkgOps.isEmpty()) - && (opModes == null || opModes.size() <= 0) && (state == UID_STATE_CACHED - && (pendingState == UID_STATE_CACHED)); + && (pendingState == UID_STATE_CACHED)) + && (mAppOpsServiceInterface.areUidModesDefault(uid) + && areAllPackageModesDefault); + } + + // Functions for uid mode access and manipulation. + public SparseIntArray getNonDefaultUidModes() { + return mAppOpsServiceInterface.getNonDefaultUidModes(uid); + } + + public int getUidMode(int op) { + return mAppOpsServiceInterface.getUidMode(uid, op); + } + + public boolean setUidMode(int op, int mode) { + return mAppOpsServiceInterface.setUidMode(uid, op, mode); } int evalMode(int op, int mode) { @@ -647,21 +677,20 @@ public class AppOpsService extends IAppOpsService.Stub { public void evalForegroundOps(SparseArray> watchers) { SparseBooleanArray which = null; hasForegroundWatchers = false; - if (opModes != null) { - for (int i = opModes.size() - 1; i >= 0; i--) { - if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) { - if (which == null) { - which = new SparseBooleanArray(); - } - evalForegroundWatchers(opModes.keyAt(i), watchers, which); + final SparseIntArray opModes = getNonDefaultUidModes(); + for (int i = opModes.size() - 1; i >= 0; i--) { + if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) { + if (which == null) { + which = new SparseBooleanArray(); } + evalForegroundWatchers(opModes.keyAt(i), watchers, which); } } if (pkgOps != null) { for (int i = pkgOps.size() - 1; i >= 0; i--) { Ops ops = pkgOps.valueAt(i); for (int j = ops.size() - 1; j >= 0; j--) { - if (ops.valueAt(j).mode == AppOpsManager.MODE_FOREGROUND) { + if (ops.valueAt(j).getMode() == AppOpsManager.MODE_FOREGROUND) { if (which == null) { which = new SparseBooleanArray(); } @@ -1451,8 +1480,6 @@ public class AppOpsService extends IAppOpsService.Stub { final UidState uidState; final @NonNull String packageName; - private @Mode int mode; - /** attributionTag -> AttributedOp */ final ArrayMap mAttributions = new ArrayMap<>(1); @@ -1461,15 +1488,17 @@ public class AppOpsService extends IAppOpsService.Stub { this.uid = uid; this.uidState = uidState; this.packageName = packageName; - this.mode = AppOpsManager.opToDefaultMode(op); } - int getMode() { - return mode; + @Mode int getMode() { + return mAppOpsServiceInterface.getPackageMode(packageName, this.op); + } + void setMode(@Mode int mode) { + mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode); } int evalMode() { - return uidState.evalMode(op, mode); + return uidState.evalMode(op, getMode()); } void removeAttributionsWithNoTime() { @@ -1503,7 +1532,7 @@ public class AppOpsService extends IAppOpsService.Stub { mAttributions.valueAt(i).createAttributedOpEntryLocked()); } - return new OpEntry(op, mode, attributionEntries); + return new OpEntry(op, getMode(), attributionEntries); } @NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) { @@ -1518,7 +1547,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - return new OpEntry(op, mode, attributionEntries); + return new OpEntry(op, getMode(), attributionEntries); } boolean isRunning() { @@ -1775,6 +1804,7 @@ public class AppOpsService extends IAppOpsService.Stub { public AppOpsService(File storagePath, Handler handler, Context context) { mContext = context; + mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this); LockGuard.installLock(this, LockGuard.INDEX_APP_OPS); mFile = new AtomicFile(storagePath, "appops"); @@ -1815,7 +1845,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState == null || uidState.pkgOps == null) { return; } - + mAppOpsServiceInterface.removePackage(pkgName); Ops removedOps = uidState.pkgOps.remove(pkgName); if (removedOps != null) { scheduleFastWriteLocked(); @@ -2037,25 +2067,27 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - Ops ops = null; + Ops removedOps = null; // Remove any package state if such. if (uidState.pkgOps != null) { - ops = uidState.pkgOps.remove(packageName); + removedOps = uidState.pkgOps.remove(packageName); + mAppOpsServiceInterface.removePackage(packageName); } // If we just nuked the last package state check if the UID is valid. - if (ops != null && uidState.pkgOps.isEmpty() + if (removedOps != null && uidState.pkgOps.isEmpty() && getPackagesForUid(uid).length <= 0) { + uidState.clear(); mUidStates.remove(uid); } - if (ops != null) { + if (removedOps != null) { scheduleFastWriteLocked(); - final int numOps = ops.size(); + final int numOps = removedOps.size(); for (int opNum = 0; opNum < numOps; opNum++) { - final Op op = ops.valueAt(opNum); + final Op op = removedOps.valueAt(opNum); final int numAttributions = op.mAttributions.size(); for (int attributionNum = 0; attributionNum < numAttributions; @@ -2080,6 +2112,7 @@ public class AppOpsService extends IAppOpsService.Stub { public void uidRemoved(int uid) { synchronized (this) { if (mUidStates.indexOfKey(uid) >= 0) { + mUidStates.get(uid).clear(); mUidStates.remove(uid); scheduleFastWriteLocked(); } @@ -2185,12 +2218,11 @@ public class AppOpsService extends IAppOpsService.Stub { private ArrayList collectOps(Ops pkgOps, int[] ops) { ArrayList resOps = null; - final long elapsedNow = SystemClock.elapsedRealtime(); if (ops == null) { resOps = new ArrayList<>(); for (int j=0; j(); } - resOps.add(getOpEntryForResult(curOp, elapsedNow)); + resOps.add(getOpEntryForResult(curOp)); } } } @@ -2209,11 +2241,12 @@ public class AppOpsService extends IAppOpsService.Stub { @Nullable private ArrayList collectUidOps(@NonNull UidState uidState, @Nullable int[] ops) { - if (uidState.opModes == null) { + final SparseIntArray opModes = uidState.getNonDefaultUidModes(); + if (opModes == null) { return null; } - int opModeCount = uidState.opModes.size(); + int opModeCount = opModes.size(); if (opModeCount == 0) { return null; } @@ -2221,25 +2254,24 @@ public class AppOpsService extends IAppOpsService.Stub { if (ops == null) { resOps = new ArrayList<>(); for (int i = 0; i < opModeCount; i++) { - int code = uidState.opModes.keyAt(i); - resOps.add(new OpEntry(code, uidState.opModes.get(code), Collections.emptyMap())); + int code = opModes.keyAt(i); + resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap())); } } else { for (int j=0; j= 0) { + if (opModes.indexOfKey(code) >= 0) { if (resOps == null) { resOps = new ArrayList<>(); } - resOps.add(new OpEntry(code, uidState.opModes.get(code), - Collections.emptyMap())); + resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap())); } } } return resOps; } - private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) { + private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) { return op.createEntryLocked(); } @@ -2484,15 +2516,18 @@ public class AppOpsService extends IAppOpsService.Stub { Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false); if (ops != null) { ops.remove(op.op); + op.setMode(AppOpsManager.opToDefaultMode(op.op)); if (ops.size() <= 0) { UidState uidState = ops.uidState; ArrayMap pkgOps = uidState.pkgOps; if (pkgOps != null) { pkgOps.remove(ops.packageName); + mAppOpsServiceInterface.removePackage(ops.packageName); if (pkgOps.isEmpty()) { uidState.pkgOps = null; } if (uidState.isDefault()) { + uidState.clear(); mUidStates.remove(uid); } } @@ -2548,33 +2583,18 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == defaultMode) { return; } - previousMode = MODE_DEFAULT; uidState = new UidState(uid); - uidState.opModes = new SparseIntArray(); - uidState.opModes.put(code, mode); mUidStates.put(uid, uidState); - scheduleWriteLocked(); - } else if (uidState.opModes == null) { - previousMode = MODE_DEFAULT; - if (mode != defaultMode) { - uidState.opModes = new SparseIntArray(); - uidState.opModes.put(code, mode); - scheduleWriteLocked(); - } + } + if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) { + previousMode = uidState.getUidMode(code); } else { - if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) { - return; - } - previousMode = uidState.opModes.get(code); - if (mode == defaultMode) { - uidState.opModes.delete(code); - if (uidState.opModes.size() <= 0) { - uidState.opModes = null; - } - } else { - uidState.opModes.put(code, mode); - } - scheduleWriteLocked(); + // doesn't look right but is legacy behavior. + previousMode = MODE_DEFAULT; + } + + if (!uidState.setUidMode(code, mode)) { + return; } uidState.evalForegroundOps(mOpModeWatchers); if (mode != MODE_ERRORED && mode != previousMode) { @@ -2806,9 +2826,10 @@ public class AppOpsService extends IAppOpsService.Stub { UidState uidState = getUidStateLocked(uid, false); Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true); if (op != null) { - if (op.mode != mode) { - previousMode = op.mode; - op.mode = mode; + if (op.getMode() != mode) { + previousMode = op.getMode(); + op.setMode(mode); + if (uidState != null) { uidState.evalForegroundOps(mOpModeWatchers); } @@ -2976,17 +2997,14 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = mUidStates.size() - 1; i >= 0; i--) { UidState uidState = mUidStates.valueAt(i); - SparseIntArray opModes = uidState.opModes; + SparseIntArray opModes = uidState.getNonDefaultUidModes(); if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) { final int uidOpCount = opModes.size(); for (int j = uidOpCount - 1; j >= 0; j--) { final int code = opModes.keyAt(j); if (AppOpsManager.opAllowsReset(code)) { int previousMode = opModes.valueAt(j); - opModes.removeAt(j); - if (opModes.size() <= 0) { - uidState.opModes = null; - } + uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code)); for (String packageName : getPackagesForUid(uidState.uid)) { callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, previousMode, mOpModeWatchers.get(code)); @@ -3028,9 +3046,9 @@ public class AppOpsService extends IAppOpsService.Stub { continue; } if (AppOpsManager.opAllowsReset(curOp.op) - && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) { - int previousMode = curOp.mode; - curOp.mode = AppOpsManager.opToDefaultMode(curOp.op); + && curOp.getMode() != AppOpsManager.opToDefaultMode(curOp.op)) { + int previousMode = curOp.getMode(); + curOp.setMode(AppOpsManager.opToDefaultMode(curOp.op)); changed = true; uidChanged = true; final int uid = curOp.uidState.uid; @@ -3049,9 +3067,11 @@ public class AppOpsService extends IAppOpsService.Stub { } if (pkgOps.size() == 0) { it.remove(); + mAppOpsServiceInterface.removePackage(packageName); } } if (uidState.isDefault()) { + uidState.clear(); mUidStates.remove(uidState.uid); } if (uidChanged) { @@ -3267,16 +3287,16 @@ public class AppOpsService extends IAppOpsService.Stub { } code = AppOpsManager.opToSwitch(code); UidState uidState = getUidStateLocked(uid, false); - if (uidState != null && uidState.opModes != null - && uidState.opModes.indexOfKey(code) >= 0) { - final int rawMode = uidState.opModes.get(code); + if (uidState != null + && uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) { + final int rawMode = uidState.getUidMode(code); return raw ? rawMode : uidState.evalMode(code, rawMode); } Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } - return raw ? op.mode : op.evalMode(); + return raw ? op.getMode() : op.evalMode(); } } @@ -3502,8 +3522,8 @@ public class AppOpsService extends IAppOpsService.Stub { } // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. - if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { - final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode)); + if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) { + final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode)); if (uidMode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " @@ -4018,8 +4038,8 @@ public class AppOpsService extends IAppOpsService.Stub { final int switchCode = AppOpsManager.opToSwitch(code); // If there is a non-default per UID policy (we set UID op mode only if // non-default) it takes over, otherwise use the per package policy. - if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) { - final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode)); + if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) { + final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode)); if (!shouldStartForMode(uidMode, startIfModeDefault)) { if (DEBUG) { Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code " @@ -4516,9 +4536,8 @@ public class AppOpsService extends IAppOpsService.Stub { continue; } - if (uidState.opModes != null - && uidState.opModes.indexOfKey(code) >= 0 - && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) { + if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code) + && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChangedForAllPkgsInUid, this, code, uidState.uid, true, null)); @@ -4536,7 +4555,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (op == null) { continue; } - if (op.mode == AppOpsManager.MODE_FOREGROUND) { + if (op.getMode() == AppOpsManager.MODE_FOREGROUND) { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChanged, this, callback, code, uidState.uid, @@ -4811,14 +4830,16 @@ public class AppOpsService extends IAppOpsService.Stub { return ops; } - private void scheduleWriteLocked() { + @Override + public void scheduleWriteLocked() { if (!mWriteScheduled) { mWriteScheduled = true; mHandler.postDelayed(mWriteRunner, WRITE_DELAY); } } - private void scheduleFastWriteLocked() { + @Override + public void scheduleFastWriteLocked() { if (!mFastWriteScheduled) { mWriteScheduled = true; mFastWriteScheduled = true; @@ -4929,6 +4950,7 @@ public class AppOpsService extends IAppOpsService.Stub { } boolean success = false; mUidStates.clear(); + mAppOpsServiceInterface.clearAllModes(); try { TypedXmlPullParser parser = Xml.resolvePullParser(stream); int type; @@ -4977,6 +4999,7 @@ public class AppOpsService extends IAppOpsService.Stub { } finally { if (!success) { mUidStates.clear(); + mAppOpsServiceInterface.clearAllModes(); } try { stream.close(); @@ -4996,11 +5019,12 @@ public class AppOpsService extends IAppOpsService.Stub { if (uidState == null) { continue; } - if (uidState.opModes != null) { - final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND); + SparseIntArray opModes = uidState.getNonDefaultUidModes(); + if (opModes != null) { + final int idx = opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND); if (idx >= 0) { - uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, - uidState.opModes.valueAt(idx)); + uidState.setUidMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, + opModes.valueAt(idx)); } } if (uidState.pkgOps == null) { @@ -5011,10 +5035,10 @@ public class AppOpsService extends IAppOpsService.Stub { Ops ops = uidState.pkgOps.valueAt(j); if (ops != null) { final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND); - if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) { + if (op != null && op.getMode() != AppOpsManager.opToDefaultMode(op.op)) { final Op copy = new Op(op.uidState, op.packageName, AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.uid); - copy.mode = op.mode; + copy.setMode(op.getMode()); ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy); changed = true; } @@ -5142,7 +5166,7 @@ public class AppOpsService extends IAppOpsService.Stub { Op op = new Op(uidState, pkgName, opCode, uidState.uid); final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op)); - op.mode = mode; + op.setMode(mode); int outerDepth = parser.getDepth(); int type; @@ -5199,16 +5223,9 @@ public class AppOpsService extends IAppOpsService.Stub { UidState uidState = mUidStates.valueAt(uidStateNum); int uid = mUidStates.keyAt(uidStateNum); - SparseIntArray opModes = uidState.opModes; + SparseIntArray opModes = uidState.getNonDefaultUidModes(); if (opModes != null && opModes.size() > 0) { - uidStatesClone.put(uid, new SparseIntArray(opModes.size())); - - final int opCount = opModes.size(); - for (int opCountNum = 0; opCountNum < opCount; opCountNum++) { - uidStatesClone.get(uid).put( - opModes.keyAt(opCountNum), - opModes.valueAt(opCountNum)); - } + uidStatesClone.put(uid, opModes); } } } @@ -6314,15 +6331,15 @@ public class AppOpsService extends IAppOpsService.Stub { } for (int i=0; i pkgOps = uidState.pkgOps; if (dumpWatchers || dumpHistory) { continue; } if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) { - boolean hasOp = dumpOp < 0 || (uidState.opModes != null - && uidState.opModes.indexOfKey(dumpOp) >= 0); + boolean hasOp = dumpOp < 0 || (opModes != null + && opModes.indexOfKey(dumpOp) >= 0); boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i); boolean hasMode = dumpMode < 0; if (!hasMode && opModes != null) { @@ -6342,7 +6359,7 @@ public class AppOpsService extends IAppOpsService.Stub { } if (!hasMode) { for (int opi = 0; !hasMode && opi < ops.size(); opi++) { - if (ops.valueAt(opi).mode == dumpMode) { + if (ops.valueAt(opi).getMode() == dumpMode) { hasMode = true; } } @@ -6437,7 +6454,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (dumpOp >= 0 && dumpOp != opCode) { continue; } - if (dumpMode >= 0 && dumpMode != op.mode) { + if (dumpMode >= 0 && dumpMode != op.getMode()) { continue; } if (!printedPackage) { @@ -6445,14 +6462,14 @@ public class AppOpsService extends IAppOpsService.Stub { printedPackage = true; } pw.print(" "); pw.print(AppOpsManager.opToName(opCode)); - pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode)); + pw.print(" ("); pw.print(AppOpsManager.modeToName(op.getMode())); final int switchOp = AppOpsManager.opToSwitch(opCode); if (switchOp != opCode) { pw.print(" / switch "); pw.print(AppOpsManager.opToName(switchOp)); final Op switchObj = ops.get(switchOp); - int mode = switchObj != null ? switchObj.mode - : AppOpsManager.opToDefaultMode(switchOp); + int mode = switchObj == null + ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode(); pw.print("="); pw.print(AppOpsManager.modeToName(mode)); } pw.println("): "); @@ -6696,7 +6713,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) { Ops ops = uidState.pkgOps.valueAt(pkgNum); Op op = ops != null ? ops.get(code) : null; - if (op == null || (op.mode != MODE_ALLOWED && op.mode != MODE_FOREGROUND)) { + if (op == null || (op.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) { continue; } int numAttrTags = op.mAttributions.size(); @@ -6817,6 +6834,7 @@ public class AppOpsService extends IAppOpsService.Stub { return; } Ops removedOps = uidState.pkgOps.remove(packageName); + mAppOpsServiceInterface.removePackage(packageName); if (removedOps != null) { scheduleFastWriteLocked(); } @@ -7136,10 +7154,12 @@ public class AppOpsService extends IAppOpsService.Stub { return false; } + @GuardedBy("this") private void removeUidsForUserLocked(int userHandle) { for (int i = mUidStates.size() - 1; i >= 0; --i) { final int uid = mUidStates.keyAt(i); if (UserHandle.getUserId(uid) == userHandle) { + mUidStates.valueAt(i).clear(); mUidStates.removeAt(i); } } diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java new file mode 100644 index 0000000000000..cd5ea120f8784 --- /dev/null +++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appop; +import android.annotation.NonNull; +import android.app.AppOpsManager.Mode; +import android.util.SparseIntArray; +/** + * Interface for accessing and modifying modes for app-ops i.e. package and uid modes. + * In the future this interface will also include mode callbacks and op restrictions. + */ +public interface AppOpsServiceInterface { + /** + * Returns a copy of non-default app-ops with op as keys and their modes as values for a uid. + * Returns an empty SparseIntArray if nothing is set. + * @param uid for which we need the app-ops and their modes. + */ + SparseIntArray getNonDefaultUidModes(int uid); + + /** + * Returns the app-op mode for a particular app-op of a uid. + * Returns default op mode if the op mode for particular uid and op is not set. + * @param uid user id for which we need the mode. + * @param op app-op for which we need the mode. + * @return mode of the app-op. + */ + int getUidMode(int uid, int op); + + /** + * Set the app-op mode for a particular uid and op. + * The mode is not set if the mode is the same as the default mode for the op. + * @param uid user id for which we want to set the mode. + * @param op app-op for which we want to set the mode. + * @param mode mode for the app-op. + * @return true if op mode is changed. + */ + boolean setUidMode(int uid, int op, @Mode int mode); + + /** + * Gets the app-op mode for a particular package. + * Returns default op mode if the op mode for the particular package is not set. + * @param packageName package name for which we need the op mode. + * @param op app-op for which we need the mode. + * @return the mode of the app-op. + */ + int getPackageMode(@NonNull String packageName, int op); + + /** + * Sets the app-op mode for a particular package. + * @param packageName package name for which we need to set the op mode. + * @param op app-op for which we need to set the mode. + * @param mode the mode of the app-op. + */ + void setPackageMode(@NonNull String packageName, int op, @Mode int mode); + + /** + * Stop tracking any app-op modes for a package. + * @param packageName Name of the package for which we want to remove all mode tracking. + */ + boolean removePackage(@NonNull String packageName); + + /** + * Stop tracking any app-op modes for this uid. + * @param uid user id for which we want to remove all tracking. + */ + void removeUid(int uid); + + /** + * Returns true if all uid modes for this uid are + * in default state. + * @param uid user id + */ + boolean areUidModesDefault(int uid); + + /** + * Returns true if all package modes for this package name are + * in default state. + * @param packageName package name. + */ + boolean arePackageModesDefault(String packageName); + + /** + * Stop tracking app-op modes for all uid and packages. + */ + void clearAllModes(); +} diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java new file mode 100644 index 0000000000000..c27c0d3de5d75 --- /dev/null +++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appop; + +import android.annotation.NonNull; +import android.app.AppOpsManager; +import android.app.AppOpsManager.Mode; +import android.util.ArrayMap; +import android.util.SparseArray; +import android.util.SparseIntArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + + +/** + * Legacy implementation for App-ops service's app-op mode (uid and package) storage and access. + * In the future this class will also include mode callbacks and op restrictions. + */ +public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface { + + // Should be the same object that the AppOpsService is using for locking. + final Object mLock; + + @GuardedBy("mLock") + @VisibleForTesting + final SparseArray mUidModes = new SparseArray<>(); + + @GuardedBy("mLock") + final ArrayMap mPackageModes = new ArrayMap<>(); + + final PersistenceScheduler mPersistenceScheduler; + + + LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler, + @NonNull Object lock) { + this.mPersistenceScheduler = persistenceScheduler; + this.mLock = lock; + } + + @Override + public SparseIntArray getNonDefaultUidModes(int uid) { + synchronized (mLock) { + SparseIntArray opModes = mUidModes.get(uid, null); + if (opModes == null) { + return new SparseIntArray(); + } + return opModes.clone(); + } + } + + @Override + public int getUidMode(int uid, int op) { + synchronized (mLock) { + SparseIntArray opModes = mUidModes.get(uid, null); + if (opModes == null) { + return AppOpsManager.opToDefaultMode(op); + } + return opModes.get(op, AppOpsManager.opToDefaultMode(op)); + } + } + + @Override + public boolean setUidMode(int uid, int op, int mode) { + final int defaultMode = AppOpsManager.opToDefaultMode(op); + synchronized (mLock) { + SparseIntArray opModes = mUidModes.get(uid, null); + if (opModes == null) { + if (mode != defaultMode) { + opModes = new SparseIntArray(); + mUidModes.put(uid, opModes); + opModes.put(op, mode); + mPersistenceScheduler.scheduleWriteLocked(); + } + } else { + if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) { + return false; + } + if (mode == defaultMode) { + opModes.delete(op); + if (opModes.size() <= 0) { + opModes = null; + mUidModes.delete(uid); + } + } else { + opModes.put(op, mode); + } + mPersistenceScheduler.scheduleWriteLocked(); + } + } + return true; + } + + @Override + public int getPackageMode(String packageName, int op) { + synchronized (mLock) { + SparseIntArray opModes = mPackageModes.getOrDefault(packageName, null); + if (opModes == null) { + return AppOpsManager.opToDefaultMode(op); + } + return opModes.get(op, AppOpsManager.opToDefaultMode(op)); + } + } + + @Override + public void setPackageMode(String packageName, int op, @Mode int mode) { + final int defaultMode = AppOpsManager.opToDefaultMode(op); + synchronized (mLock) { + SparseIntArray opModes = mPackageModes.get(packageName); + if (opModes == null) { + if (mode != defaultMode) { + opModes = new SparseIntArray(); + mPackageModes.put(packageName, opModes); + opModes.put(op, mode); + mPersistenceScheduler.scheduleWriteLocked(); + } + } else { + if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) { + return; + } + if (mode == defaultMode) { + opModes.delete(op); + if (opModes.size() <= 0) { + opModes = null; + mPackageModes.remove(packageName); + } + } else { + opModes.put(op, mode); + } + mPersistenceScheduler.scheduleWriteLocked(); + } + } + } + + @Override + public void removeUid(int uid) { + synchronized (mLock) { + SparseIntArray opModes = mUidModes.get(uid); + if (opModes == null) { + return; + } + mUidModes.remove(uid); + mPersistenceScheduler.scheduleFastWriteLocked(); + } + } + + + @Override + public boolean areUidModesDefault(int uid) { + synchronized (mLock) { + SparseIntArray opModes = mUidModes.get(uid); + return (opModes == null || opModes.size() <= 0); + } + } + + @Override + public boolean arePackageModesDefault(String packageMode) { + synchronized (mLock) { + SparseIntArray opModes = mPackageModes.get(packageMode); + return (opModes == null || opModes.size() <= 0); + } + } + + @Override + public boolean removePackage(String packageName) { + synchronized (mLock) { + SparseIntArray ops = mPackageModes.remove(packageName); + if (ops != null) { + mPersistenceScheduler.scheduleFastWriteLocked(); + return true; + } + return false; + } + } + + @Override + public void clearAllModes() { + synchronized (mLock) { + mUidModes.clear(); + mPackageModes.clear(); + } + } + +} diff --git a/services/core/java/com/android/server/appop/PersistenceScheduler.java b/services/core/java/com/android/server/appop/PersistenceScheduler.java new file mode 100644 index 0000000000000..e50b6586b4fe9 --- /dev/null +++ b/services/core/java/com/android/server/appop/PersistenceScheduler.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appop; + +/** + * Interface that allows callers to persist AppOpsService's internal state + * to disk. + */ +public interface PersistenceScheduler { + + /** + * Schedules disk writes for appOpsService and it's internal states. + */ + void scheduleWriteLocked(); + + /** + * Schedules fast disk writes for appOpsService and it's internal states. + */ + void scheduleFastWriteLocked(); + +} diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java index 99693d28f378f..7111047956bf5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java @@ -35,6 +35,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TypedXmlPullParser; import android.util.Xml; @@ -97,9 +98,10 @@ public class AppOpsUpgradeTest { final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2); for(int i = 0; i < uidStates.size(); i++) { final AppOpsService.UidState uidState = uidStates.valueAt(i); - if (uidState.opModes != null) { - final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1); - final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2); + SparseIntArray opModes = uidState.getNonDefaultUidModes(); + if (opModes != null) { + final int uidMode1 = opModes.get(op1, defaultModeOp1); + final int uidMode2 = opModes.get(op2, defaultModeOp2); assertEquals(uidMode1, uidMode2); if (uidMode1 != defaultModeOp1) { numberOfNonDefaultOps++;