Merge "Partial App-ops service refactor."

This commit is contained in:
Pyuli Naithani
2022-08-08 16:43:14 +00:00
committed by Android (Google) Code Review
5 changed files with 469 additions and 116 deletions

View File

@@ -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<String, Ops> 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<ArraySet<ModeCallback>> 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<String, AttributedOp> 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<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
ArrayList<AppOpsManager.OpEntry> resOps = null;
final long elapsedNow = SystemClock.elapsedRealtime();
if (ops == null) {
resOps = new ArrayList<>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
resOps.add(getOpEntryForResult(curOp, elapsedNow));
resOps.add(getOpEntryForResult(curOp));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -2199,7 +2231,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (resOps == null) {
resOps = new ArrayList<>();
}
resOps.add(getOpEntryForResult(curOp, elapsedNow));
resOps.add(getOpEntryForResult(curOp));
}
}
}
@@ -2209,11 +2241,12 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable
private ArrayList<AppOpsManager.OpEntry> 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<ops.length; j++) {
int code = ops[j];
if (uidState.opModes.indexOfKey(code) >= 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<String, Ops> 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<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
final SparseIntArray opModes = uidState.opModes;
final SparseIntArray opModes = uidState.getNonDefaultUidModes();
final ArrayMap<String, Ops> 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);
}
}

View File

@@ -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();
}

View File

@@ -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<SparseIntArray> mUidModes = new SparseArray<>();
@GuardedBy("mLock")
final ArrayMap<String, SparseIntArray> 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();
}
}
}

View File

@@ -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();
}

View File

@@ -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++;