summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Philip P. Moltmann <moltmann@google.com> 2020-02-06 18:21:52 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-02-06 18:21:52 +0000
commit053dbad9fd598965f4f99cc2a717934107d27b14 (patch)
tree1dfbfda1779486865ef491ba47dee00d719ed715
parent8301f05cd78f1fff3197d225515dd082ebbd606e (diff)
parentfe2fc3a9bc878cf34fedd665f9214bad6c43aafa (diff)
Merge changes from topic "AppOpsFgBackgroundMode"
* changes: Package manager is not ready when reading app-ops state Use setUidMode when reading state Expose syntax for app-ops settings as test-api Callback startWatchingMode with the right op Force update uid state when pending uid state is applied Notify all packages is uid-mode is changed
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/app/AppOpsManager.java46
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java240
-rw-r--r--services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml (renamed from services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml)0
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java (renamed from services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java)28
7 files changed, 238 insertions, 86 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index d3e7ea161fc6..e1f83822609d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -183,6 +183,9 @@ package android.app {
field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2
+ field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
+ field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
+ field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 861d41d4ce88..dfbaea10973d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -27,6 +27,8 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.usage.UsageStatsManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -101,6 +103,18 @@ import java.util.function.Supplier;
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
/**
+ * This is a subtle behavior change to {@link #startWatchingMode}.
+ *
+ * Before this change the system called back for the switched op. After the change the system
+ * will call back for the actually requested op or all switched ops if no op is specified.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L;
+
+ /**
* <p>App ops allows callers to:</p>
*
* <ul>
@@ -147,6 +161,38 @@ public class AppOpsManager {
static IBinder sClientId;
+ /**
+ * How many seconds we want for a drop in uid state from top to settle before applying it.
+ *
+ * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
+
+ /**
+ * How many second we want for a drop in uid state from foreground to settle before applying it.
+ *
+ * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME =
+ "fg_service_state_settle_time";
+
+ /**
+ * How many seconds we want for a drop in uid state from background to settle before applying
+ * it.
+ *
+ * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS}
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "HISTORICAL_MODE_" }, value = {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 12b1cbf877a3..3ad96ea193bf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2617,7 +2617,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessCpuThread.start();
mBatteryStatsService.publish();
- mAppOpsService.publish(mContext);
+ mAppOpsService.publish();
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
@@ -19510,7 +19510,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
public AppOpsService getAppOpsService(File file, Handler handler) {
- return new AppOpsService(file, handler);
+ return new AppOpsService(file, handler, getContext());
}
public Handler getUiHandler(ActivityManagerService service) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a0589c53c366..06561f57f495 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,11 +19,15 @@ package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.AppOpsManager.CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE;
import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
+import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
+import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
+import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.NoteOpEvent;
import static android.app.AppOpsManager.OP_CAMERA;
@@ -41,6 +45,7 @@ import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
import static android.app.AppOpsManager.extractUidStateFromKey;
@@ -53,6 +58,10 @@ import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.os.Process.STATSD_UID;
+import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
+
+import static java.lang.Long.max;
+
import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -70,6 +79,7 @@ import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.AsyncNotedAppOp;
+import android.compat.Compatibility;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -129,7 +139,6 @@ import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -216,7 +225,7 @@ public class AppOpsService extends IAppOpsService.Stub {
//TODO: remove this when development is done.
private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
- Context mContext;
+ final Context mContext;
final AtomicFile mFile;
final Handler mHandler;
@@ -291,8 +300,11 @@ public class AppOpsService extends IAppOpsService.Stub {
@GuardedBy("this")
private CheckOpsDelegate mCheckOpsDelegate;
- @GuardedBy("this")
- private SparseArray<List<Integer>> mSwitchOpToOps;
+ /**
+ * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never
+ * changed
+ */
+ private final SparseArray<int[]> mSwitchedOps = new SparseArray<>();
private ActivityManagerInternal mActivityManagerInternal;
@@ -343,30 +355,25 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
@VisibleForTesting
final class Constants extends ContentObserver {
- // Key names stored in the settings value.
- private static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
- private static final String KEY_FG_SERVICE_STATE_SETTLE_TIME
- = "fg_service_state_settle_time";
- private static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
/**
* How long we want for a drop in uid state from top to settle before applying it.
* @see Settings.Global#APP_OPS_CONSTANTS
- * @see #KEY_TOP_STATE_SETTLE_TIME
+ * @see AppOpsManager#KEY_TOP_STATE_SETTLE_TIME
*/
public long TOP_STATE_SETTLE_TIME;
/**
* How long we want for a drop in uid state from foreground to settle before applying it.
* @see Settings.Global#APP_OPS_CONSTANTS
- * @see #KEY_FG_SERVICE_STATE_SETTLE_TIME
+ * @see AppOpsManager#KEY_FG_SERVICE_STATE_SETTLE_TIME
*/
public long FG_SERVICE_STATE_SETTLE_TIME;
/**
* How long we want for a drop in uid state from background to settle before applying it.
* @see Settings.Global#APP_OPS_CONSTANTS
- * @see #KEY_BG_STATE_SETTLE_TIME
+ * @see AppOpsManager#KEY_BG_STATE_SETTLE_TIME
*/
public long BG_STATE_SETTLE_TIME;
@@ -1191,17 +1198,22 @@ public class AppOpsService extends IAppOpsService.Stub {
final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager();
final class ModeCallback implements DeathRecipient {
+ /** If mWatchedOpCode==ALL_OPS notify for ops affected by the switch-op */
+ public static final int ALL_OPS = -2;
+
final IAppOpsCallback mCallback;
final int mWatchingUid;
final int mFlags;
+ final int mWatchedOpCode;
final int mCallingUid;
final int mCallingPid;
- ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int callingUid,
- int callingPid) {
+ ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOp,
+ int callingUid, int callingPid) {
mCallback = callback;
mWatchingUid = watchingUid;
mFlags = flags;
+ mWatchedOpCode = watchedOp;
mCallingUid = callingUid;
mCallingPid = callingPid;
try {
@@ -1224,6 +1236,10 @@ public class AppOpsService extends IAppOpsService.Stub {
UserHandle.formatUid(sb, mWatchingUid);
sb.append(" flags=0x");
sb.append(Integer.toHexString(mFlags));
+ if (mWatchedOpCode != OP_NONE) {
+ sb.append(" op=");
+ sb.append(opToName(mWatchedOpCode));
+ }
sb.append(" from uid=");
UserHandle.formatUid(sb, mCallingUid);
sb.append(" pid=");
@@ -1337,16 +1353,23 @@ public class AppOpsService extends IAppOpsService.Stub {
featureOp.onClientDeath(clientId);
}
- public AppOpsService(File storagePath, Handler handler) {
+ public AppOpsService(File storagePath, Handler handler, Context context) {
+ mContext = context;
+
LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath, "appops");
mHandler = handler;
mConstants = new Constants(mHandler);
readState();
+
+ for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) {
+ int switchCode = AppOpsManager.opToSwitch(switchedCode);
+ mSwitchedOps.put(switchCode,
+ ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode));
+ }
}
- public void publish(Context context) {
- mContext = context;
+ public void publish() {
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
}
@@ -1616,6 +1639,19 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ /**
+ * Update the pending state for the uid
+ *
+ * @param currentTime The current elapsed real time
+ * @param uid The uid that has a pending state
+ */
+ private void updatePendingState(long currentTime, int uid) {
+ synchronized (this) {
+ mLastRealtime = max(currentTime, mLastRealtime);
+ updatePendingStateIfNeededLocked(mUidStates.get(uid));
+ }
+ }
+
public void updateUidProcState(int uid, int procState,
@ActivityManager.ProcessCapability int capability) {
synchronized (this) {
@@ -1647,7 +1683,12 @@ public class AppOpsService extends IAppOpsService.Stub {
} else {
settleTime = mConstants.BG_STATE_SETTLE_TIME;
}
- uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
+ final long commitTime = SystemClock.elapsedRealtime() + settleTime;
+ uidState.pendingStateCommitTime = commitTime;
+
+ mHandler.sendMessageDelayed(
+ PooledLambda.obtainMessage(AppOpsService::updatePendingState, this,
+ commitTime + 1, uid), settleTime + 1);
}
if (uidState.pkgOps != null) {
@@ -2014,6 +2055,19 @@ public class AppOpsService extends IAppOpsService.Stub {
uidState.evalForegroundOps(mOpModeWatchers);
}
+ notifyOpChangedForAllPkgsInUid(code, uid, false, callbackToIgnore);
+ notifyOpChangedSync(code, uid, null, mode);
+ }
+
+ /**
+ * Notify that an op changed for all packages in an uid.
+ *
+ * @param code The op that changed
+ * @param uid The uid the op was changed for
+ * @param onlyForeground Only notify watchers that watch for foreground changes
+ */
+ private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground,
+ @Nullable IAppOpsCallback callbackToIgnore) {
String[] uidPackageNames = getPackagesForUid(uid);
ArrayMap<ModeCallback, ArraySet<String>> callbackSpecs = null;
@@ -2023,6 +2077,10 @@ public class AppOpsService extends IAppOpsService.Stub {
final int callbackCount = callbacks.size();
for (int i = 0; i < callbackCount; i++) {
ModeCallback callback = callbacks.valueAt(i);
+ if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) {
+ continue;
+ }
+
ArraySet<String> changedPackages = new ArraySet<>();
Collections.addAll(changedPackages, uidPackageNames);
if (callbackSpecs == null) {
@@ -2041,6 +2099,10 @@ public class AppOpsService extends IAppOpsService.Stub {
final int callbackCount = callbacks.size();
for (int i = 0; i < callbackCount; i++) {
ModeCallback callback = callbacks.valueAt(i);
+ if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) {
+ continue;
+ }
+
ArraySet<String> changedPackages = callbackSpecs.get(callback);
if (changedPackages == null) {
changedPackages = new ArraySet<>();
@@ -2057,7 +2119,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (callbackSpecs == null) {
- notifyOpChangedSync(code, uid, null, mode);
return;
}
@@ -2079,23 +2140,24 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
-
- notifyOpChangedSync(code, uid, null, mode);
}
private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
PackageManager packageManager = mContext.getPackageManager();
+ if (packageManager == null) {
+ // This can only happen during early boot. At this time the permission state and appop
+ // state are in sync
+ return;
+ }
+
String[] packageNames = packageManager.getPackagesForUid(uid);
if (ArrayUtils.isEmpty(packageNames)) {
return;
}
String packageName = packageNames[0];
- List<Integer> ops = getSwitchOpToOps().get(switchCode);
- int opsSize = CollectionUtils.size(ops);
- for (int i = 0; i < opsSize; i++) {
- int code = ops.get(i);
-
+ int[] ops = mSwitchedOps.get(switchCode);
+ for (int code : ops) {
String permissionName = AppOpsManager.opToPermission(code);
if (permissionName == null) {
continue;
@@ -2173,25 +2235,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- @NonNull
- private SparseArray<List<Integer>> getSwitchOpToOps() {
- synchronized (this) {
- if (mSwitchOpToOps == null) {
- mSwitchOpToOps = new SparseArray<>();
- for (int op = 0; op < _NUM_OP; op++) {
- int switchOp = AppOpsManager.opToSwitch(op);
- List<Integer> ops = mSwitchOpToOps.get(switchOp);
- if (ops == null) {
- ops = new ArrayList<>();
- mSwitchOpToOps.put(switchOp, ops);
- }
- ops.add(op);
- }
- }
- return mSwitchOpToOps;
- }
- }
-
private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) {
final StorageManagerInternal storageManagerInternal =
LocalServices.getService(StorageManagerInternal.class);
@@ -2292,16 +2335,29 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uid != UID_ANY && callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
return;
}
- // There are features watching for mode changes such as window manager
- // and location manager which are in our process. The callbacks in these
- // features may require permissions our remote caller does not have.
- final long identity = Binder.clearCallingIdentity();
- try {
- callback.mCallback.opChanged(code, uid, packageName);
- } catch (RemoteException e) {
- /* ignore */
- } finally {
- Binder.restoreCallingIdentity(identity);
+
+ // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
+ int[] switchedCodes;
+ if (callback.mWatchedOpCode == ALL_OPS) {
+ switchedCodes = mSwitchedOps.get(code);
+ } else if (callback.mWatchedOpCode == OP_NONE) {
+ switchedCodes = new int[]{code};
+ } else {
+ switchedCodes = new int[]{callback.mWatchedOpCode};
+ }
+
+ for (int switchedCode : switchedCodes) {
+ // There are features watching for mode changes such as window manager
+ // and location manager which are in our process. The callbacks in these
+ // features may require permissions our remote caller does not have.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ callback.mCallback.opChanged(switchedCode, uid, packageName);
+ } catch (RemoteException e) {
+ /* ignore */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -2496,17 +2552,32 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
synchronized (this) {
- op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
+ int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
+
+ // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
+ int notifiedOps;
+ if (Compatibility.isChangeEnabled(
+ CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
+ if (op == OP_NONE) {
+ notifiedOps = ALL_OPS;
+ } else {
+ notifiedOps = op;
+ }
+ } else {
+ notifiedOps = switchOp;
+ }
+
ModeCallback cb = mModeWatchers.get(callback.asBinder());
if (cb == null) {
- cb = new ModeCallback(callback, watchedUid, flags, callingUid, callingPid);
+ cb = new ModeCallback(callback, watchedUid, flags, notifiedOps, callingUid,
+ callingPid);
mModeWatchers.put(callback.asBinder(), cb);
}
- if (op != AppOpsManager.OP_NONE) {
- ArraySet<ModeCallback> cbs = mOpModeWatchers.get(op);
+ if (switchOp != AppOpsManager.OP_NONE) {
+ ArraySet<ModeCallback> cbs = mOpModeWatchers.get(switchOp);
if (cbs == null) {
cbs = new ArraySet<>();
- mOpModeWatchers.put(op, cbs);
+ mOpModeWatchers.put(switchOp, cbs);
}
cbs.add(cb);
}
@@ -3325,6 +3396,18 @@ public class AppOpsService extends IAppOpsService.Stub {
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
} else {
+ updatePendingStateIfNeededLocked(uidState);
+ }
+ return uidState;
+ }
+
+ /**
+ * Check if the pending state should be updated and do so if needed
+ *
+ * @param uidState The uidState that might have a pending state
+ */
+ private void updatePendingStateIfNeededLocked(@NonNull UidState uidState) {
+ if (uidState != null) {
if (uidState.pendingStateCommitTime != 0) {
if (uidState.pendingStateCommitTime < mLastRealtime) {
commitUidPendingStateLocked(uidState);
@@ -3336,7 +3419,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
- return uidState;
}
private void commitUidPendingStateLocked(UidState uidState) {
@@ -3356,24 +3438,28 @@ public class AppOpsService extends IAppOpsService.Stub {
&& uidState.appWidgetVisible == uidState.pendingAppWidgetVisible) {
continue;
}
- final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
- if (callbacks != null) {
- for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) {
- final ModeCallback callback = callbacks.valueAt(cbi);
- if ((callback.mFlags & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
- || !callback.isWatchingUid(uidState.uid)) {
- continue;
- }
- boolean doAllPackages = uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0
- && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND;
- if (uidState.pkgOps != null) {
+
+ if (uidState.opModes != null
+ && uidState.opModes.indexOfKey(code) >= 0
+ && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChangedForAllPkgsInUid,
+ this, code, uidState.uid, true, null));
+ } else {
+ final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
+ if (callbacks != null) {
+ for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) {
+ final ModeCallback callback = callbacks.valueAt(cbi);
+ if ((callback.mFlags & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
+ || !callback.isWatchingUid(uidState.uid)) {
+ continue;
+ }
for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
if (op == null) {
continue;
}
- if (doAllPackages || op.mode == AppOpsManager.MODE_FOREGROUND) {
+ if (op.mode == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
this, callback, code, uidState.uid,
@@ -3798,11 +3884,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (tagName.equals("op")) {
final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
- UidState uidState = getUidStateLocked(uid, true);
- if (uidState.opModes == null) {
- uidState.opModes = new SparseIntArray();
- }
- uidState.opModes.put(code, mode);
+ setUidMode(code, uid, mode);
} else {
Slog.w(TAG, "Unknown element under <uid-ops>: "
+ parser.getName());
diff --git a/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml
index a37d84f7a08c..a37d84f7a08c 100644
--- a/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml
+++ b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 155de3bc0266..2d5fa237f6b7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -101,9 +101,8 @@ public class AppOpsServiceTest {
private StaticMockitoSession mMockingSession;
private void setupAppOpsService() {
- mAppOpsService = new AppOpsService(mAppOpsFile, mHandler);
+ mAppOpsService = new AppOpsService(mAppOpsFile, mHandler, spy(sContext));
mAppOpsService.mHistoricalRegistry.systemReady(sContext.getContentResolver());
- mAppOpsService.mContext = spy(sContext);
// Always approve all permission checks
doNothing().when(mAppOpsService.mContext).enforcePermission(anyString(), anyInt(),
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 66d2baba2909..e48b67167cdf 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -19,9 +19,17 @@ package com.android.server.appop;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -133,10 +141,24 @@ public class AppOpsUpgradeTest {
AppOpsDataParser parser = new AppOpsDataParser(mAppOpsFile);
assertTrue(parser.parse());
assertEquals(AppOpsDataParser.NO_VERSION, parser.mVersion);
- AppOpsService testService = new AppOpsService(mAppOpsFile, mHandler); // trigger upgrade
+
+ // Use mock context and package manager to fake permision package manager calls.
+ Context testContext = spy(mContext);
+
+ // Pretent everybody has all permissions
+ doNothing().when(testContext).enforcePermission(anyString(), anyInt(), anyInt(),
+ nullable(String.class));
+
+ PackageManager testPM = mock(PackageManager.class);
+ when(testContext.getPackageManager()).thenReturn(testPM);
+
+ // Stub out package calls to disable AppOpsService#updatePermissionRevokedCompat
+ when(testPM.getPackagesForUid(anyInt())).thenReturn(null);
+
+ AppOpsService testService = spy(
+ new AppOpsService(mAppOpsFile, mHandler, testContext)); // trigger upgrade
assertSameModes(testService.mUidStates, AppOpsManager.OP_RUN_IN_BACKGROUND,
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
- testService.mContext = mContext;
mHandler.removeCallbacks(testService.mWriteRunner);
testService.writeState();
assertTrue(parser.parse());