summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java210
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java279
-rw-r--r--services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java4
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java4636
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceImpl.java4742
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceInterface.java525
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java6
-rw-r--r--services/core/java/com/android/server/appop/AttributedOp.java78
-rw-r--r--services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java (renamed from services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java)27
-rw-r--r--services/permission/java/com/android/server/permission/access/AccessCheckingService.kt6
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/AppOpService.kt4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java71
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java36
15 files changed, 4612 insertions, 6018 deletions
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
deleted file mode 100644
index ef3e3685401f..000000000000
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.AppOpsManager.Mode;
-import android.util.ArraySet;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-
-import java.io.PrintWriter;
-
-/**
- * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
- * This interface also includes functions for added and removing op mode watchers.
- * In the future this interface will also include op restrictions.
- */
-public interface AppOpsCheckingServiceInterface {
- /**
- * 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.
- * @param userId user id associated with the package.
- * @return the mode of the app-op.
- */
- int getPackageMode(@NonNull String packageName, int op, @UserIdInt int userId);
-
- /**
- * 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.
- * @param userId user id associated with the package.
- *
- */
- void setPackageMode(@NonNull String packageName, int op, @Mode int mode, @UserIdInt int userId);
-
- /**
- * Stop tracking any app-op modes for a package.
- * @param packageName Name of the package for which we want to remove all mode tracking.
- * @param userId user id associated with the package.
- */
- boolean removePackage(@NonNull String packageName, @UserIdInt int userId);
-
- /**
- * 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.
- * @param userId user id associated with the package.
- */
- boolean arePackageModesDefault(@NonNull String packageName, @UserIdInt int userId);
-
- /**
- * Stop tracking app-op modes for all uid and packages.
- */
- void clearAllModes();
-
- /**
- * Registers changedListener to listen to op's mode change.
- * @param changedListener the listener that must be trigger on the op's mode change.
- * @param op op representing the app-op whose mode change needs to be listened to.
- */
- void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op);
-
- /**
- * Registers changedListener to listen to package's app-op's mode change.
- * @param changedListener the listener that must be trigger on the mode change.
- * @param packageName of the package whose app-op's mode change needs to be listened to.
- */
- void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
- @NonNull String packageName);
-
- /**
- * Stop the changedListener from triggering on any mode change.
- * @param changedListener the listener that needs to be removed.
- */
- void removeListener(@NonNull OnOpModeChangedListener changedListener);
-
- /**
- * Temporary API which will be removed once we can safely untangle the methods that use this.
- * Returns a set of OnOpModeChangedListener that are listening for op's mode changes.
- * @param op app-op whose mode change is being listened to.
- */
- ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op);
-
- /**
- * Temporary API which will be removed once we can safely untangle the methods that use this.
- * Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes.
- * @param packageName of package whose app-op's mode change is being listened to.
- */
- ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName);
-
- /**
- * Temporary API which will be removed once we can safely untangle the methods that use this.
- * Notify that the app-op's mode is changed by triggering the change listener.
- * @param op App-op whose mode has changed
- * @param uid user id associated with the app-op (or, if UID_ANY, notifies all users)
- */
- void notifyWatchersOfChange(int op, int uid);
-
- /**
- * Temporary API which will be removed once we can safely untangle the methods that use this.
- * Notify that the app-op's mode is changed by triggering the change listener.
- * @param changedListener the change listener.
- * @param op App-op whose mode has changed
- * @param uid user id associated with the app-op
- * @param packageName package name that is associated with the app-op
- */
- void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
- @Nullable String packageName);
-
- /**
- * Temporary API which will be removed once we can safely untangle the methods that use this.
- * Notify that the app-op's mode is changed to all packages associated with the uid by
- * triggering the appropriate change listener.
- * @param op App-op whose mode has changed
- * @param uid user id associated with the app-op
- * @param onlyForeground true if only watchers that
- * @param callbackToIgnore callback that should be ignored.
- */
- void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
- @Nullable OnOpModeChangedListener callbackToIgnore);
-
- /**
- * TODO: Move hasForegroundWatchers and foregroundOps into this.
- * Go over the list of app-ops for the uid and mark app-ops with MODE_FOREGROUND in
- * foregroundOps.
- * @param uid for which the app-op's mode needs to be marked.
- * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
- * @return foregroundOps.
- */
- SparseBooleanArray evalForegroundUidOps(int uid, @Nullable SparseBooleanArray foregroundOps);
-
- /**
- * Go over the list of app-ops for the package name and mark app-ops with MODE_FOREGROUND in
- * foregroundOps.
- * @param packageName for which the app-op's mode needs to be marked.
- * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
- * @param userId user id associated with the package.
- * @return foregroundOps.
- */
- SparseBooleanArray evalForegroundPackageOps(@NonNull String packageName,
- @Nullable SparseBooleanArray foregroundOps, @UserIdInt int userId);
-
- /**
- * Dump op mode and package mode listeners and their details.
- * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's set to an
- * app-op, only the watchers for that app-op are dumped.
- * @param dumpUid uid for which we want to dump op mode watchers.
- * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package name.
- * @param printWriter writer to dump to.
- */
- boolean dumpListeners(int dumpOp, int dumpUid, @Nullable String dumpPackage,
- @NonNull PrintWriter printWriter);
-}
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
deleted file mode 100644
index 44360028704e..000000000000
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * 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.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.AppOpsManager;
-import android.os.Trace;
-import android.util.ArraySet;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-
-import java.io.PrintWriter;
-
-/**
- * Surrounds all AppOpsCheckingServiceInterface method calls with Trace.traceBegin and
- * Trace.traceEnd. These traces are used for performance testing.
- */
-public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServiceInterface {
- private static final long TRACE_TAG = Trace.TRACE_TAG_SYSTEM_SERVER;
- private final AppOpsCheckingServiceInterface mService;
-
- AppOpsCheckingServiceTracingDecorator(
- @NonNull AppOpsCheckingServiceInterface appOpsCheckingServiceInterface) {
- mService = appOpsCheckingServiceInterface;
- }
-
- @Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getNonDefaultUidModes");
- try {
- return mService.getNonDefaultUidModes(uid);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public int getUidMode(int uid, int op) {
- Trace.traceBegin(TRACE_TAG, "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getUidMode");
- try {
- return mService.getUidMode(uid, op);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public boolean setUidMode(int uid, int op, @AppOpsManager.Mode int mode) {
- Trace.traceBegin(TRACE_TAG, "TaggedTracingAppOpsCheckingServiceInterfaceImpl#setUidMode");
- try {
- return mService.setUidMode(uid, op, mode);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public int getPackageMode(@NonNull String packageName, int op, @UserIdInt int userId) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getPackageMode");
- try {
- return mService.getPackageMode(packageName, op, userId);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void setPackageMode(@NonNull String packageName, int op, @AppOpsManager.Mode int mode,
- @UserIdInt int userId) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#setPackageMode");
- try {
- mService.setPackageMode(packageName, op, mode, userId);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public boolean removePackage(@NonNull String packageName, @UserIdInt int userId) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#removePackage");
- try {
- return mService.removePackage(packageName, userId);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void removeUid(int uid) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#removeUid");
- try {
- mService.removeUid(uid);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public boolean areUidModesDefault(int uid) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#areUidModesDefault");
- try {
- return mService.areUidModesDefault(uid);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public boolean arePackageModesDefault(String packageName, @UserIdInt int userId) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#arePackageModesDefault");
- try {
- return mService.arePackageModesDefault(packageName, userId);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void clearAllModes() {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#clearAllModes");
- try {
- mService.clearAllModes();
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener,
- int op) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#startWatchingOpModeChanged");
- try {
- mService.startWatchingOpModeChanged(changedListener, op);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
- @NonNull String packageName) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#startWatchingPackageModeChanged");
- try {
- mService.startWatchingPackageModeChanged(changedListener, packageName);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void removeListener(@NonNull OnOpModeChangedListener changedListener) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#removeListener");
- try {
- mService.removeListener(changedListener);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getOpModeChangedListeners");
- try {
- return mService.getOpModeChangedListeners(op);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(
- @NonNull String packageName) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getPackageModeChangedListeners");
- try {
- return mService.getPackageModeChangedListeners(packageName);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void notifyWatchersOfChange(int op, int uid) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#notifyWatchersOfChange");
- try {
- mService.notifyWatchersOfChange(op, uid);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
- @Nullable String packageName) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#notifyOpChanged");
- try {
- mService.notifyOpChanged(changedListener, op, uid, packageName);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
- @Nullable OnOpModeChangedListener callbackToIgnore) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#notifyOpChangedForAllPkgsInUid");
- try {
- mService.notifyOpChangedForAllPkgsInUid(op, uid, onlyForeground, callbackToIgnore);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#evalForegroundUidOps");
- try {
- return mService.evalForegroundUidOps(uid, foregroundOps);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public SparseBooleanArray evalForegroundPackageOps(String packageName,
- SparseBooleanArray foregroundOps, @UserIdInt int userId) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#evalForegroundPackageOps");
- try {
- return mService.evalForegroundPackageOps(packageName, foregroundOps, userId);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-
- @Override
- public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage,
- PrintWriter printWriter) {
- Trace.traceBegin(TRACE_TAG,
- "TaggedTracingAppOpsCheckingServiceInterfaceImpl#dumpListeners");
- try {
- return mService.dumpListeners(dumpOp, dumpUid, dumpPackage, printWriter);
- } finally {
- Trace.traceEnd(TRACE_TAG);
- }
- }
-}
diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
index af5b07e0bffc..adfd2afffd78 100644
--- a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
@@ -42,7 +42,7 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
private Context mContext;
private Handler mHandler;
- private AppOpsCheckingServiceInterface mAppOpsServiceInterface;
+ private AppOpsServiceInterface mAppOpsServiceInterface;
// Map from (Object token) to (int code) to (boolean restricted)
private final ArrayMap<Object, SparseBooleanArray> mGlobalRestrictions = new ArrayMap<>();
@@ -56,7 +56,7 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
mUserRestrictionExcludedPackageTags = new ArrayMap<>();
public AppOpsRestrictionsImpl(Context context, Handler handler,
- AppOpsCheckingServiceInterface appOpsServiceInterface) {
+ AppOpsServiceInterface appOpsServiceInterface) {
mContext = context;
mHandler = handler;
mAppOpsServiceInterface = appOpsServiceInterface;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 39338c6f43ad..cc0a77a6397f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -18,22 +18,56 @@ package com.android.server.appop;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
+import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
+import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
+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.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS;
+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.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
import static android.app.AppOpsManager.OP_NONE;
+import static android.app.AppOpsManager.OP_PLAY_AUDIO;
+import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
+import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
+import static android.app.AppOpsManager.OP_VIBRATE;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED;
+import static android.app.AppOpsManager.OpEventProxyInfo;
+import static android.app.AppOpsManager.RestrictionBypass;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
+import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
import static android.app.AppOpsManager._NUM_OP;
+import static android.app.AppOpsManager.extractFlagsFromKey;
+import static android.app.AppOpsManager.extractUidStateFromKey;
+import static android.app.AppOpsManager.modeToName;
+import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
import static android.app.AppOpsManager.opRestrictsRead;
+import static android.app.AppOpsManager.opToName;
import static android.app.AppOpsManager.opToPublicName;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
+import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,16 +76,21 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.AttributionFlags;
import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.Mode;
+import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.AsyncNotedAppOp;
import android.app.RuntimeAppOpAccessMessage;
import android.app.SyncNotedAppOp;
+import android.app.admin.DevicePolicyManagerInternal;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -59,11 +98,15 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
+import android.database.ContentObserver;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.PackageTagsList;
import android.os.Process;
@@ -74,14 +117,22 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.KeyValueListParser;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
@@ -93,37 +144,61 @@ import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.os.Clock;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
+import com.android.server.LockGuard;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemServiceManager;
import com.android.server.pm.PackageList;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.component.ParsedAttribution;
import com.android.server.policy.AppOpsPolicy;
+import dalvik.annotation.optimization.NeverCompile;
+
+import libcore.util.EmptyArray;
+
import org.json.JSONException;
import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
+import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
-/**
- * The system service component to {@link AppOpsManager}.
- */
-public class AppOpsService extends IAppOpsService.Stub {
-
- private final AppOpsServiceInterface mAppOpsService;
-
+public class AppOpsService extends IAppOpsService.Stub implements PersistenceScheduler {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
@@ -132,19 +207,74 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
private final ArraySet<NoteOpTrace> mNoteOpCallerStacktraces = new ArraySet<>();
+ /**
+ * Sentinel integer version to denote that there was no appops.xml found on boot.
+ * This will happen when a device boots with no existing userdata.
+ */
+ private static final int NO_FILE_VERSION = -2;
+
+ /**
+ * Sentinel integer version to denote that there was no version in the appops.xml found on boot.
+ * This means the file is coming from a build before versioning was added.
+ */
+ private static final int NO_VERSION = -1;
+
+ /** Increment by one every time and add the corresponding upgrade logic in
+ * {@link #upgradeLocked(int)} below. The first version was 1 */
+ static final int CURRENT_VERSION = 2;
+
+ /**
+ * This stores the version of appops.xml seen at boot. If this is smaller than
+ * {@link #CURRENT_VERSION}, then we will run {@link #upgradeLocked(int)} on startup.
+ */
+ private int mVersionAtBoot = NO_FILE_VERSION;
+
+ // Write at most every 30 minutes.
+ static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
+
// Constant meaning that any UID should be matched when dispatching callbacks
private static final int UID_ANY = -2;
- private static final int MAX_UNFORWARDED_OPS = 10;
+ private static final int[] OPS_RESTRICTED_ON_SUSPEND = {
+ OP_PLAY_AUDIO,
+ OP_RECORD_AUDIO,
+ OP_CAMERA,
+ OP_VIBRATE,
+ };
+ private static final int MAX_UNFORWARDED_OPS = 10;
+ private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
final Context mContext;
+ final AtomicFile mFile;
private final @Nullable File mNoteOpCallerStacktracesFile;
final Handler mHandler;
+ /**
+ * Pool for {@link AttributedOp.OpEventProxyInfoPool} to avoid to constantly reallocate new
+ * objects
+ */
+ @GuardedBy("this")
+ final AttributedOp.OpEventProxyInfoPool mOpEventProxyInfoPool =
+ new AttributedOp.OpEventProxyInfoPool(MAX_UNUSED_POOLED_OBJECTS);
+
+ /**
+ * Pool for {@link AttributedOp.InProgressStartOpEventPool} to avoid to constantly reallocate
+ * new objects
+ */
+ @GuardedBy("this")
+ final AttributedOp.InProgressStartOpEventPool mInProgressStartOpEventPool =
+ new AttributedOp.InProgressStartOpEventPool(mOpEventProxyInfoPool,
+ MAX_UNUSED_POOLED_OBJECTS);
+
private final AppOpsManagerInternalImpl mAppOpsManagerInternal
= new AppOpsManagerInternalImpl();
+ @Nullable private final DevicePolicyManagerInternal dpmi =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+
+ private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
/**
* Registered callbacks, called from {@link #collectAsyncNotedOp}.
@@ -171,9 +301,54 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean mWriteNoteOpsScheduled;
+ boolean mWriteScheduled;
+ boolean mFastWriteScheduled;
+ final Runnable mWriteRunner = new Runnable() {
+ public void run() {
+ synchronized (AppOpsService.this) {
+ mWriteScheduled = false;
+ mFastWriteScheduled = false;
+ AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ writeState();
+ return null;
+ }
+ };
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
+ }
+ }
+ };
+
+ @GuardedBy("this")
+ @VisibleForTesting
+ final SparseArray<UidState> mUidStates = new SparseArray<>();
+
+ volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
+
+ /*
+ * These are app op restrictions imposed per user from various parties.
+ */
+ private final ArrayMap<IBinder, ClientUserRestrictionState> mOpUserRestrictions =
+ new ArrayMap<>();
+
+ /*
+ * These are app op restrictions imposed globally from various parties within the system.
+ */
+ private final ArrayMap<IBinder, ClientGlobalRestrictionState> mOpGlobalRestrictions =
+ new ArrayMap<>();
+
+ SparseIntArray mProfileOwners;
+
private volatile CheckOpsDelegateDispatcher mCheckOpsDelegateDispatcher =
new CheckOpsDelegateDispatcher(/*policy*/ null, /*delegate*/ null);
+ /**
+ * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never
+ * changed
+ */
+ private final SparseArray<int[]> mSwitchedOps = new SparseArray<>();
+
+ private ActivityManagerInternal mActivityManagerInternal;
/** Package sampled for message collection in the current session */
@GuardedBy("this")
@@ -207,8 +382,545 @@ 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;
+
+ /** Interface for app-op restrictions.*/
+ @VisibleForTesting AppOpsRestrictions mAppOpsRestrictions;
+
+ private AppOpsUidStateTracker mUidStateTracker;
+
+ /** Hands the definition of foreground and uid states */
+ @GuardedBy("this")
+ public AppOpsUidStateTracker getUidStateTracker() {
+ if (mUidStateTracker == null) {
+ mUidStateTracker = new AppOpsUidStateTrackerImpl(
+ LocalServices.getService(ActivityManagerInternal.class),
+ mHandler,
+ r -> {
+ synchronized (AppOpsService.this) {
+ r.run();
+ }
+ },
+ Clock.SYSTEM_CLOCK, mConstants);
+
+ mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
+ this::onUidStateChanged);
+ }
+ return mUidStateTracker;
+ }
+
+ /**
+ * All times are in milliseconds. These constants are kept synchronized with the system
+ * global Settings. Any access to this class or its fields should be done while
+ * holding the AppOpsService lock.
+ */
+ final class Constants extends ContentObserver {
+
+ /**
+ * How long we want for a drop in uid state from top to settle before applying it.
+ * @see Settings.Global#APP_OPS_CONSTANTS
+ * @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 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 AppOpsManager#KEY_BG_STATE_SETTLE_TIME
+ */
+ public long BG_STATE_SETTLE_TIME;
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private ContentResolver mResolver;
+
+ public Constants(Handler handler) {
+ super(handler);
+ updateConstants();
+ }
+
+ public void startMonitoring(ContentResolver resolver) {
+ mResolver = resolver;
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.APP_OPS_CONSTANTS),
+ false, this);
+ updateConstants();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateConstants();
+ }
+
+ private void updateConstants() {
+ String value = mResolver != null ? Settings.Global.getString(mResolver,
+ Settings.Global.APP_OPS_CONSTANTS) : "";
+
+ synchronized (AppOpsService.this) {
+ try {
+ mParser.setString(value);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on
+ // with defaults.
+ Slog.e(TAG, "Bad app ops settings", e);
+ }
+ TOP_STATE_SETTLE_TIME = mParser.getDurationMillis(
+ KEY_TOP_STATE_SETTLE_TIME, 5 * 1000L);
+ FG_SERVICE_STATE_SETTLE_TIME = mParser.getDurationMillis(
+ KEY_FG_SERVICE_STATE_SETTLE_TIME, 5 * 1000L);
+ BG_STATE_SETTLE_TIME = mParser.getDurationMillis(
+ KEY_BG_STATE_SETTLE_TIME, 1 * 1000L);
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ pw.println(" Settings:");
+
+ pw.print(" "); pw.print(KEY_TOP_STATE_SETTLE_TIME); pw.print("=");
+ TimeUtils.formatDuration(TOP_STATE_SETTLE_TIME, pw);
+ pw.println();
+ pw.print(" "); pw.print(KEY_FG_SERVICE_STATE_SETTLE_TIME); pw.print("=");
+ TimeUtils.formatDuration(FG_SERVICE_STATE_SETTLE_TIME, pw);
+ pw.println();
+ pw.print(" "); pw.print(KEY_BG_STATE_SETTLE_TIME); pw.print("=");
+ TimeUtils.formatDuration(BG_STATE_SETTLE_TIME, pw);
+ pw.println();
+ }
+ }
+
+ @VisibleForTesting
+ final Constants mConstants;
+
+ @VisibleForTesting
+ final class UidState {
+ public final int uid;
+
+ public ArrayMap<String, Ops> pkgOps;
+
+ // true indicates there is an interested observer, false there isn't but it has such an op
+ //TODO: Move foregroundOps and hasForegroundWatchers into the AppOpsServiceInterface.
+ public SparseBooleanArray foregroundOps;
+ public boolean hasForegroundWatchers;
+
+ public UidState(int uid) {
+ this.uid = uid;
+ }
+
+ public void clear() {
+ mAppOpsServiceInterface.removeUid(uid);
+ if (pkgOps != null) {
+ for (String packageName : pkgOps.keySet()) {
+ mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
+ }
+ }
+ pkgOps = null;
+ }
+
+ public boolean isDefault() {
+ boolean areAllPackageModesDefault = true;
+ if (pkgOps != null) {
+ for (String packageName : pkgOps.keySet()) {
+ if (!mAppOpsServiceInterface.arePackageModesDefault(packageName,
+ UserHandle.getUserId(uid))) {
+ areAllPackageModesDefault = false;
+ break;
+ }
+ }
+ }
+ return (pkgOps == null || pkgOps.isEmpty())
+ && 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);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ int evalMode(int op, int mode) {
+ return getUidStateTracker().evalMode(uid, op, mode);
+ }
+
+ public void evalForegroundOps() {
+ foregroundOps = null;
+ foregroundOps = mAppOpsServiceInterface.evalForegroundUidOps(uid, foregroundOps);
+ if (pkgOps != null) {
+ for (int i = pkgOps.size() - 1; i >= 0; i--) {
+ foregroundOps = mAppOpsServiceInterface
+ .evalForegroundPackageOps(pkgOps.valueAt(i).packageName, foregroundOps,
+ UserHandle.getUserId(uid));
+ }
+ }
+ hasForegroundWatchers = false;
+ if (foregroundOps != null) {
+ for (int i = 0; i < foregroundOps.size(); i++) {
+ if (foregroundOps.valueAt(i)) {
+ hasForegroundWatchers = true;
+ break;
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("GuardedBy")
+ public int getState() {
+ return getUidStateTracker().getUidState(uid);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ public void dump(PrintWriter pw, long nowElapsed) {
+ getUidStateTracker().dumpUidState(pw, uid, nowElapsed);
+ }
+ }
+
+ final static class Ops extends SparseArray<Op> {
+ final String packageName;
+ final UidState uidState;
+
+ /**
+ * The restriction properties of the package. If {@code null} it could not have been read
+ * yet and has to be refreshed.
+ */
+ @Nullable RestrictionBypass bypass;
+
+ /** Lazily populated cache of attributionTags of this package */
+ final @NonNull ArraySet<String> knownAttributionTags = new ArraySet<>();
+
+ /**
+ * Lazily populated cache of <b>valid</b> attributionTags of this package, a set smaller
+ * than or equal to {@link #knownAttributionTags}.
+ */
+ final @NonNull ArraySet<String> validAttributionTags = new ArraySet<>();
+
+ Ops(String _packageName, UidState _uidState) {
+ packageName = _packageName;
+ uidState = _uidState;
+ }
+ }
+
+ /** Returned from {@link #verifyAndGetBypass(int, String, String, String)}. */
+ private static final class PackageVerificationResult {
+
+ final RestrictionBypass bypass;
+ final boolean isAttributionTagValid;
+
+ PackageVerificationResult(RestrictionBypass bypass, boolean isAttributionTagValid) {
+ this.bypass = bypass;
+ this.isAttributionTagValid = isAttributionTagValid;
+ }
+ }
+
+ final class Op {
+ int op;
+ int uid;
+ final UidState uidState;
+ final @NonNull String packageName;
+
+ /** attributionTag -> AttributedOp */
+ final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
+
+ Op(UidState uidState, String packageName, int op, int uid) {
+ this.op = op;
+ this.uid = uid;
+ this.uidState = uidState;
+ this.packageName = packageName;
+ }
+
+ @Mode int getMode() {
+ return mAppOpsServiceInterface.getPackageMode(packageName, this.op,
+ UserHandle.getUserId(this.uid));
+ }
+ void setMode(@Mode int mode) {
+ mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode,
+ UserHandle.getUserId(this.uid));
+ }
+
+ void removeAttributionsWithNoTime() {
+ for (int i = mAttributions.size() - 1; i >= 0; i--) {
+ if (!mAttributions.valueAt(i).hasAnyTime()) {
+ mAttributions.removeAt(i);
+ }
+ }
+ }
+
+ private @NonNull AttributedOp getOrCreateAttribution(@NonNull Op parent,
+ @Nullable String attributionTag) {
+ AttributedOp attributedOp;
+
+ attributedOp = mAttributions.get(attributionTag);
+ if (attributedOp == null) {
+ attributedOp = new AttributedOp(AppOpsService.this, attributionTag, parent);
+ mAttributions.put(attributionTag, attributedOp);
+ }
+
+ return attributedOp;
+ }
+
+ @NonNull OpEntry createEntryLocked() {
+ final int numAttributions = mAttributions.size();
+
+ final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries =
+ new ArrayMap<>(numAttributions);
+ for (int i = 0; i < numAttributions; i++) {
+ attributionEntries.put(mAttributions.keyAt(i),
+ mAttributions.valueAt(i).createAttributedOpEntryLocked());
+ }
+
+ return new OpEntry(op, getMode(), attributionEntries);
+ }
+
+ @NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
+ final int numAttributions = mAttributions.size();
+
+ final ArrayMap<String, AttributedOpEntry> attributionEntries = new ArrayMap<>(1);
+ for (int i = 0; i < numAttributions; i++) {
+ if (Objects.equals(mAttributions.keyAt(i), attributionTag)) {
+ attributionEntries.put(mAttributions.keyAt(i),
+ mAttributions.valueAt(i).createAttributedOpEntryLocked());
+ break;
+ }
+ }
+
+ return new OpEntry(op, getMode(), attributionEntries);
+ }
+
+ boolean isRunning() {
+ final int numAttributions = mAttributions.size();
+ for (int i = 0; i < numAttributions; i++) {
+ if (mAttributions.valueAt(i).isRunning()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
+ final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
+ final ArrayMap<IBinder, SparseArray<StartedCallback>> mStartedWatchers = new ArrayMap<>();
+ final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager();
+ final class ModeCallback extends OnOpModeChangedListener implements DeathRecipient {
+ /** If mWatchedOpCode==ALL_OPS notify for ops affected by the switch-op */
+ public static final int ALL_OPS = -2;
+
+ // Need to keep this only because stopWatchingMode needs an IAppOpsCallback.
+ // Otherwise we can just use the IBinder object.
+ private final IAppOpsCallback mCallback;
+
+ ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOpCode,
+ int callingUid, int callingPid) {
+ super(watchingUid, flags, watchedOpCode, callingUid, callingPid);
+ this.mCallback = callback;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ /*ignored*/
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ModeCallback{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" watchinguid=");
+ UserHandle.formatUid(sb, getWatchingUid());
+ sb.append(" flags=0x");
+ sb.append(Integer.toHexString(getFlags()));
+ switch (getWatchedOpCode()) {
+ case OP_NONE:
+ break;
+ case ALL_OPS:
+ sb.append(" op=(all)");
+ break;
+ default:
+ sb.append(" op=");
+ sb.append(opToName(getWatchedOpCode()));
+ break;
+ }
+ sb.append(" from uid=");
+ UserHandle.formatUid(sb, getCallingUid());
+ sb.append(" pid=");
+ sb.append(getCallingPid());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void unlinkToDeath() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ stopWatchingMode(mCallback);
+ }
+
+ @Override
+ public void onOpModeChanged(int op, int uid, String packageName) throws RemoteException {
+ mCallback.opChanged(op, uid, packageName);
+ }
+ }
+
+ final class ActiveCallback implements DeathRecipient {
+ final IAppOpsActiveCallback mCallback;
+ final int mWatchingUid;
+ final int mCallingUid;
+ final int mCallingPid;
+
+ ActiveCallback(IAppOpsActiveCallback callback, int watchingUid, int callingUid,
+ int callingPid) {
+ mCallback = callback;
+ mWatchingUid = watchingUid;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ /*ignored*/
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ActiveCallback{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" watchinguid=");
+ UserHandle.formatUid(sb, mWatchingUid);
+ sb.append(" from uid=");
+ UserHandle.formatUid(sb, mCallingUid);
+ sb.append(" pid=");
+ sb.append(mCallingPid);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void destroy() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ stopWatchingActive(mCallback);
+ }
+ }
+
+ final class StartedCallback implements DeathRecipient {
+ final IAppOpsStartedCallback mCallback;
+ final int mWatchingUid;
+ final int mCallingUid;
+ final int mCallingPid;
+
+ StartedCallback(IAppOpsStartedCallback callback, int watchingUid, int callingUid,
+ int callingPid) {
+ mCallback = callback;
+ mWatchingUid = watchingUid;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ /*ignored*/
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("StartedCallback{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" watchinguid=");
+ UserHandle.formatUid(sb, mWatchingUid);
+ sb.append(" from uid=");
+ UserHandle.formatUid(sb, mCallingUid);
+ sb.append(" pid=");
+ sb.append(mCallingPid);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void destroy() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ stopWatchingStarted(mCallback);
+ }
+ }
+
+ final class NotedCallback implements DeathRecipient {
+ final IAppOpsNotedCallback mCallback;
+ final int mWatchingUid;
+ final int mCallingUid;
+ final int mCallingPid;
+
+ NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid,
+ int callingPid) {
+ mCallback = callback;
+ mWatchingUid = watchingUid;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ /*ignored*/
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("NotedCallback{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" watchinguid=");
+ UserHandle.formatUid(sb, mWatchingUid);
+ sb.append(" from uid=");
+ UserHandle.formatUid(sb, mCallingUid);
+ sb.append(" pid=");
+ sb.append(mCallingPid);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void destroy() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ stopWatchingNoted(mCallback);
+ }
+ }
+
+ /**
+ * Call {@link AttributedOp#onClientDeath attributedOp.onClientDeath(clientId)}.
+ */
+ static void onClientDeath(@NonNull AttributedOp attributedOp,
+ @NonNull IBinder clientId) {
+ attributedOp.onClientDeath(clientId);
+ }
+
+
/**
* Loads the OpsValidation file results into a hashmap {@link #mNoteOpCallerStacktraces}
* so that we do not log the same operation twice between instances
@@ -233,12 +945,20 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public AppOpsService(File storagePath, Handler handler, Context context) {
- this(handler, context, new AppOpsServiceImpl(storagePath, handler, context));
- }
+ mContext = context;
- @VisibleForTesting
- public AppOpsService(Handler handler, Context context,
- AppOpsServiceInterface appOpsServiceInterface) {
+ for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) {
+ int switchCode = AppOpsManager.opToSwitch(switchedCode);
+ mSwitchedOps.put(switchCode,
+ ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode));
+ }
+ mAppOpsServiceInterface =
+ new LegacyAppOpsServiceInterfaceImpl(this, this, handler, context, mSwitchedOps);
+ mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler,
+ mAppOpsServiceInterface);
+
+ LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
+ mFile = new AtomicFile(storagePath, "appops");
if (AppOpsManager.NOTE_OP_COLLECTION_ENABLED) {
mNoteOpCallerStacktracesFile = new File(SystemServiceManager.ensureSystemDir(),
"noteOpStackTraces.json");
@@ -246,25 +966,189 @@ public class AppOpsService extends IAppOpsService.Stub {
} else {
mNoteOpCallerStacktracesFile = null;
}
-
- mAppOpsService = appOpsServiceInterface;
- mContext = context;
mHandler = handler;
+ mConstants = new Constants(mHandler);
+ readState();
}
- /**
- * Publishes binder and local service.
- */
public void publish() {
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
}
- /**
- * Finishes boot sequence.
- */
+ /** Handler for work when packages are removed or updated */
+ private BroadcastReceiver mOnPackageUpdatedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ String pkgName = intent.getData().getEncodedSchemeSpecificPart();
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
+
+ if (action.equals(ACTION_PACKAGE_REMOVED) && !intent.hasExtra(EXTRA_REPLACING)) {
+ synchronized (AppOpsService.this) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+ mAppOpsServiceInterface.removePackage(pkgName, UserHandle.getUserId(uid));
+ Ops removedOps = uidState.pkgOps.remove(pkgName);
+ if (removedOps != null) {
+ scheduleFastWriteLocked();
+ }
+ }
+ } else if (action.equals(Intent.ACTION_PACKAGE_REPLACED)) {
+ AndroidPackage pkg = getPackageManagerInternal().getPackage(pkgName);
+ if (pkg == null) {
+ return;
+ }
+
+ ArrayMap<String, String> dstAttributionTags = new ArrayMap<>();
+ ArraySet<String> attributionTags = new ArraySet<>();
+ attributionTags.add(null);
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
+ attributionTags.add(attribution.getTag());
+
+ int numInheritFrom = attribution.getInheritFrom().size();
+ for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
+ inheritFromNum++) {
+ dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum),
+ attribution.getTag());
+ }
+ }
+ }
+
+ synchronized (AppOpsService.this) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+
+ Ops ops = uidState.pkgOps.get(pkgName);
+ if (ops == null) {
+ return;
+ }
+
+ // Reset cached package properties to re-initialize when needed
+ ops.bypass = null;
+ ops.knownAttributionTags.clear();
+
+ // Merge data collected for removed attributions into their successor
+ // attributions
+ int numOps = ops.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ Op op = ops.valueAt(opNum);
+
+ int numAttributions = op.mAttributions.size();
+ for (int attributionNum = numAttributions - 1; attributionNum >= 0;
+ attributionNum--) {
+ String attributionTag = op.mAttributions.keyAt(attributionNum);
+
+ if (attributionTags.contains(attributionTag)) {
+ // attribution still exist after upgrade
+ continue;
+ }
+
+ String newAttributionTag = dstAttributionTags.get(attributionTag);
+
+ AttributedOp newAttributedOp = op.getOrCreateAttribution(op,
+ newAttributionTag);
+ newAttributedOp.add(op.mAttributions.valueAt(attributionNum));
+ op.mAttributions.removeAt(attributionNum);
+
+ scheduleFastWriteLocked();
+ }
+ }
+ }
+ }
+ }
+ };
+
public void systemReady() {
- mAppOpsService.systemReady();
+ synchronized (this) {
+ upgradeLocked(mVersionAtBoot);
+ }
+
+ mConstants.startMonitoring(mContext.getContentResolver());
+ mHistoricalRegistry.systemReady(mContext.getContentResolver());
+
+ IntentFilter packageUpdateFilter = new IntentFilter();
+ packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ packageUpdateFilter.addDataScheme("package");
+
+ mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL,
+ packageUpdateFilter, null, null);
+
+ synchronized (this) {
+ for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) {
+ int uid = mUidStates.keyAt(uidNum);
+ UidState uidState = mUidStates.valueAt(uidNum);
+
+ String[] pkgsInUid = getPackagesForUid(uidState.uid);
+ if (ArrayUtils.isEmpty(pkgsInUid)) {
+ uidState.clear();
+ mUidStates.removeAt(uidNum);
+ scheduleFastWriteLocked();
+ continue;
+ }
+
+ ArrayMap<String, Ops> pkgs = uidState.pkgOps;
+ if (pkgs == null) {
+ continue;
+ }
+
+ int numPkgs = pkgs.size();
+ for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+ String pkg = pkgs.keyAt(pkgNum);
+
+ String action;
+ if (!ArrayUtils.contains(pkgsInUid, pkg)) {
+ action = Intent.ACTION_PACKAGE_REMOVED;
+ } else {
+ action = Intent.ACTION_PACKAGE_REPLACED;
+ }
+
+ SystemServerInitThreadPool.submit(
+ () -> mOnPackageUpdatedReceiver.onReceive(mContext, new Intent(action)
+ .setData(Uri.fromParts("package", pkg, null))
+ .putExtra(Intent.EXTRA_UID, uid)),
+ "Update app-ops uidState in case package " + pkg + " changed");
+ }
+ }
+ }
+
+ final IntentFilter packageSuspendFilter = new IntentFilter();
+ packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
+ packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
+ final String[] changedPkgs = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ for (int code : OPS_RESTRICTED_ON_SUSPEND) {
+ ArraySet<OnOpModeChangedListener> onModeChangedListeners;
+ synchronized (AppOpsService.this) {
+ onModeChangedListeners =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (onModeChangedListeners == null) {
+ continue;
+ }
+ }
+ for (int i = 0; i < changedUids.length; i++) {
+ final int changedUid = changedUids[i];
+ final String changedPkg = changedPkgs[i];
+ // We trust packagemanager to insert matching uid and packageNames in the
+ // extras
+ notifyOpChanged(onModeChangedListeners, code, changedUid, changedPkg);
+ }
+ }
+ }
+ }, UserHandle.ALL, packageSuspendFilter, null, null);
final IntentFilter packageAddedFilter = new IntentFilter();
packageAddedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -272,8 +1156,9 @@ public class AppOpsService extends IAppOpsService.Stub {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final Uri data = intent.getData();
- final String packageName = intent.getData().getSchemeSpecificPart();
+ final String packageName = data.getSchemeSpecificPart();
PackageInfo pi = getPackageManagerInternal().getPackageInfo(packageName,
PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
if (isSamplingTarget(pi)) {
@@ -308,6 +1193,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
});
+
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
}
/**
@@ -322,18 +1209,132 @@ public class AppOpsService extends IAppOpsService.Stub {
mCheckOpsDelegateDispatcher = new CheckOpsDelegateDispatcher(policy, delegate);
}
- /**
- * Notify when a package is removed
- */
public void packageRemoved(int uid, String packageName) {
- mAppOpsService.packageRemoved(uid, packageName);
+ synchronized (this) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ return;
+ }
+
+ Ops removedOps = null;
+
+ // Remove any package state if such.
+ if (uidState.pkgOps != null) {
+ removedOps = uidState.pkgOps.remove(packageName);
+ mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
+ }
+
+ // If we just nuked the last package state check if the UID is valid.
+ if (removedOps != null && uidState.pkgOps.isEmpty()
+ && getPackagesForUid(uid).length <= 0) {
+ uidState.clear();
+ mUidStates.remove(uid);
+ }
+
+ if (removedOps != null) {
+ scheduleFastWriteLocked();
+
+ final int numOps = removedOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = removedOps.valueAt(opNum);
+
+ final int numAttributions = op.mAttributions.size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ AttributedOp attributedOp = op.mAttributions.valueAt(attributionNum);
+
+ while (attributedOp.isRunning()) {
+ attributedOp.finished(attributedOp.mInProgressEvents.keyAt(0));
+ }
+ while (attributedOp.isPaused()) {
+ attributedOp.finished(attributedOp.mPausedInProgressEvents.keyAt(0));
+ }
+ }
+ }
+ }
+ }
+
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory,
+ mHistoricalRegistry, uid, packageName));
}
- /**
- * Notify when a uid is removed.
- */
public void uidRemoved(int uid) {
- mAppOpsService.uidRemoved(uid);
+ synchronized (this) {
+ if (mUidStates.indexOfKey(uid) >= 0) {
+ mUidStates.get(uid).clear();
+ mUidStates.remove(uid);
+ scheduleFastWriteLocked();
+ }
+ }
+ }
+
+ // The callback method from ForegroundPolicyInterface
+ private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
+ synchronized (this) {
+ UidState uidState = getUidStateLocked(uid, true);
+
+ if (uidState != null && foregroundModeMayChange && uidState.hasForegroundWatchers) {
+ for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) {
+ if (!uidState.foregroundOps.valueAt(fgi)) {
+ continue;
+ }
+ final int code = uidState.foregroundOps.keyAt(fgi);
+
+ 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));
+ } else if (uidState.pkgOps != null) {
+ final ArraySet<OnOpModeChangedListener> listenerSet =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (listenerSet != null) {
+ for (int cbi = listenerSet.size() - 1; cbi >= 0; cbi--) {
+ final OnOpModeChangedListener listener = listenerSet.valueAt(cbi);
+ if ((listener.getFlags()
+ & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
+ || !listener.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 (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChanged,
+ this, listenerSet.valueAt(cbi), code, uidState.uid,
+ uidState.pkgOps.keyAt(pkgi)));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (uidState != null && uidState.pkgOps != null) {
+ int numPkgs = uidState.pkgOps.size();
+ for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+ Ops ops = uidState.pkgOps.valueAt(pkgNum);
+
+ int numOps = ops.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ Op op = ops.valueAt(opNum);
+
+ int numAttributions = op.mAttributions.size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ AttributedOp attributedOp = op.mAttributions.valueAt(
+ attributionNum);
+
+ attributedOp.onUidStateChanged(state);
+ }
+ }
+ }
+ }
+ }
}
/**
@@ -341,60 +1342,542 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
public void updateUidProcState(int uid, int procState,
@ActivityManager.ProcessCapability int capability) {
- mAppOpsService.updateUidProcState(uid, procState, capability);
+ synchronized (this) {
+ getUidStateTracker().updateUidProcState(uid, procState, capability);
+ if (!mUidStates.contains(uid)) {
+ UidState uidState = new UidState(uid);
+ mUidStates.put(uid, uidState);
+ onUidStateChanged(uid,
+ AppOpsUidStateTracker.processStateToUidState(procState), false);
+ }
+ }
}
- /**
- * Initiates shutdown.
- */
public void shutdown() {
- mAppOpsService.shutdown();
-
+ Slog.w(TAG, "Writing app ops before shutdown...");
+ boolean doWrite = false;
+ synchronized (this) {
+ if (mWriteScheduled) {
+ mWriteScheduled = false;
+ mFastWriteScheduled = false;
+ mHandler.removeCallbacks(mWriteRunner);
+ doWrite = true;
+ }
+ }
+ if (doWrite) {
+ writeState();
+ }
if (AppOpsManager.NOTE_OP_COLLECTION_ENABLED && mWriteNoteOpsScheduled) {
writeNoteOps();
}
+
+ mHistoricalRegistry.shutdown();
+ }
+
+ private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
+ ArrayList<AppOpsManager.OpEntry> resOps = null;
+ if (ops == null) {
+ resOps = new ArrayList<>();
+ for (int j=0; j<pkgOps.size(); j++) {
+ Op curOp = pkgOps.valueAt(j);
+ resOps.add(getOpEntryForResult(curOp));
+ }
+ } else {
+ for (int j=0; j<ops.length; j++) {
+ Op curOp = pkgOps.get(ops[j]);
+ if (curOp != null) {
+ if (resOps == null) {
+ resOps = new ArrayList<>();
+ }
+ resOps.add(getOpEntryForResult(curOp));
+ }
+ }
+ }
+ return resOps;
+ }
+
+ @Nullable
+ private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
+ @Nullable int[] ops) {
+ final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes == null) {
+ return null;
+ }
+
+ int opModeCount = opModes.size();
+ if (opModeCount == 0) {
+ return null;
+ }
+ ArrayList<AppOpsManager.OpEntry> resOps = null;
+ if (ops == null) {
+ resOps = new ArrayList<>();
+ for (int i = 0; i < opModeCount; i++) {
+ 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 (opModes.indexOfKey(code) >= 0) {
+ if (resOps == null) {
+ resOps = new ArrayList<>();
+ }
+ resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
+ }
+ }
+ }
+ return resOps;
+ }
+
+ private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) {
+ return op.createEntryLocked();
}
@Override
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
- return mAppOpsService.getPackagesForOps(ops);
+ final int callingUid = Binder.getCallingUid();
+ final boolean hasAllPackageAccess = mContext.checkPermission(
+ Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(),
+ Binder.getCallingUid(), null) == PackageManager.PERMISSION_GRANTED;
+ ArrayList<AppOpsManager.PackageOps> res = null;
+ synchronized (this) {
+ final int uidStateCount = mUidStates.size();
+ for (int i = 0; i < uidStateCount; i++) {
+ UidState uidState = mUidStates.valueAt(i);
+ if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()) {
+ continue;
+ }
+ ArrayMap<String, Ops> packages = uidState.pkgOps;
+ final int packageCount = packages.size();
+ for (int j = 0; j < packageCount; j++) {
+ Ops pkgOps = packages.valueAt(j);
+ ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
+ if (resOps != null) {
+ if (res == null) {
+ res = new ArrayList<>();
+ }
+ AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
+ pkgOps.packageName, pkgOps.uidState.uid, resOps);
+ // Caller can always see their packages and with a permission all.
+ if (hasAllPackageAccess || callingUid == pkgOps.uidState.uid) {
+ res.add(resPackage);
+ }
+ }
+ }
+ }
+ }
+ return res;
}
@Override
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
int[] ops) {
- return mAppOpsService.getOpsForPackage(uid, packageName, ops);
+ enforceGetAppOpsStatsPermissionIfNeeded(uid,packageName);
+ String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return Collections.emptyList();
+ }
+ synchronized (this) {
+ Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, false, null,
+ /* edit */ false);
+ if (pkgOps == null) {
+ return null;
+ }
+ ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
+ if (resOps == null) {
+ return null;
+ }
+ ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
+ AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
+ pkgOps.packageName, pkgOps.uidState.uid, resOps);
+ res.add(resPackage);
+ return res;
+ }
+ }
+
+ private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ // We get to access everything
+ if (callingUid == Process.myPid()) {
+ return;
+ }
+ // Apps can access their own data
+ if (uid == callingUid && packageName != null
+ && checkPackage(uid, packageName) == MODE_ALLOWED) {
+ return;
+ }
+ // Otherwise, you need a permission...
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), callingUid, null);
+ }
+
+ /**
+ * Verify that historical appop request arguments are valid.
+ */
+ private void ensureHistoricalOpRequestIsValid(int uid, String packageName,
+ String attributionTag, List<String> opNames, int filter, long beginTimeMillis,
+ long endTimeMillis, int flags) {
+ if ((filter & FILTER_BY_UID) != 0) {
+ Preconditions.checkArgument(uid != Process.INVALID_UID);
+ } else {
+ Preconditions.checkArgument(uid == Process.INVALID_UID);
+ }
+
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
+ Objects.requireNonNull(packageName);
+ } else {
+ Preconditions.checkArgument(packageName == null);
+ }
+
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) == 0) {
+ Preconditions.checkArgument(attributionTag == null);
+ }
+
+ if ((filter & FILTER_BY_OP_NAMES) != 0) {
+ Objects.requireNonNull(opNames);
+ } else {
+ Preconditions.checkArgument(opNames == null);
+ }
+
+ Preconditions.checkFlagsArgument(filter,
+ FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_ATTRIBUTION_TAG
+ | FILTER_BY_OP_NAMES);
+ Preconditions.checkArgumentNonnegative(beginTimeMillis);
+ Preconditions.checkArgument(endTimeMillis > beginTimeMillis);
+ Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
}
@Override
public void getHistoricalOps(int uid, String packageName, String attributionTag,
List<String> opNames, int dataType, int filter, long beginTimeMillis,
long endTimeMillis, int flags, RemoteCallback callback) {
- mAppOpsService.getHistoricalOps(uid, packageName, attributionTag, opNames,
- dataType, filter, beginTimeMillis, endTimeMillis, flags, callback);
+ PackageManager pm = mContext.getPackageManager();
+
+ ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
+ beginTimeMillis, endTimeMillis, flags);
+ Objects.requireNonNull(callback, "callback cannot be null");
+ ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+ boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
+ if (!isSelfRequest) {
+ boolean isCallerInstrumented =
+ ami.getInstrumentationSourceUid(Binder.getCallingUid()) != Process.INVALID_UID;
+ boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+ boolean isCallerPermissionController;
+ try {
+ isCallerPermissionController = pm.getPackageUidAsUser(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0,
+ UserHandle.getUserId(Binder.getCallingUid()))
+ == Binder.getCallingUid();
+ } catch (PackageManager.NameNotFoundException doesNotHappen) {
+ return;
+ }
+
+ boolean doesCallerHavePermission = mContext.checkPermission(
+ android.Manifest.permission.GET_HISTORICAL_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid())
+ == PackageManager.PERMISSION_GRANTED;
+
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController
+ && !doesCallerHavePermission) {
+ mHandler.post(() -> callback.sendResult(new Bundle()));
+ return;
+ }
+
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+ }
+
+ final String[] opNamesArray = (opNames != null)
+ ? opNames.toArray(new String[opNames.size()]) : null;
+
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
+ // Must not hold the appops lock
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
+ mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
List<String> opNames, int dataType, int filter, long beginTimeMillis,
long endTimeMillis, int flags, RemoteCallback callback) {
- mAppOpsService.getHistoricalOpsFromDiskRaw(uid, packageName, attributionTag,
- opNames, dataType, filter, beginTimeMillis, endTimeMillis, flags, callback);
+ ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
+ beginTimeMillis, endTimeMillis, flags);
+ Objects.requireNonNull(callback, "callback cannot be null");
+
+ mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
+ Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+
+ final String[] opNamesArray = (opNames != null)
+ ? opNames.toArray(new String[opNames.size()]) : null;
+
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
+ // Must not hold the appops lock
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
+ mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
public void reloadNonHistoricalState() {
- mAppOpsService.reloadNonHistoricalState();
+ mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
+ Binder.getCallingPid(), Binder.getCallingUid(), "reloadNonHistoricalState");
+ writeState();
+ readState();
}
@Override
public List<AppOpsManager.PackageOps> getUidOps(int uid, int[] ops) {
- return mAppOpsService.getUidOps(uid, ops);
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ synchronized (this) {
+ UidState uidState = getUidStateLocked(uid, false);
+ if (uidState == null) {
+ return null;
+ }
+ ArrayList<AppOpsManager.OpEntry> resOps = collectUidOps(uidState, ops);
+ if (resOps == null) {
+ return null;
+ }
+ ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
+ AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
+ null, uidState.uid, resOps);
+ res.add(resPackage);
+ return res;
+ }
+ }
+
+ private void pruneOpLocked(Op op, int uid, String packageName) {
+ op.removeAttributionsWithNoTime();
+
+ if (op.mAttributions.isEmpty()) {
+ 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,
+ UserHandle.getUserId(uidState.uid));
+ if (pkgOps.isEmpty()) {
+ uidState.pkgOps = null;
+ }
+ if (uidState.isDefault()) {
+ uidState.clear();
+ mUidStates.remove(uid);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) {
+ if (callingPid == Process.myPid()) {
+ return;
+ }
+ final int callingUser = UserHandle.getUserId(callingUid);
+ synchronized (this) {
+ if (mProfileOwners != null && mProfileOwners.get(callingUser, -1) == callingUid) {
+ if (targetUid >= 0 && callingUser == UserHandle.getUserId(targetUid)) {
+ // Profile owners are allowed to change modes but only for apps
+ // within their user.
+ return;
+ }
+ }
+ }
+ mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
}
@Override
public void setUidMode(int code, int uid, int mode) {
- mAppOpsService.setUidMode(code, uid, mode, null);
+ setUidMode(code, uid, mode, null);
+ }
+
+ private void setUidMode(int code, int uid, int mode,
+ @Nullable IAppOpsCallback permissionPolicyCallback) {
+ if (DEBUG) {
+ Slog.i(TAG, "uid " + uid + " OP_" + opToName(code) + " := " + modeToName(mode)
+ + " by uid " + Binder.getCallingUid());
+ }
+
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
+ verifyIncomingOp(code);
+ code = AppOpsManager.opToSwitch(code);
+
+ if (permissionPolicyCallback == null) {
+ updatePermissionRevokedCompat(uid, code, mode);
+ }
+
+ int previousMode;
+ synchronized (this) {
+ final int defaultMode = AppOpsManager.opToDefaultMode(code);
+
+ UidState uidState = getUidStateLocked(uid, false);
+ if (uidState == null) {
+ if (mode == defaultMode) {
+ return;
+ }
+ uidState = new UidState(uid);
+ mUidStates.put(uid, uidState);
+ }
+ if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+ previousMode = uidState.getUidMode(code);
+ } else {
+ // doesn't look right but is legacy behavior.
+ previousMode = MODE_DEFAULT;
+ }
+
+ if (!uidState.setUidMode(code, mode)) {
+ return;
+ }
+ uidState.evalForegroundOps();
+ if (mode != MODE_ERRORED && mode != previousMode) {
+ updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
+ }
+ }
+
+ notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
+ notifyOpChangedSync(code, uid, null, mode, previousMode);
+ }
+
+ /**
+ * 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) {
+ ModeCallback listenerToIgnore = callbackToIgnore != null
+ ? mModeWatchers.get(callbackToIgnore.asBinder()) : null;
+ mAppOpsServiceInterface.notifyOpChangedForAllPkgsInUid(code, uid, onlyForeground,
+ listenerToIgnore);
+ }
+
+ 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];
+
+ int[] ops = mSwitchedOps.get(switchCode);
+ for (int code : ops) {
+ String permissionName = AppOpsManager.opToPermission(code);
+ if (permissionName == null) {
+ continue;
+ }
+
+ if (packageManager.checkPermission(permissionName, packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ continue;
+ }
+
+ if (!permissionInfo.isRuntime()) {
+ continue;
+ }
+
+ boolean supportsRuntimePermissions = getPackageManagerInternal()
+ .getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M;
+
+ UserHandle user = UserHandle.getUserHandleForUid(uid);
+ boolean isRevokedCompat;
+ if (permissionInfo.backgroundPermission != null) {
+ if (packageManager.checkPermission(permissionInfo.backgroundPermission, packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+
+ if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
+ Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+ + " permission state, this is discouraged and you should revoke the"
+ + " runtime permission instead: uid=" + uid + ", switchCode="
+ + switchCode + ", mode=" + mode + ", permission="
+ + permissionInfo.backgroundPermission);
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
+ packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ isBackgroundRevokedCompat
+ ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
+ && mode != AppOpsManager.MODE_FOREGROUND;
+ } else {
+ isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+ }
+
+ if (isRevokedCompat && supportsRuntimePermissions) {
+ Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+ + " permission state, this is discouraged and you should revoke the"
+ + " runtime permission instead: uid=" + uid + ", switchCode="
+ + switchCode + ", mode=" + mode + ", permission=" + permissionName);
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ packageManager.updatePermissionFlags(permissionName, packageName,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, isRevokedCompat
+ ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode,
+ int previousMode) {
+ final StorageManagerInternal storageManagerInternal =
+ LocalServices.getService(StorageManagerInternal.class);
+ if (storageManagerInternal != null) {
+ storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode, previousMode);
+ }
}
/**
@@ -407,12 +1890,309 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
@Override
public void setMode(int code, int uid, @NonNull String packageName, int mode) {
- mAppOpsService.setMode(code, uid, packageName, mode, null);
+ setMode(code, uid, packageName, mode, null);
+ }
+
+ void setMode(int code, int uid, @NonNull String packageName, int mode,
+ @Nullable IAppOpsCallback permissionPolicyCallback) {
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
+ verifyIncomingOp(code);
+ if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+ return;
+ }
+
+ ArraySet<OnOpModeChangedListener> repCbs = null;
+ code = AppOpsManager.opToSwitch(code);
+
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, null);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot setMode", e);
+ return;
+ }
+
+ int previousMode = MODE_DEFAULT;
+ synchronized (this) {
+ UidState uidState = getUidStateLocked(uid, false);
+ Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
+ if (op != null) {
+ if (op.getMode() != mode) {
+ previousMode = op.getMode();
+ op.setMode(mode);
+
+ if (uidState != null) {
+ uidState.evalForegroundOps();
+ }
+ ArraySet<OnOpModeChangedListener> cbs =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArraySet<>();
+ }
+ repCbs.addAll(cbs);
+ }
+ cbs = mAppOpsServiceInterface.getPackageModeChangedListeners(packageName);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArraySet<>();
+ }
+ repCbs.addAll(cbs);
+ }
+ if (repCbs != null && permissionPolicyCallback != null) {
+ repCbs.remove(mModeWatchers.get(permissionPolicyCallback.asBinder()));
+ }
+ if (mode == AppOpsManager.opToDefaultMode(op.op)) {
+ // If going into the default mode, prune this op
+ // if there is nothing else interesting in it.
+ pruneOpLocked(op, uid, packageName);
+ }
+ scheduleFastWriteLocked();
+ if (mode != MODE_ERRORED) {
+ updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
+ }
+ }
+ }
+ }
+ if (repCbs != null) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChanged,
+ this, repCbs, code, uid, packageName));
+ }
+
+ notifyOpChangedSync(code, uid, packageName, mode, previousMode);
+ }
+
+ private void notifyOpChanged(ArraySet<OnOpModeChangedListener> callbacks, int code,
+ int uid, String packageName) {
+ for (int i = 0; i < callbacks.size(); i++) {
+ final OnOpModeChangedListener callback = callbacks.valueAt(i);
+ notifyOpChanged(callback, code, uid, packageName);
+ }
+ }
+
+ private void notifyOpChanged(OnOpModeChangedListener callback, int code,
+ int uid, String packageName) {
+ mAppOpsServiceInterface.notifyOpChanged(callback, code, uid, packageName);
+ }
+
+ private static ArrayList<ChangeRec> addChange(ArrayList<ChangeRec> reports,
+ int op, int uid, String packageName, int previousMode) {
+ boolean duplicate = false;
+ if (reports == null) {
+ reports = new ArrayList<>();
+ } else {
+ final int reportCount = reports.size();
+ for (int j = 0; j < reportCount; j++) {
+ ChangeRec report = reports.get(j);
+ if (report.op == op && report.pkg.equals(packageName)) {
+ duplicate = true;
+ break;
+ }
+ }
+ }
+ if (!duplicate) {
+ reports.add(new ChangeRec(op, uid, packageName, previousMode));
+ }
+
+ return reports;
+ }
+
+ private static HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> addCallbacks(
+ HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> callbacks,
+ int op, int uid, String packageName, int previousMode,
+ ArraySet<OnOpModeChangedListener> cbs) {
+ if (cbs == null) {
+ return callbacks;
+ }
+ if (callbacks == null) {
+ callbacks = new HashMap<>();
+ }
+ final int N = cbs.size();
+ for (int i=0; i<N; i++) {
+ OnOpModeChangedListener cb = cbs.valueAt(i);
+ ArrayList<ChangeRec> reports = callbacks.get(cb);
+ ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName, previousMode);
+ if (changed != reports) {
+ callbacks.put(cb, changed);
+ }
+ }
+ return callbacks;
+ }
+
+ static final class ChangeRec {
+ final int op;
+ final int uid;
+ final String pkg;
+ final int previous_mode;
+
+ ChangeRec(int _op, int _uid, String _pkg, int _previous_mode) {
+ op = _op;
+ uid = _uid;
+ pkg = _pkg;
+ previous_mode = _previous_mode;
+ }
}
@Override
public void resetAllModes(int reqUserId, String reqPackageName) {
- mAppOpsService.resetAllModes(reqUserId, reqPackageName);
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId,
+ true, true, "resetAllModes", null);
+
+ int reqUid = -1;
+ if (reqPackageName != null) {
+ try {
+ reqUid = AppGlobals.getPackageManager().getPackageUid(
+ reqPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, reqUserId);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
+ }
+
+ enforceManageAppOpsModes(callingPid, callingUid, reqUid);
+
+ HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> callbacks = null;
+ ArrayList<ChangeRec> allChanges = new ArrayList<>();
+ synchronized (this) {
+ boolean changed = false;
+ for (int i = mUidStates.size() - 1; i >= 0; i--) {
+ UidState uidState = mUidStates.valueAt(i);
+
+ 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);
+ uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
+ for (String packageName : getPackagesForUid(uidState.uid)) {
+ callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
+ previousMode,
+ mAppOpsServiceInterface.getOpModeChangedListeners(code));
+ callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
+ previousMode, mAppOpsServiceInterface
+ .getPackageModeChangedListeners(packageName));
+
+ allChanges = addChange(allChanges, code, uidState.uid,
+ packageName, previousMode);
+ }
+ }
+ }
+ }
+
+ if (uidState.pkgOps == null) {
+ continue;
+ }
+
+ if (reqUserId != UserHandle.USER_ALL
+ && reqUserId != UserHandle.getUserId(uidState.uid)) {
+ // Skip any ops for a different user
+ continue;
+ }
+
+ Map<String, Ops> packages = uidState.pkgOps;
+ Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
+ boolean uidChanged = false;
+ while (it.hasNext()) {
+ Map.Entry<String, Ops> ent = it.next();
+ String packageName = ent.getKey();
+ if (reqPackageName != null && !reqPackageName.equals(packageName)) {
+ // Skip any ops for a different package
+ continue;
+ }
+ Ops pkgOps = ent.getValue();
+ for (int j=pkgOps.size()-1; j>=0; j--) {
+ Op curOp = pkgOps.valueAt(j);
+ if (shouldDeferResetOpToDpm(curOp.op)) {
+ deferResetOpToDpm(curOp.op, reqPackageName, reqUserId);
+ continue;
+ }
+ if (AppOpsManager.opAllowsReset(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;
+ callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
+ previousMode,
+ mAppOpsServiceInterface.getOpModeChangedListeners(curOp.op));
+ callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
+ previousMode, mAppOpsServiceInterface
+ .getPackageModeChangedListeners(packageName));
+
+ allChanges = addChange(allChanges, curOp.op, uid, packageName,
+ previousMode);
+ curOp.removeAttributionsWithNoTime();
+ if (curOp.mAttributions.isEmpty()) {
+ pkgOps.removeAt(j);
+ }
+ }
+ }
+ if (pkgOps.size() == 0) {
+ it.remove();
+ mAppOpsServiceInterface.removePackage(packageName,
+ UserHandle.getUserId(uidState.uid));
+ }
+ }
+ if (uidState.isDefault()) {
+ uidState.clear();
+ mUidStates.remove(uidState.uid);
+ }
+ if (uidChanged) {
+ uidState.evalForegroundOps();
+ }
+ }
+
+ if (changed) {
+ scheduleFastWriteLocked();
+ }
+ }
+ if (callbacks != null) {
+ for (Map.Entry<OnOpModeChangedListener, ArrayList<ChangeRec>> ent
+ : callbacks.entrySet()) {
+ OnOpModeChangedListener cb = ent.getKey();
+ ArrayList<ChangeRec> reports = ent.getValue();
+ for (int i=0; i<reports.size(); i++) {
+ ChangeRec rep = reports.get(i);
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChanged,
+ this, cb, rep.op, rep.uid, rep.pkg));
+ }
+ }
+ }
+
+ int numChanges = allChanges.size();
+ for (int i = 0; i < numChanges; i++) {
+ ChangeRec change = allChanges.get(i);
+ notifyOpChangedSync(change.op, change.uid, change.pkg,
+ AppOpsManager.opToDefaultMode(change.op), change.previous_mode);
+ }
+ }
+
+ private boolean shouldDeferResetOpToDpm(int op) {
+ // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
+ // pre-grants to a role-based mechanism or another general-purpose mechanism.
+ return dpmi != null && dpmi.supportsResetOp(op);
+ }
+
+ /** Assumes {@link #shouldDeferResetOpToDpm(int)} is true. */
+ private void deferResetOpToDpm(int op, String packageName, @UserIdInt int userId) {
+ // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
+ // pre-grants to a role-based mechanism or another general-purpose mechanism.
+ dpmi.resetOp(op, packageName, userId);
+ }
+
+ private void evalAllForegroundOpsLocked() {
+ for (int uidi = mUidStates.size() - 1; uidi >= 0; uidi--) {
+ final UidState uidState = mUidStates.valueAt(uidi);
+ if (uidState.foregroundOps != null) {
+ uidState.evalForegroundOps();
+ }
+ }
}
@Override
@@ -423,17 +2203,66 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void startWatchingModeWithFlags(int op, String packageName, int flags,
IAppOpsCallback callback) {
- mAppOpsService.startWatchingModeWithFlags(op, packageName, flags, callback);
+ int watchedUid = -1;
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ // TODO: should have a privileged permission to protect this.
+ // Also, if the caller has requested WATCH_FOREGROUND_CHANGES, should we require
+ // the USAGE_STATS permission since this can provide information about when an
+ // app is in the foreground?
+ Preconditions.checkArgumentInRange(op, AppOpsManager.OP_NONE,
+ AppOpsManager._NUM_OP - 1, "Invalid op code: " + op);
+ if (callback == null) {
+ return;
+ }
+ final boolean mayWatchPackageName = packageName != null
+ && !filterAppAccessUnlocked(packageName, UserHandle.getUserId(callingUid));
+ synchronized (this) {
+ int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
+
+ int notifiedOps;
+ if ((flags & CALL_BACK_ON_SWITCHED_OP) == 0) {
+ 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, notifiedOps, callingUid,
+ callingPid);
+ mModeWatchers.put(callback.asBinder(), cb);
+ }
+ if (switchOp != AppOpsManager.OP_NONE) {
+ mAppOpsServiceInterface.startWatchingOpModeChanged(cb, switchOp);
+ }
+ if (mayWatchPackageName) {
+ mAppOpsServiceInterface.startWatchingPackageModeChanged(cb, packageName);
+ }
+ evalAllForegroundOpsLocked();
+ }
}
@Override
public void stopWatchingMode(IAppOpsCallback callback) {
- mAppOpsService.stopWatchingMode(callback);
+ if (callback == null) {
+ return;
+ }
+ synchronized (this) {
+ ModeCallback cb = mModeWatchers.remove(callback.asBinder());
+ if (cb != null) {
+ cb.unlinkToDeath();
+ mAppOpsServiceInterface.removeListener(cb);
+ }
+
+ evalAllForegroundOpsLocked();
+ }
}
- /**
- * @return the current {@link CheckOpsDelegate}.
- */
public CheckOpsDelegate getAppOpsServiceDelegate() {
synchronized (AppOpsService.this) {
final CheckOpsDelegateDispatcher dispatcher = mCheckOpsDelegateDispatcher;
@@ -441,9 +2270,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- /**
- * Sets the appops {@link CheckOpsDelegate}
- */
public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) {
synchronized (AppOpsService.this) {
final CheckOpsDelegateDispatcher oldDispatcher = mCheckOpsDelegateDispatcher;
@@ -467,7 +2293,58 @@ public class AppOpsService extends IAppOpsService.Stub {
private int checkOperationImpl(int code, int uid, String packageName,
@Nullable String attributionTag, boolean raw) {
- return mAppOpsService.checkOperation(code, uid, packageName, attributionTag, raw);
+ verifyIncomingOp(code);
+ if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+ return AppOpsManager.opToDefaultMode(code);
+ }
+
+ String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag, raw);
+ }
+
+ /**
+ * Get the mode of an app-op.
+ *
+ * @param code The code of the op
+ * @param uid The uid of the package the op belongs to
+ * @param packageName The package the op belongs to
+ * @param raw If the raw state of eval-ed state should be checked.
+ *
+ * @return The mode of the op
+ */
+ private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, boolean raw) {
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, null);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "checkOperation", e);
+ return AppOpsManager.opToDefaultMode(code);
+ }
+
+ if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ synchronized (this) {
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, true)) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ code = AppOpsManager.opToSwitch(code);
+ UidState uidState = getUidStateLocked(uid, false);
+ 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.getMode() : op.uidState.evalMode(op.op, op.getMode());
+ }
}
@Override
@@ -487,8 +2364,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void setAudioRestriction(int code, int usage, int uid, int mode,
String[] exceptionPackages) {
- mAppOpsService.enforceManageAppOpsModes(Binder.getCallingPid(),
- Binder.getCallingUid(), uid);
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingUid(uid);
verifyIncomingOp(code);
@@ -496,35 +2372,58 @@ public class AppOpsService extends IAppOpsService.Stub {
code, usage, uid, mode, exceptionPackages);
mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceInterface::notifyWatchersOfChange, mAppOpsService, code,
- UID_ANY));
+ AppOpsService::notifyWatchersOfChange, this, code, UID_ANY));
}
@Override
public void setCameraAudioRestriction(@CAMERA_AUDIO_RESTRICTION int mode) {
- mAppOpsService.enforceManageAppOpsModes(Binder.getCallingPid(),
- Binder.getCallingUid(), -1);
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), -1);
mAudioRestrictionManager.setCameraAudioRestriction(mode);
mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceInterface::notifyWatchersOfChange, mAppOpsService,
+ AppOpsService::notifyWatchersOfChange, this,
AppOpsManager.OP_PLAY_AUDIO, UID_ANY));
mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceInterface::notifyWatchersOfChange, mAppOpsService,
+ AppOpsService::notifyWatchersOfChange, this,
AppOpsManager.OP_VIBRATE, UID_ANY));
}
@Override
public int checkPackage(int uid, String packageName) {
- return mAppOpsService.checkPackage(uid, packageName);
+ Objects.requireNonNull(packageName);
+ try {
+ verifyAndGetBypass(uid, packageName, null);
+ // When the caller is the system, it's possible that the packageName is the special
+ // one (e.g., "root") which isn't actually existed.
+ if (resolveUid(packageName) == uid
+ || (isPackageExisted(packageName)
+ && !filterAppAccessUnlocked(packageName, UserHandle.getUserId(uid)))) {
+ return AppOpsManager.MODE_ALLOWED;
+ }
+ return AppOpsManager.MODE_ERRORED;
+ } catch (SecurityException ignored) {
+ return AppOpsManager.MODE_ERRORED;
+ }
}
private boolean isPackageExisted(String packageName) {
return getPackageManagerInternal().getPackageStateInternal(packageName) != null;
}
+ /**
+ * This method will check with PackageManager to determine if the package provided should
+ * be visible to the {@link Binder#getCallingUid()}.
+ *
+ * NOTE: This must not be called while synchronized on {@code this} to avoid dead locks
+ */
+ private boolean filterAppAccessUnlocked(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ return LocalServices.getService(PackageManagerInternal.class)
+ .filterAppAccess(packageName, callingUid, userId);
+ }
+
@Override
public SyncNotedAppOp noteProxyOperation(int code, AttributionSource attributionSource,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
@@ -570,20 +2469,13 @@ public class AppOpsService extends IAppOpsService.Stub {
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
- final int proxyReturn = mAppOpsService.noteOperationUnchecked(code, proxyUid,
+ final SyncNotedAppOp proxyReturn = noteOperationUnchecked(code, proxyUid,
resolveProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
- proxyFlags);
- if (proxyReturn != AppOpsManager.MODE_ALLOWED) {
- return new SyncNotedAppOp(proxyReturn, code, proxiedAttributionTag,
+ proxyFlags, !isProxyTrusted, "proxy " + message, shouldCollectMessage);
+ if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
+ return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
proxiedPackageName);
}
- if (shouldCollectAsyncNotedOp) {
- boolean isProxyAttributionTagValid = mAppOpsService.isAttributionTagValid(proxyUid,
- resolveProxyPackageName, proxyAttributionTag, null);
- collectAsyncNotedOp(proxyUid, resolveProxyPackageName, code,
- isProxyAttributionTagValid ? proxyAttributionTag : null, proxyFlags,
- message, shouldCollectMessage);
- }
}
String resolveProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
@@ -595,32 +2487,9 @@ public class AppOpsService extends IAppOpsService.Stub {
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
- final int result = mAppOpsService.noteOperationUnchecked(code, proxiedUid,
- resolveProxiedPackageName, proxiedAttributionTag, proxyUid, resolveProxyPackageName,
- proxyAttributionTag, proxiedFlags);
-
- boolean isProxiedAttributionTagValid = mAppOpsService.isAttributionTagValid(proxiedUid,
- resolveProxiedPackageName, proxiedAttributionTag, resolveProxyPackageName);
- if (shouldCollectAsyncNotedOp && result == AppOpsManager.MODE_ALLOWED) {
- collectAsyncNotedOp(proxiedUid, resolveProxiedPackageName, code,
- isProxiedAttributionTagValid ? proxiedAttributionTag : null, proxiedFlags,
- message, shouldCollectMessage);
- }
-
-
- return new SyncNotedAppOp(result, code,
- isProxiedAttributionTagValid ? proxiedAttributionTag : null,
- resolveProxiedPackageName);
- }
-
- private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
- if (attributionSource.getUid() != Binder.getCallingUid()
- && attributionSource.isTrusted(mContext)) {
- return true;
- }
- return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null)
- == PackageManager.PERMISSION_GRANTED;
+ return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
+ proxiedAttributionTag, proxyUid, resolveProxyPackageName, proxyAttributionTag,
+ proxiedFlags, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
@Override
@@ -634,58 +2503,258 @@ public class AppOpsService extends IAppOpsService.Stub {
private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
- int result = mAppOpsService.noteOperation(code, uid, packageName,
- attributionTag, message);
-
String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
+ }
+ return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
+ Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ }
- boolean isAttributionTagValid = mAppOpsService.isAttributionTagValid(uid,
- resolvedPackageName, attributionTag, null);
-
- if (shouldCollectAsyncNotedOp && result == MODE_ALLOWED) {
- collectAsyncNotedOp(uid, resolvedPackageName, code,
- isAttributionTagValid ? attributionTag : null, AppOpsManager.OP_FLAG_SELF,
- message, shouldCollectMessage);
+ private SyncNotedAppOp noteOperationUnchecked(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, int proxyUid, String proxyPackageName,
+ @Nullable String proxyAttributionTag, @OpFlags int flags,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage) {
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ boolean wasNull = attributionTag == null;
+ if (!pvr.isAttributionTagValid) {
+ attributionTag = null;
+ }
+ } catch (SecurityException e) {
+ Slog.e(TAG, "noteOperation", e);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
- return new SyncNotedAppOp(result, code, isAttributionTagValid ? attributionTag : null,
- resolvedPackageName);
+ synchronized (this) {
+ final Ops ops = getOpsLocked(uid, packageName, attributionTag,
+ pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
+ if (ops == null) {
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ AppOpsManager.MODE_IGNORED);
+ if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ + " package " + packageName + "flags: " +
+ AppOpsManager.flagsToString(flags));
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
+ }
+ final Op op = getOpLocked(ops, code, uid, true);
+ final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
+ if (attributedOp.isRunning()) {
+ Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
+ + code + " startTime of in progress event="
+ + attributedOp.mInProgressEvents.valueAt(0).getStartTime());
+ }
+
+ final int switchCode = AppOpsManager.opToSwitch(code);
+ final UidState uidState = ops.uidState;
+ if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, false)) {
+ attributedOp.rejected(uidState.getState(), flags);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ AppOpsManager.MODE_IGNORED);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
+ }
+ // 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.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 "
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
+ attributedOp.rejected(uidState.getState(), flags);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ uidMode);
+ return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
+ }
+ } else {
+ final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
+ : op;
+ final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
+ attributedOp.rejected(uidState.getState(), flags);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ mode);
+ return new SyncNotedAppOp(mode, code, attributionTag, packageName);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG,
+ "noteOperation: allowing code " + code + " uid " + uid + " package "
+ + packageName + (attributionTag == null ? ""
+ : "." + attributionTag) + " flags: "
+ + AppOpsManager.flagsToString(flags));
+ }
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ AppOpsManager.MODE_ALLOWED);
+ attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState.getState(),
+ flags);
+
+ if (shouldCollectAsyncNotedOp) {
+ collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
+ shouldCollectMessage);
+ }
+
+ return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ packageName);
+ }
}
// TODO moltmann: Allow watching for attribution ops
@Override
public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
- mAppOpsService.startWatchingActive(ops, callback);
+ int watchedUid = Process.INVALID_UID;
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = callingUid;
+ }
+ if (ops != null) {
+ Preconditions.checkArrayElementsInRange(ops, 0,
+ AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops));
+ }
+ if (callback == null) {
+ return;
+ }
+ synchronized (this) {
+ SparseArray<ActiveCallback> callbacks = mActiveWatchers.get(callback.asBinder());
+ if (callbacks == null) {
+ callbacks = new SparseArray<>();
+ mActiveWatchers.put(callback.asBinder(), callbacks);
+ }
+ final ActiveCallback activeCallback = new ActiveCallback(callback, watchedUid,
+ callingUid, callingPid);
+ for (int op : ops) {
+ callbacks.put(op, activeCallback);
+ }
+ }
}
@Override
public void stopWatchingActive(IAppOpsActiveCallback callback) {
- mAppOpsService.stopWatchingActive(callback);
+ if (callback == null) {
+ return;
+ }
+ synchronized (this) {
+ final SparseArray<ActiveCallback> activeCallbacks =
+ mActiveWatchers.remove(callback.asBinder());
+ if (activeCallbacks == null) {
+ return;
+ }
+ final int callbackCount = activeCallbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ activeCallbacks.valueAt(i).destroy();
+ }
+ }
}
@Override
public void startWatchingStarted(int[] ops, @NonNull IAppOpsStartedCallback callback) {
- mAppOpsService.startWatchingStarted(ops, callback);
+ int watchedUid = Process.INVALID_UID;
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = callingUid;
+ }
+
+ Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
+ Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
+ "Invalid op code in: " + Arrays.toString(ops));
+ Objects.requireNonNull(callback, "Callback cannot be null");
+
+ synchronized (this) {
+ SparseArray<StartedCallback> callbacks = mStartedWatchers.get(callback.asBinder());
+ if (callbacks == null) {
+ callbacks = new SparseArray<>();
+ mStartedWatchers.put(callback.asBinder(), callbacks);
+ }
+
+ final StartedCallback startedCallback = new StartedCallback(callback, watchedUid,
+ callingUid, callingPid);
+ for (int op : ops) {
+ callbacks.put(op, startedCallback);
+ }
+ }
}
@Override
public void stopWatchingStarted(IAppOpsStartedCallback callback) {
- mAppOpsService.stopWatchingStarted(callback);
+ Objects.requireNonNull(callback, "Callback cannot be null");
+
+ synchronized (this) {
+ final SparseArray<StartedCallback> startedCallbacks =
+ mStartedWatchers.remove(callback.asBinder());
+ if (startedCallbacks == null) {
+ return;
+ }
+
+ final int callbackCount = startedCallbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ startedCallbacks.valueAt(i).destroy();
+ }
+ }
}
@Override
public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
- mAppOpsService.startWatchingNoted(ops, callback);
+ int watchedUid = Process.INVALID_UID;
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = callingUid;
+ }
+ Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
+ Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
+ "Invalid op code in: " + Arrays.toString(ops));
+ Objects.requireNonNull(callback, "Callback cannot be null");
+ synchronized (this) {
+ SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
+ if (callbacks == null) {
+ callbacks = new SparseArray<>();
+ mNotedWatchers.put(callback.asBinder(), callbacks);
+ }
+ final NotedCallback notedCallback = new NotedCallback(callback, watchedUid,
+ callingUid, callingPid);
+ for (int op : ops) {
+ callbacks.put(op, notedCallback);
+ }
+ }
}
@Override
public void stopWatchingNoted(IAppOpsNotedCallback callback) {
- mAppOpsService.stopWatchingNoted(callback);
+ Objects.requireNonNull(callback, "Callback cannot be null");
+ synchronized (this) {
+ final SparseArray<NotedCallback> notedCallbacks =
+ mNotedWatchers.remove(callback.asBinder());
+ if (notedCallbacks == null) {
+ return;
+ }
+ final int callbackCount = notedCallbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ notedCallbacks.valueAt(i).destroy();
+ }
+ }
}
/**
@@ -772,7 +2841,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid();
Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
- mAppOpsService.verifyPackage(uid, packageName);
+ verifyAndGetBypass(uid, packageName, null);
synchronized (this) {
RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -802,7 +2871,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid();
Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
- mAppOpsService.verifyPackage(uid, packageName);
+ verifyAndGetBypass(uid, packageName, null);
synchronized (this) {
RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -821,7 +2890,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid();
- mAppOpsService.verifyPackage(uid, packageName);
+ verifyAndGetBypass(uid, packageName, null);
synchronized (this) {
return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid));
@@ -844,49 +2913,54 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message,
boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
int attributionChainId) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
- int result = mAppOpsService.startOperation(clientId, code, uid, packageName,
- attributionTag, startIfModeDefault, message,
- attributionFlags, attributionChainId);
-
String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
-
- boolean isAttributionTagValid = mAppOpsService.isAttributionTagValid(uid,
- resolvedPackageName, attributionTag, null);
-
- if (shouldCollectAsyncNotedOp && result == MODE_ALLOWED) {
- collectAsyncNotedOp(uid, resolvedPackageName, code,
- isAttributionTagValid ? attributionTag : null, AppOpsManager.OP_FLAG_SELF,
- message, shouldCollectMessage);
+ if (resolvedPackageName == null) {
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
- return new SyncNotedAppOp(result, code, isAttributionTagValid ? attributionTag : null,
- resolvedPackageName);
+ // As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution
+ // purposes and not as a check, also make sure that the caller is allowed to access
+ // the data gated by OP_RECORD_AUDIO.
+ //
+ // TODO: Revert this change before Android 12.
+ if (code == OP_RECORD_AUDIO_HOTWORD || code == OP_RECEIVE_AMBIENT_TRIGGER_AUDIO) {
+ int result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
+ if (result != AppOpsManager.MODE_ALLOWED) {
+ return new SyncNotedAppOp(result, code, attributionTag, packageName);
+ }
+ }
+ return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
+ Process.INVALID_UID, null, null, OP_FLAG_SELF, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
+ attributionChainId, /*dryRun*/ false);
}
@Override
- public SyncNotedAppOp startProxyOperation(IBinder clientId, int code,
+ public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
@AttributionFlags int proxiedAttributionFlags, int attributionChainId) {
- return mCheckOpsDelegateDispatcher.startProxyOperation(clientId, code,
- attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
- proxiedAttributionFlags, attributionChainId);
+ return mCheckOpsDelegateDispatcher.startProxyOperation(clientId, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags,
+ attributionChainId);
}
- private SyncNotedAppOp startProxyOperationImpl(IBinder clientId, int code,
+ private SyncNotedAppOp startProxyOperationImpl(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags
int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags,
int attributionChainId) {
-
final int proxyUid = attributionSource.getUid();
final String proxyPackageName = attributionSource.getPackageName();
final String proxyAttributionTag = attributionSource.getAttributionTag();
@@ -934,66 +3008,145 @@ public class AppOpsService extends IAppOpsService.Stub {
if (!skipProxyOperation) {
// Test if the proxied operation will succeed before starting the proxy operation
- final int testProxiedOp = mAppOpsService.startOperationUnchecked(clientId, code,
+ final SyncNotedAppOp testProxiedOp = startOperationUnchecked(clientId, code,
proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
proxiedAttributionFlags, attributionChainId, /*dryRun*/ true);
-
- boolean isTestProxiedAttributionTagValid =
- mAppOpsService.isAttributionTagValid(proxiedUid, resolvedProxiedPackageName,
- proxiedAttributionTag, resolvedProxyPackageName);
-
- if (!shouldStartForMode(testProxiedOp, startIfModeDefault)) {
- return new SyncNotedAppOp(testProxiedOp, code,
- isTestProxiedAttributionTagValid ? proxiedAttributionTag : null,
- resolvedProxiedPackageName);
+ if (!shouldStartForMode(testProxiedOp.getOpMode(), startIfModeDefault)) {
+ return testProxiedOp;
}
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
- final int proxyAppOp = mAppOpsService.startOperationUnchecked(clientId, code, proxyUid,
+ final SyncNotedAppOp proxyAppOp = startOperationUnchecked(clientId, code, proxyUid,
resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
- proxyFlags, startIfModeDefault, proxyAttributionFlags, attributionChainId,
+ proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
+ shouldCollectMessage, proxyAttributionFlags, attributionChainId,
/*dryRun*/ false);
+ if (!shouldStartForMode(proxyAppOp.getOpMode(), startIfModeDefault)) {
+ return proxyAppOp;
+ }
+ }
- boolean isProxyAttributionTagValid = mAppOpsService.isAttributionTagValid(proxyUid,
- resolvedProxyPackageName, proxyAttributionTag, null);
+ return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
+ proxiedAttributionTag, proxyUid, resolvedProxyPackageName, proxyAttributionTag,
+ proxiedFlags, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, proxiedAttributionFlags, attributionChainId,
+ /*dryRun*/ false);
+ }
- if (!shouldStartForMode(proxyAppOp, startIfModeDefault)) {
- return new SyncNotedAppOp(proxyAppOp, code,
- isProxyAttributionTagValid ? proxyAttributionTag : null,
- resolvedProxyPackageName);
- }
+ private boolean shouldStartForMode(int mode, boolean startIfModeDefault) {
+ return (mode == MODE_ALLOWED || (mode == MODE_DEFAULT && startIfModeDefault));
+ }
- if (shouldCollectAsyncNotedOp) {
- collectAsyncNotedOp(proxyUid, resolvedProxyPackageName, code,
- isProxyAttributionTagValid ? proxyAttributionTag : null, proxyFlags,
- message, shouldCollectMessage);
+ private SyncNotedAppOp startOperationUnchecked(IBinder clientId, int code, int uid,
+ @NonNull String packageName, @Nullable String attributionTag, int proxyUid,
+ String proxyPackageName, @Nullable String proxyAttributionTag, @OpFlags int flags,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId, boolean dryRun) {
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
+ if (!pvr.isAttributionTagValid) {
+ attributionTag = null;
}
+ } catch (SecurityException e) {
+ Slog.e(TAG, "startOperation", e);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
- final int proxiedAppOp = mAppOpsService.startOperationUnchecked(clientId, code, proxiedUid,
- resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
- resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
- proxiedAttributionFlags, attributionChainId,/*dryRun*/ false);
-
- boolean isProxiedAttributionTagValid = mAppOpsService.isAttributionTagValid(proxiedUid,
- resolvedProxiedPackageName, proxiedAttributionTag, resolvedProxyPackageName);
+ boolean isRestricted = false;
+ int startType = START_TYPE_FAILED;
+ synchronized (this) {
+ final Ops ops = getOpsLocked(uid, packageName, attributionTag,
+ pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
+ if (ops == null) {
+ if (!dryRun) {
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
+ flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags,
+ attributionChainId);
+ }
+ if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ + " package " + packageName + " flags: "
+ + AppOpsManager.flagsToString(flags));
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
+ }
+ final Op op = getOpLocked(ops, code, uid, true);
+ final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
+ final UidState uidState = ops.uidState;
+ isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
+ false);
+ 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.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 "
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
+ }
+ if (!dryRun) {
+ attributedOp.rejected(uidState.getState(), flags);
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
+ flags, uidMode, startType, attributionFlags, attributionChainId);
+ }
+ return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
+ }
+ } else {
+ final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
+ : op;
+ final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ if (mode != AppOpsManager.MODE_ALLOWED
+ && (!startIfModeDefault || mode != MODE_DEFAULT)) {
+ if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
+ if (!dryRun) {
+ attributedOp.rejected(uidState.getState(), flags);
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
+ flags, mode, startType, attributionFlags, attributionChainId);
+ }
+ return new SyncNotedAppOp(mode, code, attributionTag, packageName);
+ }
+ }
+ if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ + " package " + packageName + " restricted: " + isRestricted
+ + " flags: " + AppOpsManager.flagsToString(flags));
+ if (!dryRun) {
+ try {
+ if (isRestricted) {
+ attributedOp.createPaused(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState.getState(), flags,
+ attributionFlags, attributionChainId);
+ } else {
+ attributedOp.started(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState.getState(), flags,
+ attributionFlags, attributionChainId);
+ startType = START_TYPE_STARTED;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags,
+ attributionChainId);
+ }
+ }
- if (shouldCollectAsyncNotedOp && proxiedAppOp == MODE_ALLOWED) {
- collectAsyncNotedOp(proxyUid, resolvedProxiedPackageName, code,
- isProxiedAttributionTagValid ? proxiedAttributionTag : null,
- proxiedAttributionFlags, message, shouldCollectMessage);
+ if (shouldCollectAsyncNotedOp && !dryRun && !isRestricted) {
+ collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
+ message, shouldCollectMessage);
}
- return new SyncNotedAppOp(proxiedAppOp, code,
- isProxiedAttributionTagValid ? proxiedAttributionTag : null,
- resolvedProxiedPackageName);
- }
-
- private boolean shouldStartForMode(int mode, boolean startIfModeDefault) {
- return (mode == MODE_ALLOWED || (mode == MODE_DEFAULT && startIfModeDefault));
+ return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
+ packageName);
}
@Override
@@ -1005,11 +3158,22 @@ public class AppOpsService extends IAppOpsService.Stub {
private void finishOperationImpl(IBinder clientId, int code, int uid, String packageName,
String attributionTag) {
- mAppOpsService.finishOperation(clientId, code, uid, packageName, attributionTag);
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+ return;
+ }
+
+ String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return;
+ }
+
+ finishOperationUnchecked(clientId, code, uid, resolvedPackageName, attributionTag);
}
@Override
- public void finishProxyOperation(IBinder clientId, int code,
+ public void finishProxyOperation(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
mCheckOpsDelegateDispatcher.finishProxyOperation(clientId, code, attributionSource,
skipProxyOperation);
@@ -1041,8 +3205,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (!skipProxyOperation) {
- mAppOpsService.finishOperationUnchecked(clientId, code, proxyUid,
- resolvedProxyPackageName, proxyAttributionTag);
+ finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName,
+ proxyAttributionTag);
}
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
@@ -1051,12 +3215,209 @@ public class AppOpsService extends IAppOpsService.Stub {
return null;
}
- mAppOpsService.finishOperationUnchecked(clientId, code, proxiedUid,
- resolvedProxiedPackageName, proxiedAttributionTag);
+ finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
+ proxiedAttributionTag);
return null;
}
+ private void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag) {
+ PackageVerificationResult pvr;
+ try {
+ pvr = verifyAndGetBypass(uid, packageName, attributionTag);
+ if (!pvr.isAttributionTagValid) {
+ attributionTag = null;
+ }
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot finishOperation", e);
+ return;
+ }
+
+ synchronized (this) {
+ Op op = getOpLocked(code, uid, packageName, attributionTag, pvr.isAttributionTagValid,
+ pvr.bypass, /* edit */ true);
+ if (op == null) {
+ Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
+ return;
+ }
+ final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
+ if (attributedOp == null) {
+ Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
+ return;
+ }
+
+ if (attributedOp.isRunning() || attributedOp.isPaused()) {
+ attributedOp.finished(clientId);
+ } else {
+ Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
+ }
+ }
+ }
+
+ void scheduleOpActiveChangedIfNeededLocked(int code, int uid, @NonNull
+ String packageName, @Nullable String attributionTag, boolean active, @AttributionFlags
+ int attributionFlags, int attributionChainId) {
+ ArraySet<ActiveCallback> dispatchedCallbacks = null;
+ final int callbackListCount = mActiveWatchers.size();
+ for (int i = 0; i < callbackListCount; i++) {
+ final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i);
+ ActiveCallback callback = callbacks.get(code);
+ if (callback != null) {
+ if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+ continue;
+ }
+ if (dispatchedCallbacks == null) {
+ dispatchedCallbacks = new ArraySet<>();
+ }
+ dispatchedCallbacks.add(callback);
+ }
+ }
+ if (dispatchedCallbacks == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpActiveChanged,
+ this, dispatchedCallbacks, code, uid, packageName, attributionTag, active,
+ attributionFlags, attributionChainId));
+ }
+
+ private void notifyOpActiveChanged(ArraySet<ActiveCallback> callbacks,
+ int code, int uid, @NonNull String packageName, @Nullable String attributionTag,
+ boolean active, @AttributionFlags int attributionFlags, int attributionChainId) {
+ // 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 {
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ final ActiveCallback callback = callbacks.valueAt(i);
+ try {
+ if (shouldIgnoreCallback(code, callback.mCallingPid, callback.mCallingUid)) {
+ continue;
+ }
+ callback.mCallback.opActiveChanged(code, uid, packageName, attributionTag,
+ active, attributionFlags, attributionChainId);
+ } catch (RemoteException e) {
+ /* do nothing */
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
+ String attributionTag, @OpFlags int flags, @Mode int result,
+ @AppOpsManager.OnOpStartedListener.StartedType int startedType,
+ @AttributionFlags int attributionFlags, int attributionChainId) {
+ ArraySet<StartedCallback> dispatchedCallbacks = null;
+ final int callbackListCount = mStartedWatchers.size();
+ for (int i = 0; i < callbackListCount; i++) {
+ final SparseArray<StartedCallback> callbacks = mStartedWatchers.valueAt(i);
+
+ StartedCallback callback = callbacks.get(code);
+ if (callback != null) {
+ if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+ continue;
+ }
+
+ if (dispatchedCallbacks == null) {
+ dispatchedCallbacks = new ArraySet<>();
+ }
+ dispatchedCallbacks.add(callback);
+ }
+ }
+
+ if (dispatchedCallbacks == null) {
+ return;
+ }
+
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpStarted,
+ this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags,
+ result, startedType, attributionFlags, attributionChainId));
+ }
+
+ private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
+ int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
+ @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType,
+ @AttributionFlags int attributionFlags, int attributionChainId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ final StartedCallback callback = callbacks.valueAt(i);
+ try {
+ if (shouldIgnoreCallback(code, callback.mCallingPid, callback.mCallingUid)) {
+ continue;
+ }
+ callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags,
+ result, startedType, attributionFlags, attributionChainId);
+ } catch (RemoteException e) {
+ /* do nothing */
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
+ String attributionTag, @OpFlags int flags, @Mode int result) {
+ ArraySet<NotedCallback> dispatchedCallbacks = null;
+ final int callbackListCount = mNotedWatchers.size();
+ for (int i = 0; i < callbackListCount; i++) {
+ final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i);
+ final NotedCallback callback = callbacks.get(code);
+ if (callback != null) {
+ if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+ continue;
+ }
+ if (dispatchedCallbacks == null) {
+ dispatchedCallbacks = new ArraySet<>();
+ }
+ dispatchedCallbacks.add(callback);
+ }
+ }
+ if (dispatchedCallbacks == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChecked,
+ this, dispatchedCallbacks, code, uid, packageName, attributionTag, flags,
+ result));
+ }
+
+ private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
+ int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
+ @Mode int result) {
+ // There are features watching for checks in our process. The callbacks in
+ // these features may require permissions our remote caller does not have.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ final NotedCallback callback = callbacks.valueAt(i);
+ try {
+ if (shouldIgnoreCallback(code, callback.mCallingPid, callback.mCallingUid)) {
+ continue;
+ }
+ callback.mCallback.opNoted(code, uid, packageName, attributionTag, flags,
+ result);
+ } catch (RemoteException e) {
+ /* do nothing */
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public int permissionToOpCode(String permission) {
if (permission == null) {
@@ -1114,6 +3475,13 @@ public class AppOpsService extends IAppOpsService.Stub {
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
+ private boolean shouldIgnoreCallback(int op, int watcherPid, int watcherUid) {
+ // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission,
+ // as watcher should not use this to signal if the value is changed.
+ return opRestrictsRead(op) && mContext.checkPermission(Manifest.permission.MANAGE_APPOPS,
+ watcherPid, watcherUid) != PackageManager.PERMISSION_GRANTED;
+ }
+
private void verifyIncomingOp(int op) {
if (op >= 0 && op < AppOpsManager._NUM_OP) {
// Enforce manage appops permission if it's a restricted read op.
@@ -1154,6 +3522,35 @@ public class AppOpsService extends IAppOpsService.Stub {
|| resolveUid(resolvedPackage) != Process.INVALID_UID;
}
+ private boolean isCallerAndAttributionTrusted(@NonNull AttributionSource attributionSource) {
+ if (attributionSource.getUid() != Binder.getCallingUid()
+ && attributionSource.isTrusted(mContext)) {
+ return true;
+ }
+ return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ if (!edit) {
+ return null;
+ }
+ uidState = new UidState(uid);
+ mUidStates.put(uid, uidState);
+ }
+
+ return uidState;
+ }
+
+ private void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
+ synchronized (this) {
+ getUidStateTracker().updateAppWidgetVisibility(uidPackageNames, visible);
+ }
+ }
+
/**
* @return {@link PackageManagerInternal}
*/
@@ -1165,6 +3562,801 @@ public class AppOpsService extends IAppOpsService.Stub {
return mPackageManagerInternal;
}
+ /**
+ * Create a restriction description matching the properties of the package.
+ *
+ * @param pkg The package to create the restriction description for
+ *
+ * @return The restriction matching the package
+ */
+ private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
+ return new RestrictionBypass(pkg.getUid() == Process.SYSTEM_UID, pkg.isPrivileged(),
+ mContext.checkPermission(android.Manifest.permission
+ .EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
+ == PackageManager.PERMISSION_GRANTED);
+ }
+
+ /**
+ * @see #verifyAndGetBypass(int, String, String, String)
+ */
+ private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
+ @Nullable String attributionTag) {
+ return verifyAndGetBypass(uid, packageName, attributionTag, null);
+ }
+
+ /**
+ * Verify that package belongs to uid and return the {@link RestrictionBypass bypass
+ * description} for the package, along with a boolean indicating whether the attribution tag is
+ * valid.
+ *
+ * @param uid The uid the package belongs to
+ * @param packageName The package the might belong to the uid
+ * @param attributionTag attribution tag or {@code null} if no need to verify
+ * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
+ *
+ * @return PackageVerificationResult containing {@link RestrictionBypass} and whether the
+ * attribution tag is valid
+ */
+ private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
+ @Nullable String attributionTag, @Nullable String proxyPackageName) {
+ if (uid == Process.ROOT_UID) {
+ // For backwards compatibility, don't check package name for root UID.
+ return new PackageVerificationResult(null,
+ /* isAttributionTagValid */ true);
+ }
+ if (Process.isSdkSandboxUid(uid)) {
+ // SDK sandbox processes run in their own UID range, but their associated
+ // UID for checks should always be the UID of the package implementing SDK sandbox
+ // service.
+ // TODO: We will need to modify the callers of this function instead, so
+ // modifications and checks against the app ops state are done with the
+ // correct UID.
+ try {
+ final PackageManager pm = mContext.getPackageManager();
+ final String supplementalPackageName = pm.getSdkSandboxPackageName();
+ if (Objects.equals(packageName, supplementalPackageName)) {
+ uid = pm.getPackageUidAsUser(supplementalPackageName,
+ PackageManager.PackageInfoFlags.of(0), UserHandle.getUserId(uid));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't happen for the supplemental package
+ e.printStackTrace();
+ }
+ }
+
+
+ // Do not check if uid/packageName/attributionTag is already known.
+ synchronized (this) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState != null && uidState.pkgOps != null) {
+ Ops ops = uidState.pkgOps.get(packageName);
+
+ if (ops != null && (attributionTag == null || ops.knownAttributionTags.contains(
+ attributionTag)) && ops.bypass != null) {
+ return new PackageVerificationResult(ops.bypass,
+ ops.validAttributionTags.contains(attributionTag));
+ }
+ }
+ }
+
+ int callingUid = Binder.getCallingUid();
+
+ // Allow any attribution tag for resolvable uids
+ int pkgUid;
+ if (Objects.equals(packageName, "com.android.shell")) {
+ // Special case for the shell which is a package but should be able
+ // to bypass app attribution tag restrictions.
+ pkgUid = Process.SHELL_UID;
+ } else {
+ pkgUid = resolveUid(packageName);
+ }
+ if (pkgUid != Process.INVALID_UID) {
+ if (pkgUid != UserHandle.getAppId(uid)) {
+ Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
+ + "Package \"" + packageName + "\" does not belong to uid " + uid + ".");
+ String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
+ throw new SecurityException("Specified package \"" + packageName + "\" under uid "
+ + UserHandle.getAppId(uid) + otherUidMessage);
+ }
+ return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
+ /* isAttributionTagValid */ true);
+ }
+
+ int userId = UserHandle.getUserId(uid);
+ RestrictionBypass bypass = null;
+ boolean isAttributionTagValid = false;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ AndroidPackage pkg = pmInt.getPackage(packageName);
+ if (pkg != null) {
+ isAttributionTagValid = isAttributionInPackage(pkg, attributionTag);
+ pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
+ bypass = getBypassforPackage(pkg);
+ }
+ if (!isAttributionTagValid) {
+ AndroidPackage proxyPkg = proxyPackageName != null
+ ? pmInt.getPackage(proxyPackageName) : null;
+ // Re-check in proxy.
+ isAttributionTagValid = isAttributionInPackage(proxyPkg, attributionTag);
+ String msg;
+ if (pkg != null && isAttributionTagValid) {
+ msg = "attributionTag " + attributionTag + " declared in manifest of the proxy"
+ + " package " + proxyPackageName + ", this is not advised";
+ } else if (pkg != null) {
+ msg = "attributionTag " + attributionTag + " not declared in manifest of "
+ + packageName;
+ } else {
+ msg = "package " + packageName + " not found, can't check for "
+ + "attributionTag " + attributionTag;
+ }
+
+ try {
+ if (!mPlatformCompat.isChangeEnabledByPackageName(
+ SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
+ userId) || !mPlatformCompat.isChangeEnabledByUid(
+ SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE,
+ callingUid)) {
+ // Do not override tags if overriding is not enabled for this package
+ isAttributionTagValid = true;
+ }
+ Slog.e(TAG, msg);
+ } catch (RemoteException neverHappens) {
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (pkgUid != uid) {
+ Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
+ + "Package \"" + packageName + "\" does not belong to uid " + uid + ".");
+ String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
+ throw new SecurityException("Specified package \"" + packageName + "\" under uid " + uid
+ + otherUidMessage);
+ }
+
+ return new PackageVerificationResult(bypass, isAttributionTagValid);
+ }
+
+ private boolean isAttributionInPackage(@Nullable AndroidPackage pkg,
+ @Nullable String attributionTag) {
+ if (pkg == null) {
+ return false;
+ } else if (attributionTag == null) {
+ return true;
+ }
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int i = 0; i < numAttributions; i++) {
+ if (pkg.getAttributions().get(i).getTag().equals(attributionTag)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get (and potentially create) ops.
+ *
+ * @param uid The uid the package belongs to
+ * @param packageName The name of the package
+ * @param attributionTag attribution tag
+ * @param isAttributionTagValid whether the given attribution tag is valid
+ * @param bypass When to bypass certain op restrictions (can be null if edit == false)
+ * @param edit If an ops does not exist, create the ops?
+
+ * @return The ops
+ */
+ private Ops getOpsLocked(int uid, String packageName, @Nullable String attributionTag,
+ boolean isAttributionTagValid, @Nullable RestrictionBypass bypass, boolean edit) {
+ UidState uidState = getUidStateLocked(uid, edit);
+ if (uidState == null) {
+ return null;
+ }
+
+ if (uidState.pkgOps == null) {
+ if (!edit) {
+ return null;
+ }
+ uidState.pkgOps = new ArrayMap<>();
+ }
+
+ Ops ops = uidState.pkgOps.get(packageName);
+ if (ops == null) {
+ if (!edit) {
+ return null;
+ }
+ ops = new Ops(packageName, uidState);
+ uidState.pkgOps.put(packageName, ops);
+ }
+
+ if (edit) {
+ if (bypass != null) {
+ ops.bypass = bypass;
+ }
+
+ if (attributionTag != null) {
+ ops.knownAttributionTags.add(attributionTag);
+ if (isAttributionTagValid) {
+ ops.validAttributionTags.add(attributionTag);
+ } else {
+ ops.validAttributionTags.remove(attributionTag);
+ }
+ }
+ }
+
+ return ops;
+ }
+
+ @Override
+ public void scheduleWriteLocked() {
+ if (!mWriteScheduled) {
+ mWriteScheduled = true;
+ mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
+ }
+ }
+
+ @Override
+ public void scheduleFastWriteLocked() {
+ if (!mFastWriteScheduled) {
+ mWriteScheduled = true;
+ mFastWriteScheduled = true;
+ mHandler.removeCallbacks(mWriteRunner);
+ mHandler.postDelayed(mWriteRunner, 10*1000);
+ }
+ }
+
+ /**
+ * Get the state of an op for a uid.
+ *
+ * @param code The code of the op
+ * @param uid The uid the of the package
+ * @param packageName The package name for which to get the state for
+ * @param attributionTag The attribution tag
+ * @param isAttributionTagValid Whether the given attribution tag is valid
+ * @param bypass When to bypass certain op restrictions (can be null if edit == false)
+ * @param edit Iff {@code true} create the {@link Op} object if not yet created
+ *
+ * @return The {@link Op state} of the op
+ */
+ private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, boolean isAttributionTagValid,
+ @Nullable RestrictionBypass bypass, boolean edit) {
+ Ops ops = getOpsLocked(uid, packageName, attributionTag, isAttributionTagValid, bypass,
+ edit);
+ if (ops == null) {
+ return null;
+ }
+ return getOpLocked(ops, code, uid, edit);
+ }
+
+ private Op getOpLocked(Ops ops, int code, int uid, boolean edit) {
+ Op op = ops.get(code);
+ if (op == null) {
+ if (!edit) {
+ return null;
+ }
+ op = new Op(ops.uidState, ops.packageName, code, uid);
+ ops.put(code, op);
+ }
+ if (edit) {
+ scheduleWriteLocked();
+ }
+ return op;
+ }
+
+ private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) {
+ if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) {
+ return false;
+ }
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
+ }
+
+ private boolean isOpRestrictedLocked(int uid, int code, String packageName,
+ String attributionTag, @Nullable RestrictionBypass appBypass, boolean isCheckOp) {
+ int restrictionSetCount = mOpGlobalRestrictions.size();
+
+ for (int i = 0; i < restrictionSetCount; i++) {
+ ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i);
+ if (restrictionState.hasRestriction(code)) {
+ return true;
+ }
+ }
+
+ int userHandle = UserHandle.getUserId(uid);
+ restrictionSetCount = mOpUserRestrictions.size();
+
+ for (int i = 0; i < restrictionSetCount; i++) {
+ // For each client, check that the given op is not restricted, or that the given
+ // package is exempt from the restriction.
+ ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
+ if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle,
+ isCheckOp)) {
+ RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
+ if (opBypass != null) {
+ // If we are the system, bypass user restrictions for certain codes
+ synchronized (this) {
+ if (opBypass.isSystemUid && appBypass != null && appBypass.isSystemUid) {
+ return false;
+ }
+ if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
+ return false;
+ }
+ if (opBypass.isRecordAudioRestrictionExcept && appBypass != null
+ && appBypass.isRecordAudioRestrictionExcept) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void readState() {
+ synchronized (mFile) {
+ synchronized (this) {
+ FileInputStream stream;
+ try {
+ stream = mFile.openRead();
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
+ return;
+ }
+ boolean success = false;
+ mUidStates.clear();
+ mAppOpsServiceInterface.clearAllModes();
+ try {
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("no start tag found");
+ }
+
+ mVersionAtBoot = parser.getAttributeInt(null, "v", NO_VERSION);
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("pkg")) {
+ readPackage(parser);
+ } else if (tagName.equals("uid")) {
+ readUidOps(parser);
+ } else {
+ Slog.w(TAG, "Unknown element under <app-ops>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ success = true;
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } finally {
+ if (!success) {
+ mUidStates.clear();
+ mAppOpsServiceInterface.clearAllModes();
+ }
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ @GuardedBy("this")
+ void upgradeRunAnyInBackgroundLocked() {
+ for (int i = 0; i < mUidStates.size(); i++) {
+ final UidState uidState = mUidStates.valueAt(i);
+ if (uidState == null) {
+ continue;
+ }
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes != null) {
+ final int idx = opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
+ if (idx >= 0) {
+ uidState.setUidMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ opModes.valueAt(idx));
+ }
+ }
+ if (uidState.pkgOps == null) {
+ continue;
+ }
+ boolean changed = false;
+ for (int j = 0; j < uidState.pkgOps.size(); j++) {
+ Ops ops = uidState.pkgOps.valueAt(j);
+ if (ops != null) {
+ final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
+ 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.setMode(op.getMode());
+ ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ uidState.evalForegroundOps();
+ }
+ }
+ }
+
+ /**
+ * The interpretation of the default mode - MODE_DEFAULT - for OP_SCHEDULE_EXACT_ALARM is
+ * changing. Simultaneously, we want to change this op's mode from MODE_DEFAULT to MODE_ALLOWED
+ * for already installed apps. For newer apps, it will stay as MODE_DEFAULT.
+ */
+ @VisibleForTesting
+ @GuardedBy("this")
+ void upgradeScheduleExactAlarmLocked() {
+ final PermissionManagerServiceInternal pmsi = LocalServices.getService(
+ PermissionManagerServiceInternal.class);
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ final PackageManagerInternal pmi = getPackageManagerInternal();
+
+ final String[] packagesDeclaringPermission = pmsi.getAppOpPermissionPackages(
+ AppOpsManager.opToPermission(OP_SCHEDULE_EXACT_ALARM));
+ final int[] userIds = umi.getUserIds();
+
+ for (final String pkg : packagesDeclaringPermission) {
+ for (int userId : userIds) {
+ final int uid = pmi.getPackageUid(pkg, 0, userId);
+
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ uidState = new UidState(uid);
+ mUidStates.put(uid, uidState);
+ }
+ final int oldMode = uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM);
+ if (oldMode == AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM)) {
+ uidState.setUidMode(OP_SCHEDULE_EXACT_ALARM, MODE_ALLOWED);
+ }
+ }
+ // This appop is meant to be controlled at a uid level. So we leave package modes as
+ // they are.
+ }
+ }
+
+ private void upgradeLocked(int oldVersion) {
+ if (oldVersion >= CURRENT_VERSION) {
+ return;
+ }
+ Slog.d(TAG, "Upgrading app-ops xml from version " + oldVersion + " to " + CURRENT_VERSION);
+ switch (oldVersion) {
+ case NO_VERSION:
+ upgradeRunAnyInBackgroundLocked();
+ // fall through
+ case 1:
+ upgradeScheduleExactAlarmLocked();
+ // fall through
+ case 2:
+ // for future upgrades
+ }
+ scheduleFastWriteLocked();
+ }
+
+ private void readUidOps(TypedXmlPullParser parser) throws NumberFormatException,
+ XmlPullParserException, IOException {
+ final int uid = parser.getAttributeInt(null, "n");
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("op")) {
+ final int code = parser.getAttributeInt(null, "n");
+ final int mode = parser.getAttributeInt(null, "m");
+ setUidMode(code, uid, mode);
+ } else {
+ Slog.w(TAG, "Unknown element under <uid-ops>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ private void readPackage(TypedXmlPullParser parser)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ String pkgName = parser.getAttributeValue(null, "n");
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("uid")) {
+ readUid(parser, pkgName);
+ } else {
+ Slog.w(TAG, "Unknown element under <pkg>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ private void readUid(TypedXmlPullParser parser, String pkgName)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ int uid = parser.getAttributeInt(null, "n");
+ final UidState uidState = getUidStateLocked(uid, true);
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals("op")) {
+ readOp(parser, uidState, pkgName);
+ } else {
+ Slog.w(TAG, "Unknown element under <pkg>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ uidState.evalForegroundOps();
+ }
+
+ private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent,
+ @Nullable String attribution)
+ throws NumberFormatException, IOException, XmlPullParserException {
+ final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution);
+
+ final long key = parser.getAttributeLong(null, "n");
+ final int uidState = extractUidStateFromKey(key);
+ final int opFlags = extractFlagsFromKey(key);
+
+ final long accessTime = parser.getAttributeLong(null, "t", 0);
+ final long rejectTime = parser.getAttributeLong(null, "r", 0);
+ final long accessDuration = parser.getAttributeLong(null, "d", -1);
+ final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
+ final int proxyUid = parser.getAttributeInt(null, "pu", Process.INVALID_UID);
+ final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc");
+
+ if (accessTime > 0) {
+ attributedOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg,
+ proxyAttributionTag, uidState, opFlags);
+ }
+ if (rejectTime > 0) {
+ attributedOp.rejected(rejectTime, uidState, opFlags);
+ }
+ }
+
+ private void readOp(TypedXmlPullParser parser,
+ @NonNull UidState uidState, @NonNull String pkgName)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ int opCode = parser.getAttributeInt(null, "n");
+ Op op = new Op(uidState, pkgName, opCode, uidState.uid);
+
+ final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
+ op.setMode(mode);
+
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals("st")) {
+ readAttributionOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
+ } else {
+ Slog.w(TAG, "Unknown element under <op>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ if (uidState.pkgOps == null) {
+ uidState.pkgOps = new ArrayMap<>();
+ }
+ Ops ops = uidState.pkgOps.get(pkgName);
+ if (ops == null) {
+ ops = new Ops(pkgName, uidState);
+ uidState.pkgOps.put(pkgName, ops);
+ }
+ ops.put(op.op, op);
+ }
+
+ void writeState() {
+ synchronized (mFile) {
+ FileOutputStream stream;
+ try {
+ stream = mFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state: " + e);
+ return;
+ }
+
+ List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
+
+ try {
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
+ out.startDocument(null, true);
+ out.startTag(null, "app-ops");
+ out.attributeInt(null, "v", CURRENT_VERSION);
+
+ SparseArray<SparseIntArray> uidStatesClone;
+ synchronized (this) {
+ uidStatesClone = new SparseArray<>(mUidStates.size());
+
+ final int uidStateCount = mUidStates.size();
+ for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
+ UidState uidState = mUidStates.valueAt(uidStateNum);
+ int uid = mUidStates.keyAt(uidStateNum);
+
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes != null && opModes.size() > 0) {
+ uidStatesClone.put(uid, opModes);
+ }
+ }
+ }
+
+ final int uidStateCount = uidStatesClone.size();
+ for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
+ SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum);
+ if (opModes != null && opModes.size() > 0) {
+ out.startTag(null, "uid");
+ out.attributeInt(null, "n", uidStatesClone.keyAt(uidStateNum));
+ final int opCount = opModes.size();
+ for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
+ final int op = opModes.keyAt(opCountNum);
+ final int mode = opModes.valueAt(opCountNum);
+ out.startTag(null, "op");
+ out.attributeInt(null, "n", op);
+ out.attributeInt(null, "m", mode);
+ out.endTag(null, "op");
+ }
+ out.endTag(null, "uid");
+ }
+ }
+
+ if (allOps != null) {
+ String lastPkg = null;
+ for (int i=0; i<allOps.size(); i++) {
+ AppOpsManager.PackageOps pkg = allOps.get(i);
+ if (!Objects.equals(pkg.getPackageName(), lastPkg)) {
+ if (lastPkg != null) {
+ out.endTag(null, "pkg");
+ }
+ lastPkg = pkg.getPackageName();
+ if (lastPkg != null) {
+ out.startTag(null, "pkg");
+ out.attribute(null, "n", lastPkg);
+ }
+ }
+ out.startTag(null, "uid");
+ out.attributeInt(null, "n", pkg.getUid());
+ List<AppOpsManager.OpEntry> ops = pkg.getOps();
+ for (int j=0; j<ops.size(); j++) {
+ AppOpsManager.OpEntry op = ops.get(j);
+ out.startTag(null, "op");
+ out.attributeInt(null, "n", op.getOp());
+ if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
+ out.attributeInt(null, "m", op.getMode());
+ }
+
+ for (String attributionTag : op.getAttributedOpEntries().keySet()) {
+ final AttributedOpEntry attribution =
+ op.getAttributedOpEntries().get(attributionTag);
+
+ final ArraySet<Long> keys = attribution.collectKeys();
+
+ final int keyCount = keys.size();
+ for (int k = 0; k < keyCount; k++) {
+ final long key = keys.valueAt(k);
+
+ final int uidState = AppOpsManager.extractUidStateFromKey(key);
+ final int flags = AppOpsManager.extractFlagsFromKey(key);
+
+ final long accessTime = attribution.getLastAccessTime(uidState,
+ uidState, flags);
+ final long rejectTime = attribution.getLastRejectTime(uidState,
+ uidState, flags);
+ final long accessDuration = attribution.getLastDuration(
+ uidState, uidState, flags);
+ // Proxy information for rejections is not backed up
+ final OpEventProxyInfo proxy = attribution.getLastProxyInfo(
+ uidState, uidState, flags);
+
+ if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
+ && proxy == null) {
+ continue;
+ }
+
+ String proxyPkg = null;
+ String proxyAttributionTag = null;
+ int proxyUid = Process.INVALID_UID;
+ if (proxy != null) {
+ proxyPkg = proxy.getPackageName();
+ proxyAttributionTag = proxy.getAttributionTag();
+ proxyUid = proxy.getUid();
+ }
+
+ out.startTag(null, "st");
+ if (attributionTag != null) {
+ out.attribute(null, "id", attributionTag);
+ }
+ out.attributeLong(null, "n", key);
+ if (accessTime > 0) {
+ out.attributeLong(null, "t", accessTime);
+ }
+ if (rejectTime > 0) {
+ out.attributeLong(null, "r", rejectTime);
+ }
+ if (accessDuration > 0) {
+ out.attributeLong(null, "d", accessDuration);
+ }
+ if (proxyPkg != null) {
+ out.attribute(null, "pp", proxyPkg);
+ }
+ if (proxyAttributionTag != null) {
+ out.attribute(null, "pc", proxyAttributionTag);
+ }
+ if (proxyUid >= 0) {
+ out.attributeInt(null, "pu", proxyUid);
+ }
+ out.endTag(null, "st");
+ }
+ }
+
+ out.endTag(null, "op");
+ }
+ out.endTag(null, "uid");
+ }
+ if (lastPkg != null) {
+ out.endTag(null, "pkg");
+ }
+ }
+
+ out.endTag(null, "app-ops");
+ out.endDocument();
+ mFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state, restoring backup.", e);
+ mFile.failWrite(stream);
+ }
+ }
+ mHistoricalRegistry.writeAndClearDiscreteHistory();
+ }
+
static class Shell extends ShellCommand {
final IAppOpsService mInterface;
final AppOpsService mInternal;
@@ -1178,6 +4370,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int mode;
int packageUid;
int nonpackageUid;
+ final static Binder sBinder = new Binder();
IBinder mToken;
boolean targetsUid;
@@ -1198,7 +4391,7 @@ public class AppOpsService extends IAppOpsService.Stub {
dumpCommandHelp(pw);
}
- static int strOpToOp(String op, PrintWriter err) {
+ static private int strOpToOp(String op, PrintWriter err) {
try {
return AppOpsManager.strOpToOp(op);
} catch (IllegalArgumentException e) {
@@ -1395,24 +4588,6 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.println(" not specified, the current user is assumed.");
}
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mAppOpsService.dump(fd, pw, args);
-
- pw.println();
- if (mCheckOpsDelegateDispatcher.mPolicy != null
- && mCheckOpsDelegateDispatcher.mPolicy instanceof AppOpsPolicy) {
- AppOpsPolicy policy = (AppOpsPolicy) mCheckOpsDelegateDispatcher.mPolicy;
- policy.dumpTags(pw);
- } else {
- pw.println(" AppOps policy not set.");
- }
-
- if (mAudioRestrictionManager.hasActiveRestrictions()) {
- pw.println();
- mAudioRestrictionManager.dump(pw);
- }
- }
static int onShellCommand(Shell shell, String cmd) {
if (cmd == null) {
return shell.handleDefaultCommands(cmd);
@@ -1616,12 +4791,14 @@ public class AppOpsService extends IAppOpsService.Stub {
return 0;
}
case "write-settings": {
- shell.mInternal.mAppOpsService
- .enforceManageAppOpsModes(Binder.getCallingPid(),
+ shell.mInternal.enforceManageAppOpsModes(Binder.getCallingPid(),
Binder.getCallingUid(), -1);
final long token = Binder.clearCallingIdentity();
try {
- shell.mInternal.mAppOpsService.writeState();
+ synchronized (shell.mInternal) {
+ shell.mInternal.mHandler.removeCallbacks(shell.mInternal.mWriteRunner);
+ }
+ shell.mInternal.writeState();
pw.println("Current settings written.");
} finally {
Binder.restoreCallingIdentity(token);
@@ -1629,12 +4806,11 @@ public class AppOpsService extends IAppOpsService.Stub {
return 0;
}
case "read-settings": {
- shell.mInternal.mAppOpsService
- .enforceManageAppOpsModes(Binder.getCallingPid(),
- Binder.getCallingUid(), -1);
+ shell.mInternal.enforceManageAppOpsModes(Binder.getCallingPid(),
+ Binder.getCallingUid(), -1);
final long token = Binder.clearCallingIdentity();
try {
- shell.mInternal.mAppOpsService.readState();
+ shell.mInternal.readState();
pw.println("Last settings read.");
} finally {
Binder.restoreCallingIdentity(token);
@@ -1680,70 +4856,877 @@ public class AppOpsService extends IAppOpsService.Stub {
return -1;
}
+ private void dumpHelp(PrintWriter pw) {
+ pw.println("AppOps service (appops) dump options:");
+ pw.println(" -h");
+ pw.println(" Print this help text.");
+ pw.println(" --op [OP]");
+ pw.println(" Limit output to data associated with the given app op code.");
+ pw.println(" --mode [MODE]");
+ pw.println(" Limit output to data associated with the given app op mode.");
+ pw.println(" --package [PACKAGE]");
+ pw.println(" Limit output to data associated with the given package name.");
+ pw.println(" --attributionTag [attributionTag]");
+ pw.println(" Limit output to data associated with the given attribution tag.");
+ pw.println(" --include-discrete [n]");
+ pw.println(" Include discrete ops limited to n per dimension. Use zero for no limit.");
+ pw.println(" --watchers");
+ pw.println(" Only output the watcher sections.");
+ pw.println(" --history");
+ pw.println(" Only output history.");
+ pw.println(" --uid-state-changes");
+ pw.println(" Include logs about uid state changes.");
+ }
+
+ private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
+ @HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now,
+ @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
+ final int numAttributions = op.mAttributions.size();
+ for (int i = 0; i < numAttributions; i++) {
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(
+ op.mAttributions.keyAt(i), filterAttributionTag)) {
+ continue;
+ }
+
+ pw.print(prefix + op.mAttributions.keyAt(i) + "=[\n");
+ dumpStatesLocked(pw, nowElapsed, op, op.mAttributions.keyAt(i), now, sdf, date,
+ prefix + " ");
+ pw.print(prefix + "]\n");
+ }
+ }
+
+ private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
+ @Nullable String attributionTag, long now, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix) {
+
+ final AttributedOpEntry entry = op.createSingleAttributionEntryLocked(
+ attributionTag).getAttributedOpEntries().get(attributionTag);
+
+ final ArraySet<Long> keys = entry.collectKeys();
+
+ final int keyCount = keys.size();
+ for (int k = 0; k < keyCount; k++) {
+ final long key = keys.valueAt(k);
+
+ final int uidState = AppOpsManager.extractUidStateFromKey(key);
+ final int flags = AppOpsManager.extractFlagsFromKey(key);
+
+ final long accessTime = entry.getLastAccessTime(uidState, uidState, flags);
+ final long rejectTime = entry.getLastRejectTime(uidState, uidState, flags);
+ final long accessDuration = entry.getLastDuration(uidState, uidState, flags);
+ final OpEventProxyInfo proxy = entry.getLastProxyInfo(uidState, uidState, flags);
+
+ String proxyPkg = null;
+ String proxyAttributionTag = null;
+ int proxyUid = Process.INVALID_UID;
+ if (proxy != null) {
+ proxyPkg = proxy.getPackageName();
+ proxyAttributionTag = proxy.getAttributionTag();
+ proxyUid = proxy.getUid();
+ }
+
+ if (accessTime > 0) {
+ pw.print(prefix);
+ pw.print("Access: ");
+ pw.print(AppOpsManager.keyToString(key));
+ pw.print(" ");
+ date.setTime(accessTime);
+ pw.print(sdf.format(date));
+ pw.print(" (");
+ TimeUtils.formatDuration(accessTime - now, pw);
+ pw.print(")");
+ if (accessDuration > 0) {
+ pw.print(" duration=");
+ TimeUtils.formatDuration(accessDuration, pw);
+ }
+ if (proxyUid >= 0) {
+ pw.print(" proxy[");
+ pw.print("uid=");
+ pw.print(proxyUid);
+ pw.print(", pkg=");
+ pw.print(proxyPkg);
+ pw.print(", attributionTag=");
+ pw.print(proxyAttributionTag);
+ pw.print("]");
+ }
+ pw.println();
+ }
+
+ if (rejectTime > 0) {
+ pw.print(prefix);
+ pw.print("Reject: ");
+ pw.print(AppOpsManager.keyToString(key));
+ date.setTime(rejectTime);
+ pw.print(sdf.format(date));
+ pw.print(" (");
+ TimeUtils.formatDuration(rejectTime - now, pw);
+ pw.print(")");
+ if (proxyUid >= 0) {
+ pw.print(" proxy[");
+ pw.print("uid=");
+ pw.print(proxyUid);
+ pw.print(", pkg=");
+ pw.print(proxyPkg);
+ pw.print(", attributionTag=");
+ pw.print(proxyAttributionTag);
+ pw.print("]");
+ }
+ pw.println();
+ }
+ }
+
+ final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
+ if (attributedOp.isRunning()) {
+ long earliestElapsedTime = Long.MAX_VALUE;
+ long maxNumStarts = 0;
+ int numInProgressEvents = attributedOp.mInProgressEvents.size();
+ for (int i = 0; i < numInProgressEvents; i++) {
+ AttributedOp.InProgressStartOpEvent event =
+ attributedOp.mInProgressEvents.valueAt(i);
+
+ earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
+ maxNumStarts = Math.max(maxNumStarts, event.mNumUnfinishedStarts);
+ }
+
+ pw.print(prefix + "Running start at: ");
+ TimeUtils.formatDuration(nowElapsed - earliestElapsedTime, pw);
+ pw.println();
+
+ if (maxNumStarts > 1) {
+ pw.print(prefix + "startNesting=");
+ pw.println(maxNumStarts);
+ }
+ }
+ }
+
+ @NeverCompile // Avoid size overhead of debugging code.
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+
+ int dumpOp = OP_NONE;
+ String dumpPackage = null;
+ String dumpAttributionTag = null;
+ int dumpUid = Process.INVALID_UID;
+ int dumpMode = -1;
+ boolean dumpWatchers = false;
+ // TODO ntmyren: Remove the dumpHistory and dumpFilter
+ boolean dumpHistory = false;
+ boolean includeDiscreteOps = false;
+ boolean dumpUidStateChangeLogs = false;
+ int nDiscreteOps = 10;
+ @HistoricalOpsRequestFilter int dumpFilter = 0;
+ boolean dumpAll = false;
+
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if ("-h".equals(arg)) {
+ dumpHelp(pw);
+ return;
+ } else if ("-a".equals(arg)) {
+ // dump all data
+ dumpAll = true;
+ } else if ("--op".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --op option");
+ return;
+ }
+ dumpOp = Shell.strOpToOp(args[i], pw);
+ dumpFilter |= FILTER_BY_OP_NAMES;
+ if (dumpOp < 0) {
+ return;
+ }
+ } else if ("--package".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --package option");
+ return;
+ }
+ dumpPackage = args[i];
+ dumpFilter |= FILTER_BY_PACKAGE_NAME;
+ try {
+ dumpUid = AppGlobals.getPackageManager().getPackageUid(dumpPackage,
+ PackageManager.MATCH_KNOWN_PACKAGES | PackageManager.MATCH_INSTANT,
+ 0);
+ } catch (RemoteException e) {
+ }
+ if (dumpUid < 0) {
+ pw.println("Unknown package: " + dumpPackage);
+ return;
+ }
+ dumpUid = UserHandle.getAppId(dumpUid);
+ dumpFilter |= FILTER_BY_UID;
+ } else if ("--attributionTag".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --attributionTag option");
+ return;
+ }
+ dumpAttributionTag = args[i];
+ dumpFilter |= FILTER_BY_ATTRIBUTION_TAG;
+ } else if ("--mode".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --mode option");
+ return;
+ }
+ dumpMode = Shell.strModeToMode(args[i], pw);
+ if (dumpMode < 0) {
+ return;
+ }
+ } else if ("--watchers".equals(arg)) {
+ dumpWatchers = true;
+ } else if ("--include-discrete".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --include-discrete option");
+ return;
+ }
+ try {
+ nDiscreteOps = Integer.valueOf(args[i]);
+ } catch (NumberFormatException e) {
+ pw.println("Wrong parameter: " + args[i]);
+ return;
+ }
+ includeDiscreteOps = true;
+ } else if ("--history".equals(arg)) {
+ dumpHistory = true;
+ } else if (arg.length() > 0 && arg.charAt(0) == '-') {
+ pw.println("Unknown option: " + arg);
+ return;
+ } else if ("--uid-state-changes".equals(arg)) {
+ dumpUidStateChangeLogs = true;
+ } else {
+ pw.println("Unknown command: " + arg);
+ return;
+ }
+ }
+ }
+
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ final Date date = new Date();
+ synchronized (this) {
+ pw.println("Current AppOps Service state:");
+ if (!dumpHistory && !dumpWatchers) {
+ mConstants.dump(pw);
+ }
+ pw.println();
+ final long now = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowUptime = SystemClock.uptimeMillis();
+ boolean needSep = false;
+ if (dumpFilter == 0 && dumpMode < 0 && mProfileOwners != null && !dumpWatchers
+ && !dumpHistory) {
+ pw.println(" Profile owners:");
+ for (int poi = 0; poi < mProfileOwners.size(); poi++) {
+ pw.print(" User #");
+ pw.print(mProfileOwners.keyAt(poi));
+ pw.print(": ");
+ UserHandle.formatUid(pw, mProfileOwners.valueAt(poi));
+ pw.println();
+ }
+ pw.println();
+ }
+
+ if (!dumpHistory) {
+ needSep |= mAppOpsServiceInterface.dumpListeners(dumpOp, dumpUid, dumpPackage, pw);
+ }
+
+ if (mModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) {
+ boolean printedHeader = false;
+ for (int i = 0; i < mModeWatchers.size(); i++) {
+ final ModeCallback cb = mModeWatchers.valueAt(i);
+ if (dumpPackage != null
+ && dumpUid != UserHandle.getAppId(cb.getWatchingUid())) {
+ continue;
+ }
+ needSep = true;
+ if (!printedHeader) {
+ pw.println(" All op mode watchers:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(mModeWatchers.keyAt(i))));
+ pw.print(": "); pw.println(cb);
+ }
+ }
+ if (mActiveWatchers.size() > 0 && dumpMode < 0) {
+ needSep = true;
+ boolean printedHeader = false;
+ for (int watcherNum = 0; watcherNum < mActiveWatchers.size(); watcherNum++) {
+ final SparseArray<ActiveCallback> activeWatchers =
+ mActiveWatchers.valueAt(watcherNum);
+ if (activeWatchers.size() <= 0) {
+ continue;
+ }
+ final ActiveCallback cb = activeWatchers.valueAt(0);
+ if (dumpOp >= 0 && activeWatchers.indexOfKey(dumpOp) < 0) {
+ continue;
+ }
+ if (dumpPackage != null
+ && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" All op active watchers:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(
+ mActiveWatchers.keyAt(watcherNum))));
+ pw.println(" ->");
+ pw.print(" [");
+ final int opCount = activeWatchers.size();
+ for (int opNum = 0; opNum < opCount; opNum++) {
+ if (opNum > 0) {
+ pw.print(' ');
+ }
+ pw.print(AppOpsManager.opToName(activeWatchers.keyAt(opNum)));
+ if (opNum < opCount - 1) {
+ pw.print(',');
+ }
+ }
+ pw.println("]");
+ pw.print(" ");
+ pw.println(cb);
+ }
+ }
+ if (mStartedWatchers.size() > 0 && dumpMode < 0) {
+ needSep = true;
+ boolean printedHeader = false;
+
+ final int watchersSize = mStartedWatchers.size();
+ for (int watcherNum = 0; watcherNum < watchersSize; watcherNum++) {
+ final SparseArray<StartedCallback> startedWatchers =
+ mStartedWatchers.valueAt(watcherNum);
+ if (startedWatchers.size() <= 0) {
+ continue;
+ }
+
+ final StartedCallback cb = startedWatchers.valueAt(0);
+ if (dumpOp >= 0 && startedWatchers.indexOfKey(dumpOp) < 0) {
+ continue;
+ }
+
+ if (dumpPackage != null
+ && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+ continue;
+ }
+
+ if (!printedHeader) {
+ pw.println(" All op started watchers:");
+ printedHeader = true;
+ }
+
+ pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(
+ mStartedWatchers.keyAt(watcherNum))));
+ pw.println(" ->");
+
+ pw.print(" [");
+ final int opCount = startedWatchers.size();
+ for (int opNum = 0; opNum < opCount; opNum++) {
+ if (opNum > 0) {
+ pw.print(' ');
+ }
+
+ pw.print(AppOpsManager.opToName(startedWatchers.keyAt(opNum)));
+ if (opNum < opCount - 1) {
+ pw.print(',');
+ }
+ }
+ pw.println("]");
+
+ pw.print(" ");
+ pw.println(cb);
+ }
+ }
+ if (mNotedWatchers.size() > 0 && dumpMode < 0) {
+ needSep = true;
+ boolean printedHeader = false;
+ for (int watcherNum = 0; watcherNum < mNotedWatchers.size(); watcherNum++) {
+ final SparseArray<NotedCallback> notedWatchers =
+ mNotedWatchers.valueAt(watcherNum);
+ if (notedWatchers.size() <= 0) {
+ continue;
+ }
+ final NotedCallback cb = notedWatchers.valueAt(0);
+ if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
+ continue;
+ }
+ if (dumpPackage != null
+ && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" All op noted watchers:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(
+ mNotedWatchers.keyAt(watcherNum))));
+ pw.println(" ->");
+ pw.print(" [");
+ final int opCount = notedWatchers.size();
+ for (int opNum = 0; opNum < opCount; opNum++) {
+ if (opNum > 0) {
+ pw.print(' ');
+ }
+ pw.print(AppOpsManager.opToName(notedWatchers.keyAt(opNum)));
+ if (opNum < opCount - 1) {
+ pw.print(',');
+ }
+ }
+ pw.println("]");
+ pw.print(" ");
+ pw.println(cb);
+ }
+ }
+ if (mAudioRestrictionManager.hasActiveRestrictions() && dumpOp < 0
+ && dumpPackage != null && dumpMode < 0 && !dumpWatchers) {
+ needSep = mAudioRestrictionManager.dump(pw) || needSep;
+ }
+ if (needSep) {
+ pw.println();
+ }
+ for (int i=0; i<mUidStates.size(); i++) {
+ UidState uidState = mUidStates.valueAt(i);
+ 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 || (opModes != null
+ && opModes.indexOfKey(dumpOp) >= 0);
+ boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i);
+ boolean hasMode = dumpMode < 0;
+ if (!hasMode && opModes != null) {
+ for (int opi = 0; !hasMode && opi < opModes.size(); opi++) {
+ if (opModes.valueAt(opi) == dumpMode) {
+ hasMode = true;
+ }
+ }
+ }
+ if (pkgOps != null) {
+ for (int pkgi = 0;
+ (!hasOp || !hasPackage || !hasMode) && pkgi < pkgOps.size();
+ pkgi++) {
+ Ops ops = pkgOps.valueAt(pkgi);
+ if (!hasOp && ops != null && ops.indexOfKey(dumpOp) >= 0) {
+ hasOp = true;
+ }
+ if (!hasMode) {
+ for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
+ if (ops.valueAt(opi).getMode() == dumpMode) {
+ hasMode = true;
+ }
+ }
+ }
+ if (!hasPackage && dumpPackage.equals(ops.packageName)) {
+ hasPackage = true;
+ }
+ }
+ }
+ if (uidState.foregroundOps != null && !hasOp) {
+ if (uidState.foregroundOps.indexOfKey(dumpOp) > 0) {
+ hasOp = true;
+ }
+ }
+ if (!hasOp || !hasPackage || !hasMode) {
+ continue;
+ }
+ }
+
+ pw.print(" Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
+ uidState.dump(pw, nowElapsed);
+ if (uidState.foregroundOps != null && (dumpMode < 0
+ || dumpMode == AppOpsManager.MODE_FOREGROUND)) {
+ pw.println(" foregroundOps:");
+ for (int j = 0; j < uidState.foregroundOps.size(); j++) {
+ if (dumpOp >= 0 && dumpOp != uidState.foregroundOps.keyAt(j)) {
+ continue;
+ }
+ pw.print(" ");
+ pw.print(AppOpsManager.opToName(uidState.foregroundOps.keyAt(j)));
+ pw.print(": ");
+ pw.println(uidState.foregroundOps.valueAt(j) ? "WATCHER" : "SILENT");
+ }
+ pw.print(" hasForegroundWatchers=");
+ pw.println(uidState.hasForegroundWatchers);
+ }
+ needSep = true;
+
+ if (opModes != null) {
+ final int opModeCount = opModes.size();
+ for (int j = 0; j < opModeCount; j++) {
+ final int code = opModes.keyAt(j);
+ final int mode = opModes.valueAt(j);
+ if (dumpOp >= 0 && dumpOp != code) {
+ continue;
+ }
+ if (dumpMode >= 0 && dumpMode != mode) {
+ continue;
+ }
+ pw.print(" "); pw.print(AppOpsManager.opToName(code));
+ pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode));
+ }
+ }
+
+ if (pkgOps == null) {
+ continue;
+ }
+
+ for (int pkgi = 0; pkgi < pkgOps.size(); pkgi++) {
+ final Ops ops = pkgOps.valueAt(pkgi);
+ if (dumpPackage != null && !dumpPackage.equals(ops.packageName)) {
+ continue;
+ }
+ boolean printedPackage = false;
+ for (int j=0; j<ops.size(); j++) {
+ final Op op = ops.valueAt(j);
+ final int opCode = op.op;
+ if (dumpOp >= 0 && dumpOp != opCode) {
+ continue;
+ }
+ if (dumpMode >= 0 && dumpMode != op.getMode()) {
+ continue;
+ }
+ if (!printedPackage) {
+ pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
+ printedPackage = true;
+ }
+ pw.print(" "); pw.print(AppOpsManager.opToName(opCode));
+ 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
+ ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
+ pw.print("="); pw.print(AppOpsManager.modeToName(mode));
+ }
+ pw.println("): ");
+ dumpStatesLocked(pw, dumpAttributionTag, dumpFilter, nowElapsed, op, now,
+ sdf, date, " ");
+ }
+ }
+ }
+ if (needSep) {
+ pw.println();
+ }
+
+ boolean showUserRestrictions = !(dumpMode < 0 && !dumpWatchers && !dumpHistory);
+ mAppOpsRestrictions.dumpRestrictions(pw, dumpOp, dumpPackage, showUserRestrictions);
+
+ if (!dumpHistory && !dumpWatchers) {
+ pw.println();
+ if (mCheckOpsDelegateDispatcher.mPolicy != null
+ && mCheckOpsDelegateDispatcher.mPolicy instanceof AppOpsPolicy) {
+ AppOpsPolicy policy = (AppOpsPolicy) mCheckOpsDelegateDispatcher.mPolicy;
+ policy.dumpTags(pw);
+ } else {
+ pw.println(" AppOps policy not set.");
+ }
+ }
+
+ if (dumpAll || dumpUidStateChangeLogs) {
+ pw.println();
+ pw.println("Uid State Changes Event Log:");
+ getUidStateTracker().dumpEvents(pw);
+ }
+ }
+
+ // Must not hold the appops lock
+ if (dumpHistory && !dumpWatchers) {
+ mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp,
+ dumpFilter);
+ }
+ if (includeDiscreteOps) {
+ pw.println("Discrete accesses: ");
+ mHistoricalRegistry.dumpDiscreteData(pw, dumpUid, dumpPackage, dumpAttributionTag,
+ dumpFilter, dumpOp, sdf, date, " ", nDiscreteOps);
+ }
+ }
+
@Override
public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
- mAppOpsService.setUserRestrictions(restrictions, token, userHandle);
+ checkSystemUid("setUserRestrictions");
+ Objects.requireNonNull(restrictions);
+ Objects.requireNonNull(token);
+ for (int i = 0; i < AppOpsManager._NUM_OP; i++) {
+ String restriction = AppOpsManager.opToRestriction(i);
+ if (restriction != null) {
+ setUserRestrictionNoCheck(i, restrictions.getBoolean(restriction, false), token,
+ userHandle, null);
+ }
+ }
}
@Override
public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
PackageTagsList excludedPackageTags) {
- mAppOpsService.setUserRestriction(code, restricted, token, userHandle,
- excludedPackageTags);
+ if (Binder.getCallingPid() != Process.myPid()) {
+ mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ }
+ if (userHandle != UserHandle.getCallingUserId()) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission
+ .INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(Manifest.permission
+ .INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need INTERACT_ACROSS_USERS_FULL or"
+ + " INTERACT_ACROSS_USERS to interact cross user ");
+ }
+ }
+ verifyIncomingOp(code);
+ Objects.requireNonNull(token);
+ setUserRestrictionNoCheck(code, restricted, token, userHandle, excludedPackageTags);
+ }
+
+ private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
+ int userHandle, PackageTagsList excludedPackageTags) {
+ synchronized (AppOpsService.this) {
+ ClientUserRestrictionState restrictionState = mOpUserRestrictions.get(token);
+
+ if (restrictionState == null) {
+ try {
+ restrictionState = new ClientUserRestrictionState(token);
+ } catch (RemoteException e) {
+ return;
+ }
+ mOpUserRestrictions.put(token, restrictionState);
+ }
+
+ if (restrictionState.setRestriction(code, restricted, excludedPackageTags,
+ userHandle)) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyWatchersOfChange, this, code, UID_ANY));
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::updateStartedOpModeForUser, this, code, restricted,
+ userHandle));
+ }
+
+ if (restrictionState.isDefault()) {
+ mOpUserRestrictions.remove(token);
+ restrictionState.destroy();
+ }
+ }
+ }
+
+ private void updateStartedOpModeForUser(int code, boolean restricted, int userId) {
+ synchronized (AppOpsService.this) {
+ int numUids = mUidStates.size();
+ for (int uidNum = 0; uidNum < numUids; uidNum++) {
+ int uid = mUidStates.keyAt(uidNum);
+ if (userId != UserHandle.USER_ALL && UserHandle.getUserId(uid) != userId) {
+ continue;
+ }
+ updateStartedOpModeForUidLocked(code, restricted, uid);
+ }
+ }
+ }
+
+ private void updateStartedOpModeForUidLocked(int code, boolean restricted, int uid) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+
+ int numPkgOps = uidState.pkgOps.size();
+ 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.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
+ continue;
+ }
+ int numAttrTags = op.mAttributions.size();
+ for (int attrNum = 0; attrNum < numAttrTags; attrNum++) {
+ AttributedOp attrOp = op.mAttributions.valueAt(attrNum);
+ if (restricted && attrOp.isRunning()) {
+ attrOp.pause();
+ } else if (attrOp.isPaused()) {
+ attrOp.resume();
+ }
+ }
+ }
+ }
+
+ private void notifyWatchersOfChange(int code, int uid) {
+ final ArraySet<OnOpModeChangedListener> modeChangedListenerSet;
+ synchronized (this) {
+ modeChangedListenerSet = mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (modeChangedListenerSet == null) {
+ return;
+ }
+ }
+
+ notifyOpChanged(modeChangedListenerSet, code, uid, null);
}
@Override
public void removeUser(int userHandle) throws RemoteException {
- mAppOpsService.removeUser(userHandle);
+ checkSystemUid("removeUser");
+ synchronized (AppOpsService.this) {
+ final int tokenCount = mOpUserRestrictions.size();
+ for (int i = tokenCount - 1; i >= 0; i--) {
+ ClientUserRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
+ opRestrictions.removeUser(userHandle);
+ }
+ removeUidsForUserLocked(userHandle);
+ }
}
@Override
public boolean isOperationActive(int code, int uid, String packageName) {
- return mAppOpsService.isOperationActive(code, uid, packageName);
+ if (Binder.getCallingUid() != uid) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ verifyIncomingOp(code);
+ if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
+ return false;
+ }
+
+ final String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return false;
+ }
+ // TODO moltmann: Allow to check for attribution op activeness
+ synchronized (AppOpsService.this) {
+ Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, false, null, false);
+ if (pkgOps == null) {
+ return false;
+ }
+
+ Op op = pkgOps.get(code);
+ if (op == null) {
+ return false;
+ }
+
+ return op.isRunning();
+ }
}
@Override
public boolean isProxying(int op, @NonNull String proxyPackageName,
@NonNull String proxyAttributionTag, int proxiedUid,
@NonNull String proxiedPackageName) {
- return mAppOpsService.isProxying(op, proxyPackageName, proxyAttributionTag,
- proxiedUid, proxiedPackageName);
+ Objects.requireNonNull(proxyPackageName);
+ Objects.requireNonNull(proxiedPackageName);
+ final long callingUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final List<AppOpsManager.PackageOps> packageOps = getOpsForPackage(proxiedUid,
+ proxiedPackageName, new int[] {op});
+ if (packageOps == null || packageOps.isEmpty()) {
+ return false;
+ }
+ final List<OpEntry> opEntries = packageOps.get(0).getOps();
+ if (opEntries.isEmpty()) {
+ return false;
+ }
+ final OpEntry opEntry = opEntries.get(0);
+ if (!opEntry.isRunning()) {
+ return false;
+ }
+ final OpEventProxyInfo proxyInfo = opEntry.getLastProxyInfo(
+ OP_FLAG_TRUSTED_PROXIED | AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED);
+ return proxyInfo != null && callingUid == proxyInfo.getUid()
+ && proxyPackageName.equals(proxyInfo.getPackageName())
+ && Objects.equals(proxyAttributionTag, proxyInfo.getAttributionTag());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
public void resetPackageOpsNoHistory(@NonNull String packageName) {
- mAppOpsService.resetPackageOpsNoHistory(packageName);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "resetPackageOpsNoHistory");
+ synchronized (AppOpsService.this) {
+ final int uid = mPackageManagerInternal.getPackageUid(packageName, 0,
+ UserHandle.getCallingUserId());
+ if (uid == Process.INVALID_UID) {
+ return;
+ }
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+ Ops removedOps = uidState.pkgOps.remove(packageName);
+ mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
+ if (removedOps != null) {
+ scheduleFastWriteLocked();
+ }
+ }
}
@Override
public void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
long baseSnapshotInterval, int compressionStep) {
- mAppOpsService.setHistoryParameters(mode, baseSnapshotInterval, compressionStep);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "setHistoryParameters");
+ // Must not hold the appops lock
+ mHistoricalRegistry.setHistoryParameters(mode, baseSnapshotInterval, compressionStep);
}
@Override
public void offsetHistory(long offsetMillis) {
- mAppOpsService.offsetHistory(offsetMillis);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "offsetHistory");
+ // Must not hold the appops lock
+ mHistoricalRegistry.offsetHistory(offsetMillis);
+ mHistoricalRegistry.offsetDiscreteHistory(offsetMillis);
}
@Override
public void addHistoricalOps(HistoricalOps ops) {
- mAppOpsService.addHistoricalOps(ops);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "addHistoricalOps");
+ // Must not hold the appops lock
+ mHistoricalRegistry.addHistoricalOps(ops);
}
@Override
public void resetHistoryParameters() {
- mAppOpsService.resetHistoryParameters();
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "resetHistoryParameters");
+ // Must not hold the appops lock
+ mHistoricalRegistry.resetHistoryParameters();
}
@Override
public void clearHistory() {
- mAppOpsService.clearHistory();
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "clearHistory");
+ // Must not hold the appops lock
+ mHistoricalRegistry.clearAllHistory();
}
@Override
public void rebootHistory(long offlineDurationMillis) {
- mAppOpsService.rebootHistory(offlineDurationMillis);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "rebootHistory");
+
+ Preconditions.checkArgument(offlineDurationMillis >= 0);
+
+ // Must not hold the appops lock
+ mHistoricalRegistry.shutdown();
+
+ if (offlineDurationMillis > 0) {
+ SystemClock.sleep(offlineDurationMillis);
+ }
+
+ mHistoricalRegistry = new HistoricalRegistry(mHistoricalRegistry);
+ mHistoricalRegistry.systemReady(mContext.getContentResolver());
+ mHistoricalRegistry.persistPendingHistory();
}
/**
@@ -1998,6 +5981,24 @@ 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);
+ }
+ }
+ }
+
+ private void checkSystemUid(String function) {
+ int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException(function + " must by called by the system");
+ }
+ }
+
private static int resolveUid(String packageName) {
if (packageName == null) {
return Process.INVALID_UID;
@@ -2018,43 +6019,184 @@ public class AppOpsService extends IAppOpsService.Stub {
return Process.INVALID_UID;
}
+ private static String[] getPackagesForUid(int uid) {
+ String[] packageNames = null;
+
+ // Very early during boot the package manager is not yet or not yet fully started. At this
+ // time there are no packages yet.
+ if (AppGlobals.getPackageManager() != null) {
+ try {
+ packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
+ }
+ if (packageNames == null) {
+ return EmptyArray.STRING;
+ }
+ return packageNames;
+ }
+
+ private final class ClientUserRestrictionState implements DeathRecipient {
+ private final IBinder token;
+
+ ClientUserRestrictionState(IBinder token)
+ throws RemoteException {
+ token.linkToDeath(this, 0);
+ this.token = token;
+ }
+
+ public boolean setRestriction(int code, boolean restricted,
+ PackageTagsList excludedPackageTags, int userId) {
+ return mAppOpsRestrictions.setUserRestriction(token, userId, code,
+ restricted, excludedPackageTags);
+ }
+
+ public boolean hasRestriction(int code, String packageName, String attributionTag,
+ int userId, boolean isCheckOp) {
+ return mAppOpsRestrictions.getUserRestriction(token, userId, code, packageName,
+ attributionTag, isCheckOp);
+ }
+
+ public void removeUser(int userId) {
+ mAppOpsRestrictions.clearUserRestrictions(token, userId);
+ }
+
+ public boolean isDefault() {
+ return !mAppOpsRestrictions.hasUserRestrictions(token);
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (AppOpsService.this) {
+ mAppOpsRestrictions.clearUserRestrictions(token);
+ mOpUserRestrictions.remove(token);
+ destroy();
+ }
+ }
+
+ public void destroy() {
+ token.unlinkToDeath(this, 0);
+ }
+ }
+
+ private final class ClientGlobalRestrictionState implements DeathRecipient {
+ final IBinder mToken;
+
+ ClientGlobalRestrictionState(IBinder token)
+ throws RemoteException {
+ token.linkToDeath(this, 0);
+ this.mToken = token;
+ }
+
+ boolean setRestriction(int code, boolean restricted) {
+ return mAppOpsRestrictions.setGlobalRestriction(mToken, code, restricted);
+ }
+
+ boolean hasRestriction(int code) {
+ return mAppOpsRestrictions.getGlobalRestriction(mToken, code);
+ }
+
+ boolean isDefault() {
+ return !mAppOpsRestrictions.hasGlobalRestrictions(mToken);
+ }
+
+ @Override
+ public void binderDied() {
+ mAppOpsRestrictions.clearGlobalRestrictions(mToken);
+ mOpGlobalRestrictions.remove(mToken);
+ destroy();
+ }
+
+ void destroy() {
+ mToken.unlinkToDeath(this, 0);
+ }
+ }
+
private final class AppOpsManagerInternalImpl extends AppOpsManagerInternal {
@Override public void setDeviceAndProfileOwners(SparseIntArray owners) {
- AppOpsService.this.mAppOpsService.setDeviceAndProfileOwners(owners);
+ synchronized (AppOpsService.this) {
+ mProfileOwners = owners;
+ }
}
@Override
public void updateAppWidgetVisibility(SparseArray<String> uidPackageNames,
boolean visible) {
- AppOpsService.this.mAppOpsService
- .updateAppWidgetVisibility(uidPackageNames, visible);
+ AppOpsService.this.updateAppWidgetVisibility(uidPackageNames, visible);
}
@Override
public void setUidModeFromPermissionPolicy(int code, int uid, int mode,
@Nullable IAppOpsCallback callback) {
- AppOpsService.this.mAppOpsService.setUidMode(code, uid, mode, callback);
+ setUidMode(code, uid, mode, callback);
}
@Override
public void setModeFromPermissionPolicy(int code, int uid, @NonNull String packageName,
int mode, @Nullable IAppOpsCallback callback) {
- AppOpsService.this.mAppOpsService
- .setMode(code, uid, packageName, mode, callback);
+ setMode(code, uid, packageName, mode, callback);
}
@Override
public void setGlobalRestriction(int code, boolean restricted, IBinder token) {
- AppOpsService.this.mAppOpsService
- .setGlobalRestriction(code, restricted, token);
+ if (Binder.getCallingPid() != Process.myPid()) {
+ // TODO instead of this enforcement put in AppOpsManagerInternal
+ throw new SecurityException("Only the system can set global restrictions");
+ }
+
+ synchronized (AppOpsService.this) {
+ ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.get(token);
+
+ if (restrictionState == null) {
+ try {
+ restrictionState = new ClientGlobalRestrictionState(token);
+ } catch (RemoteException e) {
+ return;
+ }
+ mOpGlobalRestrictions.put(token, restrictionState);
+ }
+
+ if (restrictionState.setRestriction(code, restricted)) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyWatchersOfChange, AppOpsService.this, code,
+ UID_ANY));
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::updateStartedOpModeForUser, AppOpsService.this,
+ code, restricted, UserHandle.USER_ALL));
+ }
+
+ if (restrictionState.isDefault()) {
+ mOpGlobalRestrictions.remove(token);
+ restrictionState.destroy();
+ }
+ }
}
@Override
public int getOpRestrictionCount(int code, UserHandle user, String pkg,
String attributionTag) {
- return AppOpsService.this.mAppOpsService
- .getOpRestrictionCount(code, user, pkg, attributionTag);
+ int number = 0;
+ synchronized (AppOpsService.this) {
+ int numRestrictions = mOpUserRestrictions.size();
+ for (int i = 0; i < numRestrictions; i++) {
+ if (mOpUserRestrictions.valueAt(i)
+ .hasRestriction(code, pkg, attributionTag, user.getIdentifier(),
+ false)) {
+ number++;
+ }
+ }
+
+ numRestrictions = mOpGlobalRestrictions.size();
+ for (int i = 0; i < numRestrictions; i++) {
+ if (mOpGlobalRestrictions.valueAt(i).hasRestriction(code)) {
+ number++;
+ }
+ }
+ }
+
+ return number;
}
}
@@ -2350,7 +6492,7 @@ public class AppOpsService extends IAppOpsService.Stub {
attributionFlags, attributionChainId, AppOpsService.this::startOperationImpl);
}
- public SyncNotedAppOp startProxyOperation(IBinder clientId, int code,
+ public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
@@ -2380,7 +6522,7 @@ public class AppOpsService extends IAppOpsService.Stub {
proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
}
- private SyncNotedAppOp startDelegateProxyOperationImpl(IBinder clientId, int code,
+ private SyncNotedAppOp startDelegateProxyOperationImpl(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
@@ -2414,7 +6556,7 @@ public class AppOpsService extends IAppOpsService.Stub {
AppOpsService.this::finishOperationImpl);
}
- public void finishProxyOperation(IBinder clientId, int code,
+ public void finishProxyOperation(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
@@ -2432,7 +6574,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- private Void finishDelegateProxyOperationImpl(IBinder clientId, int code,
+ private Void finishDelegateProxyOperationImpl(@NonNull IBinder clientId, int code,
@NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
mCheckOpsDelegate.finishProxyOperation(clientId, code, attributionSource,
skipProxyOperation, AppOpsService.this::finishProxyOperationImpl);
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsServiceImpl.java
deleted file mode 100644
index c3d2717ce795..000000000000
--- a/services/core/java/com/android/server/appop/AppOpsServiceImpl.java
+++ /dev/null
@@ -1,4742 +0,0 @@
-/*
- * Copyright (C) 2012 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 static android.app.AppOpsManager.AttributedOpEntry;
-import static android.app.AppOpsManager.AttributionFlags;
-import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
-import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
-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.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS;
-import static android.app.AppOpsManager.HistoricalOps;
-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.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.MODE_FOREGROUND;
-import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.Mode;
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_FLAGS_ALL;
-import static android.app.AppOpsManager.OP_FLAG_SELF;
-import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
-import static android.app.AppOpsManager.OP_NONE;
-import static android.app.AppOpsManager.OP_PLAY_AUDIO;
-import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
-import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
-import static android.app.AppOpsManager.OP_VIBRATE;
-import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED;
-import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED;
-import static android.app.AppOpsManager.OpEntry;
-import static android.app.AppOpsManager.OpEventProxyInfo;
-import static android.app.AppOpsManager.OpFlags;
-import static android.app.AppOpsManager.RestrictionBypass;
-import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
-import static android.app.AppOpsManager._NUM_OP;
-import static android.app.AppOpsManager.extractFlagsFromKey;
-import static android.app.AppOpsManager.extractUidStateFromKey;
-import static android.app.AppOpsManager.modeToName;
-import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
-import static android.app.AppOpsManager.opRestrictsRead;
-import static android.app.AppOpsManager.opToName;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.Intent.EXTRA_REPLACING;
-
-import static com.android.server.appop.AppOpsServiceImpl.ModeCallback.ALL_OPS;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.PermissionInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.PackageTagsList;
-import android.os.Process;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.storage.StorageManagerInternal;
-import android.permission.PermissionManager;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.KeyValueListParser;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.Xml;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IAppOpsActiveCallback;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsNotedCallback;
-import com.android.internal.app.IAppOpsStartedCallback;
-import com.android.internal.compat.IPlatformCompat;
-import com.android.internal.os.Clock;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.LocalServices;
-import com.android.server.LockGuard;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
-import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.component.ParsedAttribution;
-
-import dalvik.annotation.optimization.NeverCompile;
-
-import libcore.util.EmptyArray;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-class AppOpsServiceImpl implements AppOpsServiceInterface {
- static final String TAG = "AppOps";
- static final boolean DEBUG = false;
-
- /**
- * Sentinel integer version to denote that there was no appops.xml found on boot.
- * This will happen when a device boots with no existing userdata.
- */
- private static final int NO_FILE_VERSION = -2;
-
- /**
- * Sentinel integer version to denote that there was no version in the appops.xml found on boot.
- * This means the file is coming from a build before versioning was added.
- */
- private static final int NO_VERSION = -1;
-
- /**
- * Increment by one every time and add the corresponding upgrade logic in
- * {@link #upgradeLocked(int)} below. The first version was 1.
- */
- @VisibleForTesting
- static final int CURRENT_VERSION = 2;
-
- /**
- * This stores the version of appops.xml seen at boot. If this is smaller than
- * {@link #CURRENT_VERSION}, then we will run {@link #upgradeLocked(int)} on startup.
- */
- private int mVersionAtBoot = NO_FILE_VERSION;
-
- // Write at most every 30 minutes.
- static final long WRITE_DELAY = DEBUG ? 1000 : 30 * 60 * 1000;
-
- // Constant meaning that any UID should be matched when dispatching callbacks
- private static final int UID_ANY = -2;
-
- private static final int[] OPS_RESTRICTED_ON_SUSPEND = {
- OP_PLAY_AUDIO,
- OP_RECORD_AUDIO,
- OP_CAMERA,
- OP_VIBRATE,
- };
- private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
-
- final Context mContext;
- final AtomicFile mFile;
- final Handler mHandler;
-
- /**
- * Pool for {@link AttributedOp.OpEventProxyInfoPool} to avoid to constantly reallocate new
- * objects
- */
- @GuardedBy("this")
- final AttributedOp.OpEventProxyInfoPool mOpEventProxyInfoPool =
- new AttributedOp.OpEventProxyInfoPool(MAX_UNUSED_POOLED_OBJECTS);
-
- /**
- * Pool for {@link AttributedOp.InProgressStartOpEventPool} to avoid to constantly reallocate
- * new objects
- */
- @GuardedBy("this")
- final AttributedOp.InProgressStartOpEventPool mInProgressStartOpEventPool =
- new AttributedOp.InProgressStartOpEventPool(mOpEventProxyInfoPool,
- MAX_UNUSED_POOLED_OBJECTS);
- @Nullable
- private final DevicePolicyManagerInternal dpmi =
- LocalServices.getService(DevicePolicyManagerInternal.class);
-
- private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
-
- boolean mWriteScheduled;
- boolean mFastWriteScheduled;
- final Runnable mWriteRunner = new Runnable() {
- public void run() {
- synchronized (AppOpsServiceImpl.this) {
- mWriteScheduled = false;
- mFastWriteScheduled = false;
- AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- writeState();
- return null;
- }
- };
- task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
- }
- };
-
- @GuardedBy("this")
- @VisibleForTesting
- final SparseArray<UidState> mUidStates = new SparseArray<>();
-
- volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
-
- /*
- * These are app op restrictions imposed per user from various parties.
- */
- private final ArrayMap<IBinder, ClientUserRestrictionState> mOpUserRestrictions =
- new ArrayMap<>();
-
- /*
- * These are app op restrictions imposed globally from various parties within the system.
- */
- private final ArrayMap<IBinder, ClientGlobalRestrictionState> mOpGlobalRestrictions =
- new ArrayMap<>();
-
- SparseIntArray mProfileOwners;
-
- /**
- * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never
- * changed
- */
- private final SparseArray<int[]> mSwitchedOps = new SparseArray<>();
-
- /**
- * Package Manager internal. Access via {@link #getPackageManagerInternal()}
- */
- private @Nullable PackageManagerInternal mPackageManagerInternal;
-
- /**
- * Interface for app-op modes.
- */
- @VisibleForTesting
- AppOpsCheckingServiceInterface mAppOpsServiceInterface;
-
- /**
- * Interface for app-op restrictions.
- */
- @VisibleForTesting
- AppOpsRestrictions mAppOpsRestrictions;
-
- private AppOpsUidStateTracker mUidStateTracker;
-
- /**
- * Hands the definition of foreground and uid states
- */
- @GuardedBy("this")
- public AppOpsUidStateTracker getUidStateTracker() {
- if (mUidStateTracker == null) {
- mUidStateTracker = new AppOpsUidStateTrackerImpl(
- LocalServices.getService(ActivityManagerInternal.class),
- mHandler,
- r -> {
- synchronized (AppOpsServiceImpl.this) {
- r.run();
- }
- },
- Clock.SYSTEM_CLOCK, mConstants);
-
- mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
- this::onUidStateChanged);
- }
- return mUidStateTracker;
- }
-
- /**
- * All times are in milliseconds. These constants are kept synchronized with the system
- * global Settings. Any access to this class or its fields should be done while
- * holding the AppOpsService lock.
- */
- final class Constants extends ContentObserver {
-
- /**
- * How long we want for a drop in uid state from top to settle before applying it.
- *
- * @see Settings.Global#APP_OPS_CONSTANTS
- * @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 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 AppOpsManager#KEY_BG_STATE_SETTLE_TIME
- */
- public long BG_STATE_SETTLE_TIME;
-
- private final KeyValueListParser mParser = new KeyValueListParser(',');
- private ContentResolver mResolver;
-
- Constants(Handler handler) {
- super(handler);
- updateConstants();
- }
-
- public void startMonitoring(ContentResolver resolver) {
- mResolver = resolver;
- mResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.APP_OPS_CONSTANTS),
- false, this);
- updateConstants();
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- updateConstants();
- }
-
- private void updateConstants() {
- String value = mResolver != null ? Settings.Global.getString(mResolver,
- Settings.Global.APP_OPS_CONSTANTS) : "";
-
- synchronized (AppOpsServiceImpl.this) {
- try {
- mParser.setString(value);
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on
- // with defaults.
- Slog.e(TAG, "Bad app ops settings", e);
- }
- TOP_STATE_SETTLE_TIME = mParser.getDurationMillis(
- KEY_TOP_STATE_SETTLE_TIME, 5 * 1000L);
- FG_SERVICE_STATE_SETTLE_TIME = mParser.getDurationMillis(
- KEY_FG_SERVICE_STATE_SETTLE_TIME, 5 * 1000L);
- BG_STATE_SETTLE_TIME = mParser.getDurationMillis(
- KEY_BG_STATE_SETTLE_TIME, 1 * 1000L);
- }
- }
-
- void dump(PrintWriter pw) {
- pw.println(" Settings:");
-
- pw.print(" ");
- pw.print(KEY_TOP_STATE_SETTLE_TIME);
- pw.print("=");
- TimeUtils.formatDuration(TOP_STATE_SETTLE_TIME, pw);
- pw.println();
- pw.print(" ");
- pw.print(KEY_FG_SERVICE_STATE_SETTLE_TIME);
- pw.print("=");
- TimeUtils.formatDuration(FG_SERVICE_STATE_SETTLE_TIME, pw);
- pw.println();
- pw.print(" ");
- pw.print(KEY_BG_STATE_SETTLE_TIME);
- pw.print("=");
- TimeUtils.formatDuration(BG_STATE_SETTLE_TIME, pw);
- pw.println();
- }
- }
-
- @VisibleForTesting
- final Constants mConstants;
-
- @VisibleForTesting
- final class UidState {
- public final int uid;
-
- public ArrayMap<String, Ops> pkgOps;
-
- // true indicates there is an interested observer, false there isn't but it has such an op
- //TODO: Move foregroundOps and hasForegroundWatchers into the AppOpsServiceInterface.
- public SparseBooleanArray foregroundOps;
- public boolean hasForegroundWatchers;
-
- public UidState(int uid) {
- this.uid = uid;
- }
-
- public void clear() {
- mAppOpsServiceInterface.removeUid(uid);
- if (pkgOps != null) {
- for (String packageName : pkgOps.keySet()) {
- mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
- }
- }
- pkgOps = null;
- }
-
- public boolean isDefault() {
- boolean areAllPackageModesDefault = true;
- if (pkgOps != null) {
- for (String packageName : pkgOps.keySet()) {
- if (!mAppOpsServiceInterface.arePackageModesDefault(packageName,
- UserHandle.getUserId(uid))) {
- areAllPackageModesDefault = false;
- break;
- }
- }
- }
- return (pkgOps == null || pkgOps.isEmpty())
- && 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);
- }
-
- @SuppressWarnings("GuardedBy")
- int evalMode(int op, int mode) {
- return getUidStateTracker().evalMode(uid, op, mode);
- }
-
- public void evalForegroundOps() {
- foregroundOps = null;
- foregroundOps = mAppOpsServiceInterface.evalForegroundUidOps(uid, foregroundOps);
- if (pkgOps != null) {
- for (int i = pkgOps.size() - 1; i >= 0; i--) {
- foregroundOps = mAppOpsServiceInterface
- .evalForegroundPackageOps(pkgOps.valueAt(i).packageName,
- foregroundOps,
- UserHandle.getUserId(uid));
- }
- }
- hasForegroundWatchers = false;
- if (foregroundOps != null) {
- for (int i = 0; i < foregroundOps.size(); i++) {
- if (foregroundOps.valueAt(i)) {
- hasForegroundWatchers = true;
- break;
- }
- }
- }
- }
-
- @SuppressWarnings("GuardedBy")
- public int getState() {
- return getUidStateTracker().getUidState(uid);
- }
-
- @SuppressWarnings("GuardedBy")
- public void dump(PrintWriter pw, long nowElapsed) {
- getUidStateTracker().dumpUidState(pw, uid, nowElapsed);
- }
- }
-
- static final class Ops extends SparseArray<Op> {
- final String packageName;
- final UidState uidState;
-
- /**
- * The restriction properties of the package. If {@code null} it could not have been read
- * yet and has to be refreshed.
- */
- @Nullable RestrictionBypass bypass;
-
- /** Lazily populated cache of attributionTags of this package */
- final @NonNull ArraySet<String> knownAttributionTags = new ArraySet<>();
-
- /**
- * Lazily populated cache of <b>valid</b> attributionTags of this package, a set smaller
- * than or equal to {@link #knownAttributionTags}.
- */
- final @NonNull ArraySet<String> validAttributionTags = new ArraySet<>();
-
- Ops(String _packageName, UidState _uidState) {
- packageName = _packageName;
- uidState = _uidState;
- }
- }
-
- /** Returned from {@link #verifyAndGetBypass(int, String, String, String)}. */
- private static final class PackageVerificationResult {
-
- final RestrictionBypass bypass;
- final boolean isAttributionTagValid;
-
- PackageVerificationResult(RestrictionBypass bypass, boolean isAttributionTagValid) {
- this.bypass = bypass;
- this.isAttributionTagValid = isAttributionTagValid;
- }
- }
-
- final class Op {
- int op;
- int uid;
- final UidState uidState;
- final @NonNull String packageName;
-
- /** attributionTag -> AttributedOp */
- final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
-
- Op(UidState uidState, String packageName, int op, int uid) {
- this.op = op;
- this.uid = uid;
- this.uidState = uidState;
- this.packageName = packageName;
- }
-
- @Mode int getMode() {
- return mAppOpsServiceInterface.getPackageMode(packageName, this.op,
- UserHandle.getUserId(this.uid));
- }
-
- void setMode(@Mode int mode) {
- mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode,
- UserHandle.getUserId(this.uid));
- }
-
- void removeAttributionsWithNoTime() {
- for (int i = mAttributions.size() - 1; i >= 0; i--) {
- if (!mAttributions.valueAt(i).hasAnyTime()) {
- mAttributions.removeAt(i);
- }
- }
- }
-
- private @NonNull AttributedOp getOrCreateAttribution(@NonNull Op parent,
- @Nullable String attributionTag) {
- AttributedOp attributedOp;
-
- attributedOp = mAttributions.get(attributionTag);
- if (attributedOp == null) {
- attributedOp = new AttributedOp(AppOpsServiceImpl.this, attributionTag,
- parent);
- mAttributions.put(attributionTag, attributedOp);
- }
-
- return attributedOp;
- }
-
- @NonNull
- OpEntry createEntryLocked() {
- final int numAttributions = mAttributions.size();
-
- final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries =
- new ArrayMap<>(numAttributions);
- for (int i = 0; i < numAttributions; i++) {
- attributionEntries.put(mAttributions.keyAt(i),
- mAttributions.valueAt(i).createAttributedOpEntryLocked());
- }
-
- return new OpEntry(op, getMode(), attributionEntries);
- }
-
- @NonNull
- OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
- final int numAttributions = mAttributions.size();
-
- final ArrayMap<String, AttributedOpEntry> attributionEntries = new ArrayMap<>(1);
- for (int i = 0; i < numAttributions; i++) {
- if (Objects.equals(mAttributions.keyAt(i), attributionTag)) {
- attributionEntries.put(mAttributions.keyAt(i),
- mAttributions.valueAt(i).createAttributedOpEntryLocked());
- break;
- }
- }
-
- return new OpEntry(op, getMode(), attributionEntries);
- }
-
- boolean isRunning() {
- final int numAttributions = mAttributions.size();
- for (int i = 0; i < numAttributions; i++) {
- if (mAttributions.valueAt(i).isRunning()) {
- return true;
- }
- }
-
- return false;
- }
- }
-
- final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
- final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
- final ArrayMap<IBinder, SparseArray<StartedCallback>> mStartedWatchers = new ArrayMap<>();
- final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
-
- final class ModeCallback extends OnOpModeChangedListener implements DeathRecipient {
- /** If mWatchedOpCode==ALL_OPS notify for ops affected by the switch-op */
- public static final int ALL_OPS = -2;
-
- // Need to keep this only because stopWatchingMode needs an IAppOpsCallback.
- // Otherwise we can just use the IBinder object.
- private final IAppOpsCallback mCallback;
-
- ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOpCode,
- int callingUid, int callingPid) {
- super(watchingUid, flags, watchedOpCode, callingUid, callingPid);
- this.mCallback = callback;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- /*ignored*/
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(128);
- sb.append("ModeCallback{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" watchinguid=");
- UserHandle.formatUid(sb, getWatchingUid());
- sb.append(" flags=0x");
- sb.append(Integer.toHexString(getFlags()));
- switch (getWatchedOpCode()) {
- case OP_NONE:
- break;
- case ALL_OPS:
- sb.append(" op=(all)");
- break;
- default:
- sb.append(" op=");
- sb.append(opToName(getWatchedOpCode()));
- break;
- }
- sb.append(" from uid=");
- UserHandle.formatUid(sb, getCallingUid());
- sb.append(" pid=");
- sb.append(getCallingPid());
- sb.append('}');
- return sb.toString();
- }
-
- void unlinkToDeath() {
- mCallback.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- stopWatchingMode(mCallback);
- }
-
- @Override
- public void onOpModeChanged(int op, int uid, String packageName) throws RemoteException {
- mCallback.opChanged(op, uid, packageName);
- }
- }
-
- final class ActiveCallback implements DeathRecipient {
- final IAppOpsActiveCallback mCallback;
- final int mWatchingUid;
- final int mCallingUid;
- final int mCallingPid;
-
- ActiveCallback(IAppOpsActiveCallback callback, int watchingUid, int callingUid,
- int callingPid) {
- mCallback = callback;
- mWatchingUid = watchingUid;
- mCallingUid = callingUid;
- mCallingPid = callingPid;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- /*ignored*/
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(128);
- sb.append("ActiveCallback{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" watchinguid=");
- UserHandle.formatUid(sb, mWatchingUid);
- sb.append(" from uid=");
- UserHandle.formatUid(sb, mCallingUid);
- sb.append(" pid=");
- sb.append(mCallingPid);
- sb.append('}');
- return sb.toString();
- }
-
- void destroy() {
- mCallback.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- stopWatchingActive(mCallback);
- }
- }
-
- final class StartedCallback implements DeathRecipient {
- final IAppOpsStartedCallback mCallback;
- final int mWatchingUid;
- final int mCallingUid;
- final int mCallingPid;
-
- StartedCallback(IAppOpsStartedCallback callback, int watchingUid, int callingUid,
- int callingPid) {
- mCallback = callback;
- mWatchingUid = watchingUid;
- mCallingUid = callingUid;
- mCallingPid = callingPid;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- /*ignored*/
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(128);
- sb.append("StartedCallback{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" watchinguid=");
- UserHandle.formatUid(sb, mWatchingUid);
- sb.append(" from uid=");
- UserHandle.formatUid(sb, mCallingUid);
- sb.append(" pid=");
- sb.append(mCallingPid);
- sb.append('}');
- return sb.toString();
- }
-
- void destroy() {
- mCallback.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- stopWatchingStarted(mCallback);
- }
- }
-
- final class NotedCallback implements DeathRecipient {
- final IAppOpsNotedCallback mCallback;
- final int mWatchingUid;
- final int mCallingUid;
- final int mCallingPid;
-
- NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid,
- int callingPid) {
- mCallback = callback;
- mWatchingUid = watchingUid;
- mCallingUid = callingUid;
- mCallingPid = callingPid;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- /*ignored*/
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(128);
- sb.append("NotedCallback{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" watchinguid=");
- UserHandle.formatUid(sb, mWatchingUid);
- sb.append(" from uid=");
- UserHandle.formatUid(sb, mCallingUid);
- sb.append(" pid=");
- sb.append(mCallingPid);
- sb.append('}');
- return sb.toString();
- }
-
- void destroy() {
- mCallback.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- stopWatchingNoted(mCallback);
- }
- }
-
- /**
- * Call {@link AttributedOp#onClientDeath attributedOp.onClientDeath(clientId)}.
- */
- static void onClientDeath(@NonNull AttributedOp attributedOp,
- @NonNull IBinder clientId) {
- attributedOp.onClientDeath(clientId);
- }
-
- AppOpsServiceImpl(File storagePath, Handler handler, Context context) {
- mContext = context;
-
- for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) {
- int switchCode = AppOpsManager.opToSwitch(switchedCode);
- mSwitchedOps.put(switchCode,
- ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode));
- }
- mAppOpsServiceInterface = new AppOpsCheckingServiceTracingDecorator(
- new AppOpsCheckingServiceImpl(this, this, handler, context, mSwitchedOps));
- mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler,
- mAppOpsServiceInterface);
-
- LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
- mFile = new AtomicFile(storagePath, "appops");
-
- mHandler = handler;
- mConstants = new Constants(mHandler);
- readState();
- }
-
- /**
- * Handler for work when packages are removed or updated
- */
- private BroadcastReceiver mOnPackageUpdatedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- String pkgName = intent.getData().getEncodedSchemeSpecificPart();
- int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
-
- if (action.equals(ACTION_PACKAGE_REMOVED) && !intent.hasExtra(EXTRA_REPLACING)) {
- synchronized (AppOpsServiceImpl.this) {
- UidState uidState = mUidStates.get(uid);
- if (uidState == null || uidState.pkgOps == null) {
- return;
- }
- mAppOpsServiceInterface.removePackage(pkgName, UserHandle.getUserId(uid));
- Ops removedOps = uidState.pkgOps.remove(pkgName);
- if (removedOps != null) {
- scheduleFastWriteLocked();
- }
- }
- } else if (action.equals(Intent.ACTION_PACKAGE_REPLACED)) {
- AndroidPackage pkg = getPackageManagerInternal().getPackage(pkgName);
- if (pkg == null) {
- return;
- }
-
- ArrayMap<String, String> dstAttributionTags = new ArrayMap<>();
- ArraySet<String> attributionTags = new ArraySet<>();
- attributionTags.add(null);
- if (pkg.getAttributions() != null) {
- int numAttributions = pkg.getAttributions().size();
- for (int attributionNum = 0; attributionNum < numAttributions;
- attributionNum++) {
- ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
- attributionTags.add(attribution.getTag());
-
- int numInheritFrom = attribution.getInheritFrom().size();
- for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
- inheritFromNum++) {
- dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum),
- attribution.getTag());
- }
- }
- }
-
- synchronized (AppOpsServiceImpl.this) {
- UidState uidState = mUidStates.get(uid);
- if (uidState == null || uidState.pkgOps == null) {
- return;
- }
-
- Ops ops = uidState.pkgOps.get(pkgName);
- if (ops == null) {
- return;
- }
-
- // Reset cached package properties to re-initialize when needed
- ops.bypass = null;
- ops.knownAttributionTags.clear();
-
- // Merge data collected for removed attributions into their successor
- // attributions
- int numOps = ops.size();
- for (int opNum = 0; opNum < numOps; opNum++) {
- Op op = ops.valueAt(opNum);
-
- int numAttributions = op.mAttributions.size();
- for (int attributionNum = numAttributions - 1; attributionNum >= 0;
- attributionNum--) {
- String attributionTag = op.mAttributions.keyAt(attributionNum);
-
- if (attributionTags.contains(attributionTag)) {
- // attribution still exist after upgrade
- continue;
- }
-
- String newAttributionTag = dstAttributionTags.get(attributionTag);
-
- AttributedOp newAttributedOp = op.getOrCreateAttribution(op,
- newAttributionTag);
- newAttributedOp.add(op.mAttributions.valueAt(attributionNum));
- op.mAttributions.removeAt(attributionNum);
-
- scheduleFastWriteLocked();
- }
- }
- }
- }
- }
- };
-
- @Override
- public void systemReady() {
- synchronized (this) {
- upgradeLocked(mVersionAtBoot);
- }
-
- mConstants.startMonitoring(mContext.getContentResolver());
- mHistoricalRegistry.systemReady(mContext.getContentResolver());
-
- IntentFilter packageUpdateFilter = new IntentFilter();
- packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- packageUpdateFilter.addDataScheme("package");
-
- mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL,
- packageUpdateFilter, null, null);
-
- synchronized (this) {
- for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) {
- int uid = mUidStates.keyAt(uidNum);
- UidState uidState = mUidStates.valueAt(uidNum);
-
- String[] pkgsInUid = getPackagesForUid(uidState.uid);
- if (ArrayUtils.isEmpty(pkgsInUid)) {
- uidState.clear();
- mUidStates.removeAt(uidNum);
- scheduleFastWriteLocked();
- continue;
- }
-
- ArrayMap<String, Ops> pkgs = uidState.pkgOps;
- if (pkgs == null) {
- continue;
- }
-
- int numPkgs = pkgs.size();
- for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
- String pkg = pkgs.keyAt(pkgNum);
-
- String action;
- if (!ArrayUtils.contains(pkgsInUid, pkg)) {
- action = Intent.ACTION_PACKAGE_REMOVED;
- } else {
- action = Intent.ACTION_PACKAGE_REPLACED;
- }
-
- SystemServerInitThreadPool.submit(
- () -> mOnPackageUpdatedReceiver.onReceive(mContext, new Intent(action)
- .setData(Uri.fromParts("package", pkg, null))
- .putExtra(Intent.EXTRA_UID, uid)),
- "Update app-ops uidState in case package " + pkg + " changed");
- }
- }
- }
-
- final IntentFilter packageSuspendFilter = new IntentFilter();
- packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
- packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
- final String[] changedPkgs = intent.getStringArrayExtra(
- Intent.EXTRA_CHANGED_PACKAGE_LIST);
- for (int code : OPS_RESTRICTED_ON_SUSPEND) {
- ArraySet<OnOpModeChangedListener> onModeChangedListeners;
- synchronized (AppOpsServiceImpl.this) {
- onModeChangedListeners =
- mAppOpsServiceInterface.getOpModeChangedListeners(code);
- if (onModeChangedListeners == null) {
- continue;
- }
- }
- for (int i = 0; i < changedUids.length; i++) {
- final int changedUid = changedUids[i];
- final String changedPkg = changedPkgs[i];
- // We trust packagemanager to insert matching uid and packageNames in the
- // extras
- notifyOpChanged(onModeChangedListeners, code, changedUid, changedPkg);
- }
- }
- }
- }, UserHandle.ALL, packageSuspendFilter, null, null);
- }
-
- @Override
- public void packageRemoved(int uid, String packageName) {
- synchronized (this) {
- UidState uidState = mUidStates.get(uid);
- if (uidState == null) {
- return;
- }
-
- Ops removedOps = null;
-
- // Remove any package state if such.
- if (uidState.pkgOps != null) {
- removedOps = uidState.pkgOps.remove(packageName);
- mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
- }
-
- // If we just nuked the last package state check if the UID is valid.
- if (removedOps != null && uidState.pkgOps.isEmpty()
- && getPackagesForUid(uid).length <= 0) {
- uidState.clear();
- mUidStates.remove(uid);
- }
-
- if (removedOps != null) {
- scheduleFastWriteLocked();
-
- final int numOps = removedOps.size();
- for (int opNum = 0; opNum < numOps; opNum++) {
- final Op op = removedOps.valueAt(opNum);
-
- final int numAttributions = op.mAttributions.size();
- for (int attributionNum = 0; attributionNum < numAttributions;
- attributionNum++) {
- AttributedOp attributedOp = op.mAttributions.valueAt(attributionNum);
-
- while (attributedOp.isRunning()) {
- attributedOp.finished(attributedOp.mInProgressEvents.keyAt(0));
- }
- while (attributedOp.isPaused()) {
- attributedOp.finished(attributedOp.mPausedInProgressEvents.keyAt(0));
- }
- }
- }
- }
- }
-
- mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory,
- mHistoricalRegistry, uid, packageName));
- }
-
- @Override
- public void uidRemoved(int uid) {
- synchronized (this) {
- if (mUidStates.indexOfKey(uid) >= 0) {
- mUidStates.get(uid).clear();
- mUidStates.remove(uid);
- scheduleFastWriteLocked();
- }
- }
- }
-
- // The callback method from ForegroundPolicyInterface
- private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
- synchronized (this) {
- UidState uidState = getUidStateLocked(uid, true);
-
- if (uidState != null && foregroundModeMayChange && uidState.hasForegroundWatchers) {
- for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) {
- if (!uidState.foregroundOps.valueAt(fgi)) {
- continue;
- }
- final int code = uidState.foregroundOps.keyAt(fgi);
-
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
- && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpChangedForAllPkgsInUid,
- this, code, uidState.uid, true, null));
- } else if (uidState.pkgOps != null) {
- final ArraySet<OnOpModeChangedListener> listenerSet =
- mAppOpsServiceInterface.getOpModeChangedListeners(code);
- if (listenerSet != null) {
- for (int cbi = listenerSet.size() - 1; cbi >= 0; cbi--) {
- final OnOpModeChangedListener listener = listenerSet.valueAt(cbi);
- if ((listener.getFlags()
- & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
- || !listener.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 (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpChanged,
- this, listenerSet.valueAt(cbi), code, uidState.uid,
- uidState.pkgOps.keyAt(pkgi)));
- }
- }
- }
- }
- }
- }
- }
-
- if (uidState != null && uidState.pkgOps != null) {
- int numPkgs = uidState.pkgOps.size();
- for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
- Ops ops = uidState.pkgOps.valueAt(pkgNum);
-
- int numOps = ops.size();
- for (int opNum = 0; opNum < numOps; opNum++) {
- Op op = ops.valueAt(opNum);
-
- int numAttributions = op.mAttributions.size();
- for (int attributionNum = 0; attributionNum < numAttributions;
- attributionNum++) {
- AttributedOp attributedOp = op.mAttributions.valueAt(
- attributionNum);
-
- attributedOp.onUidStateChanged(state);
- }
- }
- }
- }
- }
- }
-
- /**
- * Notify the proc state or capability has changed for a certain UID.
- */
- @Override
- public void updateUidProcState(int uid, int procState,
- @ActivityManager.ProcessCapability int capability) {
- synchronized (this) {
- getUidStateTracker().updateUidProcState(uid, procState, capability);
- if (!mUidStates.contains(uid)) {
- UidState uidState = new UidState(uid);
- mUidStates.put(uid, uidState);
- onUidStateChanged(uid,
- AppOpsUidStateTracker.processStateToUidState(procState), false);
- }
- }
- }
-
- @Override
- public void shutdown() {
- Slog.w(TAG, "Writing app ops before shutdown...");
- boolean doWrite = false;
- synchronized (this) {
- if (mWriteScheduled) {
- mWriteScheduled = false;
- mFastWriteScheduled = false;
- mHandler.removeCallbacks(mWriteRunner);
- doWrite = true;
- }
- }
- if (doWrite) {
- writeState();
- }
-
- mHistoricalRegistry.shutdown();
- }
-
- private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
- ArrayList<AppOpsManager.OpEntry> resOps = null;
- if (ops == null) {
- resOps = new ArrayList<>();
- for (int j = 0; j < pkgOps.size(); j++) {
- Op curOp = pkgOps.valueAt(j);
- resOps.add(getOpEntryForResult(curOp));
- }
- } else {
- for (int j = 0; j < ops.length; j++) {
- Op curOp = pkgOps.get(ops[j]);
- if (curOp != null) {
- if (resOps == null) {
- resOps = new ArrayList<>();
- }
- resOps.add(getOpEntryForResult(curOp));
- }
- }
- }
- return resOps;
- }
-
- @Nullable
- private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
- @Nullable int[] ops) {
- final SparseIntArray opModes = uidState.getNonDefaultUidModes();
- if (opModes == null) {
- return null;
- }
-
- int opModeCount = opModes.size();
- if (opModeCount == 0) {
- return null;
- }
- ArrayList<AppOpsManager.OpEntry> resOps = null;
- if (ops == null) {
- resOps = new ArrayList<>();
- for (int i = 0; i < opModeCount; i++) {
- 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 (opModes.indexOfKey(code) >= 0) {
- if (resOps == null) {
- resOps = new ArrayList<>();
- }
- resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
- }
- }
- }
- return resOps;
- }
-
- private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) {
- return op.createEntryLocked();
- }
-
- @Override
- public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
- final int callingUid = Binder.getCallingUid();
- final boolean hasAllPackageAccess = mContext.checkPermission(
- Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(),
- Binder.getCallingUid(), null) == PackageManager.PERMISSION_GRANTED;
- ArrayList<AppOpsManager.PackageOps> res = null;
- synchronized (this) {
- final int uidStateCount = mUidStates.size();
- for (int i = 0; i < uidStateCount; i++) {
- UidState uidState = mUidStates.valueAt(i);
- if (uidState.pkgOps == null || uidState.pkgOps.isEmpty()) {
- continue;
- }
- ArrayMap<String, Ops> packages = uidState.pkgOps;
- final int packageCount = packages.size();
- for (int j = 0; j < packageCount; j++) {
- Ops pkgOps = packages.valueAt(j);
- ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
- if (resOps != null) {
- if (res == null) {
- res = new ArrayList<>();
- }
- AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
- pkgOps.packageName, pkgOps.uidState.uid, resOps);
- // Caller can always see their packages and with a permission all.
- if (hasAllPackageAccess || callingUid == pkgOps.uidState.uid) {
- res.add(resPackage);
- }
- }
- }
- }
- }
- return res;
- }
-
- @Override
- public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
- int[] ops) {
- enforceGetAppOpsStatsPermissionIfNeeded(uid, packageName);
- String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return Collections.emptyList();
- }
- synchronized (this) {
- Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, false, null,
- /* edit */ false);
- if (pkgOps == null) {
- return null;
- }
- ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
- if (resOps == null) {
- return null;
- }
- ArrayList<AppOpsManager.PackageOps> res = new ArrayList<>();
- AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
- pkgOps.packageName, pkgOps.uidState.uid, resOps);
- res.add(resPackage);
- return res;
- }
- }
-
- private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
- final int callingUid = Binder.getCallingUid();
- // We get to access everything
- if (callingUid == Process.myPid()) {
- return;
- }
- // Apps can access their own data
- if (uid == callingUid && packageName != null
- && checkPackage(uid, packageName) == MODE_ALLOWED) {
- return;
- }
- // Otherwise, you need a permission...
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), callingUid, null);
- }
-
- /**
- * Verify that historical appop request arguments are valid.
- */
- private void ensureHistoricalOpRequestIsValid(int uid, String packageName,
- String attributionTag, List<String> opNames, int filter, long beginTimeMillis,
- long endTimeMillis, int flags) {
- if ((filter & FILTER_BY_UID) != 0) {
- Preconditions.checkArgument(uid != Process.INVALID_UID);
- } else {
- Preconditions.checkArgument(uid == Process.INVALID_UID);
- }
-
- if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
- Objects.requireNonNull(packageName);
- } else {
- Preconditions.checkArgument(packageName == null);
- }
-
- if ((filter & FILTER_BY_ATTRIBUTION_TAG) == 0) {
- Preconditions.checkArgument(attributionTag == null);
- }
-
- if ((filter & FILTER_BY_OP_NAMES) != 0) {
- Objects.requireNonNull(opNames);
- } else {
- Preconditions.checkArgument(opNames == null);
- }
-
- Preconditions.checkFlagsArgument(filter,
- FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_ATTRIBUTION_TAG
- | FILTER_BY_OP_NAMES);
- Preconditions.checkArgumentNonnegative(beginTimeMillis);
- Preconditions.checkArgument(endTimeMillis > beginTimeMillis);
- Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
- }
-
- @Override
- public void getHistoricalOps(int uid, String packageName, String attributionTag,
- List<String> opNames, int dataType, int filter, long beginTimeMillis,
- long endTimeMillis, int flags, RemoteCallback callback) {
- PackageManager pm = mContext.getPackageManager();
-
- ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
- beginTimeMillis, endTimeMillis, flags);
- Objects.requireNonNull(callback, "callback cannot be null");
- ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
- boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
- if (!isSelfRequest) {
- boolean isCallerInstrumented =
- ami.getInstrumentationSourceUid(Binder.getCallingUid()) != Process.INVALID_UID;
- boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
- boolean isCallerPermissionController;
- try {
- isCallerPermissionController = pm.getPackageUidAsUser(
- mContext.getPackageManager().getPermissionControllerPackageName(), 0,
- UserHandle.getUserId(Binder.getCallingUid()))
- == Binder.getCallingUid();
- } catch (PackageManager.NameNotFoundException doesNotHappen) {
- return;
- }
-
- boolean doesCallerHavePermission = mContext.checkPermission(
- android.Manifest.permission.GET_HISTORICAL_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid())
- == PackageManager.PERMISSION_GRANTED;
-
- if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController
- && !doesCallerHavePermission) {
- mHandler.post(() -> callback.sendResult(new Bundle()));
- return;
- }
-
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
- }
-
- final String[] opNamesArray = (opNames != null)
- ? opNames.toArray(new String[opNames.size()]) : null;
-
- Set<String> attributionChainExemptPackages = null;
- if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
- attributionChainExemptPackages =
- PermissionManager.getIndicatorExemptedPackages(mContext);
- }
-
- final String[] chainExemptPkgArray = attributionChainExemptPackages != null
- ? attributionChainExemptPackages.toArray(
- new String[attributionChainExemptPackages.size()]) : null;
-
- // Must not hold the appops lock
- mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
- mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
- callback).recycleOnUse());
- }
-
- @Override
- public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
- List<String> opNames, int dataType, int filter, long beginTimeMillis,
- long endTimeMillis, int flags, RemoteCallback callback) {
- ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
- beginTimeMillis, endTimeMillis, flags);
- Objects.requireNonNull(callback, "callback cannot be null");
-
- mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
- Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
-
- final String[] opNamesArray = (opNames != null)
- ? opNames.toArray(new String[opNames.size()]) : null;
-
- Set<String> attributionChainExemptPackages = null;
- if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
- attributionChainExemptPackages =
- PermissionManager.getIndicatorExemptedPackages(mContext);
- }
-
- final String[] chainExemptPkgArray = attributionChainExemptPackages != null
- ? attributionChainExemptPackages.toArray(
- new String[attributionChainExemptPackages.size()]) : null;
-
- // Must not hold the appops lock
- mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
- mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
- callback).recycleOnUse());
- }
-
- @Override
- public void reloadNonHistoricalState() {
- mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
- Binder.getCallingPid(), Binder.getCallingUid(), "reloadNonHistoricalState");
- writeState();
- readState();
- }
-
- @Override
- public List<AppOpsManager.PackageOps> getUidOps(int uid, int[] ops) {
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- synchronized (this) {
- UidState uidState = getUidStateLocked(uid, false);
- if (uidState == null) {
- return null;
- }
- ArrayList<AppOpsManager.OpEntry> resOps = collectUidOps(uidState, ops);
- if (resOps == null) {
- return null;
- }
- ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
- AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
- null, uidState.uid, resOps);
- res.add(resPackage);
- return res;
- }
- }
-
- private void pruneOpLocked(Op op, int uid, String packageName) {
- op.removeAttributionsWithNoTime();
-
- if (op.mAttributions.isEmpty()) {
- 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,
- UserHandle.getUserId(uidState.uid));
- if (pkgOps.isEmpty()) {
- uidState.pkgOps = null;
- }
- if (uidState.isDefault()) {
- uidState.clear();
- mUidStates.remove(uid);
- }
- }
- }
- }
- }
- }
-
- @Override
- public void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) {
- if (callingPid == Process.myPid()) {
- return;
- }
- final int callingUser = UserHandle.getUserId(callingUid);
- synchronized (this) {
- if (mProfileOwners != null && mProfileOwners.get(callingUser, -1) == callingUid) {
- if (targetUid >= 0 && callingUser == UserHandle.getUserId(targetUid)) {
- // Profile owners are allowed to change modes but only for apps
- // within their user.
- return;
- }
- }
- }
- mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
-
- @Override
- public void setUidMode(int code, int uid, int mode,
- @Nullable IAppOpsCallback permissionPolicyCallback) {
- if (DEBUG) {
- Slog.i(TAG, "uid " + uid + " OP_" + opToName(code) + " := " + modeToName(mode)
- + " by uid " + Binder.getCallingUid());
- }
-
- enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
- verifyIncomingOp(code);
- code = AppOpsManager.opToSwitch(code);
-
- if (permissionPolicyCallback == null) {
- updatePermissionRevokedCompat(uid, code, mode);
- }
-
- int previousMode;
- synchronized (this) {
- final int defaultMode = AppOpsManager.opToDefaultMode(code);
-
- UidState uidState = getUidStateLocked(uid, false);
- if (uidState == null) {
- if (mode == defaultMode) {
- return;
- }
- uidState = new UidState(uid);
- mUidStates.put(uid, uidState);
- }
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
- previousMode = uidState.getUidMode(code);
- } else {
- // doesn't look right but is legacy behavior.
- previousMode = MODE_DEFAULT;
- }
-
- if (!uidState.setUidMode(code, mode)) {
- return;
- }
- uidState.evalForegroundOps();
- if (mode != MODE_ERRORED && mode != previousMode) {
- updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
- }
- }
-
- notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
- notifyOpChangedSync(code, uid, null, mode, previousMode);
- }
-
- /**
- * 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) {
- ModeCallback listenerToIgnore = callbackToIgnore != null
- ? mModeWatchers.get(callbackToIgnore.asBinder()) : null;
- mAppOpsServiceInterface.notifyOpChangedForAllPkgsInUid(code, uid, onlyForeground,
- listenerToIgnore);
- }
-
- 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];
-
- int[] ops = mSwitchedOps.get(switchCode);
- for (int code : ops) {
- String permissionName = AppOpsManager.opToPermission(code);
- if (permissionName == null) {
- continue;
- }
-
- if (packageManager.checkPermission(permissionName, packageName)
- != PackageManager.PERMISSION_GRANTED) {
- continue;
- }
-
- PermissionInfo permissionInfo;
- try {
- permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- continue;
- }
-
- if (!permissionInfo.isRuntime()) {
- continue;
- }
-
- boolean supportsRuntimePermissions = getPackageManagerInternal()
- .getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M;
-
- UserHandle user = UserHandle.getUserHandleForUid(uid);
- boolean isRevokedCompat;
- if (permissionInfo.backgroundPermission != null) {
- if (packageManager.checkPermission(permissionInfo.backgroundPermission, packageName)
- == PackageManager.PERMISSION_GRANTED) {
- boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
-
- if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
- Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
- + " permission state, this is discouraged and you should revoke the"
- + " runtime permission instead: uid=" + uid + ", switchCode="
- + switchCode + ", mode=" + mode + ", permission="
- + permissionInfo.backgroundPermission);
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
- packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
- isBackgroundRevokedCompat
- ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
- && mode != AppOpsManager.MODE_FOREGROUND;
- } else {
- isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
- }
-
- if (isRevokedCompat && supportsRuntimePermissions) {
- Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
- + " permission state, this is discouraged and you should revoke the"
- + " runtime permission instead: uid=" + uid + ", switchCode="
- + switchCode + ", mode=" + mode + ", permission=" + permissionName);
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- packageManager.updatePermissionFlags(permissionName, packageName,
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, isRevokedCompat
- ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode,
- int previousMode) {
- final StorageManagerInternal storageManagerInternal =
- LocalServices.getService(StorageManagerInternal.class);
- if (storageManagerInternal != null) {
- storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode, previousMode);
- }
- }
-
- @Override
- public void setMode(int code, int uid, @NonNull String packageName, int mode,
- @Nullable IAppOpsCallback permissionPolicyCallback) {
- enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
- verifyIncomingOp(code);
- if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- return;
- }
-
- ArraySet<OnOpModeChangedListener> repCbs = null;
- code = AppOpsManager.opToSwitch(code);
-
- PackageVerificationResult pvr;
- try {
- pvr = verifyAndGetBypass(uid, packageName, null);
- } catch (SecurityException e) {
- Slog.e(TAG, "Cannot setMode", e);
- return;
- }
-
- int previousMode = MODE_DEFAULT;
- synchronized (this) {
- UidState uidState = getUidStateLocked(uid, false);
- Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
- if (op != null) {
- if (op.getMode() != mode) {
- previousMode = op.getMode();
- op.setMode(mode);
-
- if (uidState != null) {
- uidState.evalForegroundOps();
- }
- ArraySet<OnOpModeChangedListener> cbs =
- mAppOpsServiceInterface.getOpModeChangedListeners(code);
- if (cbs != null) {
- if (repCbs == null) {
- repCbs = new ArraySet<>();
- }
- repCbs.addAll(cbs);
- }
- cbs = mAppOpsServiceInterface.getPackageModeChangedListeners(packageName);
- if (cbs != null) {
- if (repCbs == null) {
- repCbs = new ArraySet<>();
- }
- repCbs.addAll(cbs);
- }
- if (repCbs != null && permissionPolicyCallback != null) {
- repCbs.remove(mModeWatchers.get(permissionPolicyCallback.asBinder()));
- }
- if (mode == AppOpsManager.opToDefaultMode(op.op)) {
- // If going into the default mode, prune this op
- // if there is nothing else interesting in it.
- pruneOpLocked(op, uid, packageName);
- }
- scheduleFastWriteLocked();
- if (mode != MODE_ERRORED) {
- updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
- }
- }
- }
- }
- if (repCbs != null) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpChanged,
- this, repCbs, code, uid, packageName));
- }
-
- notifyOpChangedSync(code, uid, packageName, mode, previousMode);
- }
-
- private void notifyOpChanged(ArraySet<OnOpModeChangedListener> callbacks, int code,
- int uid, String packageName) {
- for (int i = 0; i < callbacks.size(); i++) {
- final OnOpModeChangedListener callback = callbacks.valueAt(i);
- notifyOpChanged(callback, code, uid, packageName);
- }
- }
-
- private void notifyOpChanged(OnOpModeChangedListener callback, int code,
- int uid, String packageName) {
- mAppOpsServiceInterface.notifyOpChanged(callback, code, uid, packageName);
- }
-
- private static ArrayList<ChangeRec> addChange(ArrayList<ChangeRec> reports,
- int op, int uid, String packageName, int previousMode) {
- boolean duplicate = false;
- if (reports == null) {
- reports = new ArrayList<>();
- } else {
- final int reportCount = reports.size();
- for (int j = 0; j < reportCount; j++) {
- ChangeRec report = reports.get(j);
- if (report.op == op && report.pkg.equals(packageName)) {
- duplicate = true;
- break;
- }
- }
- }
- if (!duplicate) {
- reports.add(new ChangeRec(op, uid, packageName, previousMode));
- }
-
- return reports;
- }
-
- private static HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> addCallbacks(
- HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> callbacks,
- int op, int uid, String packageName, int previousMode,
- ArraySet<OnOpModeChangedListener> cbs) {
- if (cbs == null) {
- return callbacks;
- }
- if (callbacks == null) {
- callbacks = new HashMap<>();
- }
- final int N = cbs.size();
- for (int i=0; i<N; i++) {
- OnOpModeChangedListener cb = cbs.valueAt(i);
- ArrayList<ChangeRec> reports = callbacks.get(cb);
- ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName, previousMode);
- if (changed != reports) {
- callbacks.put(cb, changed);
- }
- }
- return callbacks;
- }
-
- static final class ChangeRec {
- final int op;
- final int uid;
- final String pkg;
- final int previous_mode;
-
- ChangeRec(int _op, int _uid, String _pkg, int _previous_mode) {
- op = _op;
- uid = _uid;
- pkg = _pkg;
- previous_mode = _previous_mode;
- }
- }
-
- @Override
- public void resetAllModes(int reqUserId, String reqPackageName) {
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId,
- true, true, "resetAllModes", null);
-
- int reqUid = -1;
- if (reqPackageName != null) {
- try {
- reqUid = AppGlobals.getPackageManager().getPackageUid(
- reqPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, reqUserId);
- } catch (RemoteException e) {
- /* ignore - local call */
- }
- }
-
- enforceManageAppOpsModes(callingPid, callingUid, reqUid);
-
- HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> callbacks = null;
- ArrayList<ChangeRec> allChanges = new ArrayList<>();
- synchronized (this) {
- boolean changed = false;
- for (int i = mUidStates.size() - 1; i >= 0; i--) {
- UidState uidState = mUidStates.valueAt(i);
-
- 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);
- uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
- for (String packageName : getPackagesForUid(uidState.uid)) {
- callbacks = addCallbacks(callbacks, code, uidState.uid,
- packageName, previousMode,
- mAppOpsServiceInterface.getOpModeChangedListeners(code));
- callbacks = addCallbacks(callbacks, code, uidState.uid,
- packageName, previousMode, mAppOpsServiceInterface
- .getPackageModeChangedListeners(packageName));
-
- allChanges = addChange(allChanges, code, uidState.uid,
- packageName, previousMode);
- }
- }
- }
- }
-
- if (uidState.pkgOps == null) {
- continue;
- }
-
- if (reqUserId != UserHandle.USER_ALL
- && reqUserId != UserHandle.getUserId(uidState.uid)) {
- // Skip any ops for a different user
- continue;
- }
-
- Map<String, Ops> packages = uidState.pkgOps;
- Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
- boolean uidChanged = false;
- while (it.hasNext()) {
- Map.Entry<String, Ops> ent = it.next();
- String packageName = ent.getKey();
- if (reqPackageName != null && !reqPackageName.equals(packageName)) {
- // Skip any ops for a different package
- continue;
- }
- Ops pkgOps = ent.getValue();
- for (int j=pkgOps.size()-1; j>=0; j--) {
- Op curOp = pkgOps.valueAt(j);
- if (shouldDeferResetOpToDpm(curOp.op)) {
- deferResetOpToDpm(curOp.op, reqPackageName, reqUserId);
- continue;
- }
- if (AppOpsManager.opAllowsReset(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;
- callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
- previousMode,
- mAppOpsServiceInterface.getOpModeChangedListeners(curOp.op));
- callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
- previousMode, mAppOpsServiceInterface
- .getPackageModeChangedListeners(packageName));
-
- allChanges = addChange(allChanges, curOp.op, uid, packageName,
- previousMode);
- curOp.removeAttributionsWithNoTime();
- if (curOp.mAttributions.isEmpty()) {
- pkgOps.removeAt(j);
- }
- }
- }
- if (pkgOps.size() == 0) {
- it.remove();
- mAppOpsServiceInterface.removePackage(packageName,
- UserHandle.getUserId(uidState.uid));
- }
- }
- if (uidState.isDefault()) {
- uidState.clear();
- mUidStates.remove(uidState.uid);
- }
- if (uidChanged) {
- uidState.evalForegroundOps();
- }
- }
-
- if (changed) {
- scheduleFastWriteLocked();
- }
- }
- if (callbacks != null) {
- for (Map.Entry<OnOpModeChangedListener, ArrayList<ChangeRec>> ent
- : callbacks.entrySet()) {
- OnOpModeChangedListener cb = ent.getKey();
- ArrayList<ChangeRec> reports = ent.getValue();
- for (int i=0; i<reports.size(); i++) {
- ChangeRec rep = reports.get(i);
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpChanged,
- this, cb, rep.op, rep.uid, rep.pkg));
- }
- }
- }
-
- int numChanges = allChanges.size();
- for (int i = 0; i < numChanges; i++) {
- ChangeRec change = allChanges.get(i);
- notifyOpChangedSync(change.op, change.uid, change.pkg,
- AppOpsManager.opToDefaultMode(change.op), change.previous_mode);
- }
- }
-
- private boolean shouldDeferResetOpToDpm(int op) {
- // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
- // pre-grants to a role-based mechanism or another general-purpose mechanism.
- return dpmi != null && dpmi.supportsResetOp(op);
- }
-
- /** Assumes {@link #shouldDeferResetOpToDpm(int)} is true. */
- private void deferResetOpToDpm(int op, String packageName, @UserIdInt int userId) {
- // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
- // pre-grants to a role-based mechanism or another general-purpose mechanism.
- dpmi.resetOp(op, packageName, userId);
- }
-
- private void evalAllForegroundOpsLocked() {
- for (int uidi = mUidStates.size() - 1; uidi >= 0; uidi--) {
- final UidState uidState = mUidStates.valueAt(uidi);
- if (uidState.foregroundOps != null) {
- uidState.evalForegroundOps();
- }
- }
- }
-
- @Override
- public void startWatchingModeWithFlags(int op, String packageName, int flags,
- IAppOpsCallback callback) {
- int watchedUid = -1;
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- // TODO: should have a privileged permission to protect this.
- // Also, if the caller has requested WATCH_FOREGROUND_CHANGES, should we require
- // the USAGE_STATS permission since this can provide information about when an
- // app is in the foreground?
- Preconditions.checkArgumentInRange(op, AppOpsManager.OP_NONE,
- AppOpsManager._NUM_OP - 1, "Invalid op code: " + op);
- if (callback == null) {
- return;
- }
- final boolean mayWatchPackageName = packageName != null
- && !filterAppAccessUnlocked(packageName, UserHandle.getUserId(callingUid));
- synchronized (this) {
- int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
-
- int notifiedOps;
- if ((flags & CALL_BACK_ON_SWITCHED_OP) == 0) {
- 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, notifiedOps, callingUid,
- callingPid);
- mModeWatchers.put(callback.asBinder(), cb);
- }
- if (switchOp != AppOpsManager.OP_NONE) {
- mAppOpsServiceInterface.startWatchingOpModeChanged(cb, switchOp);
- }
- if (mayWatchPackageName) {
- mAppOpsServiceInterface.startWatchingPackageModeChanged(cb, packageName);
- }
- evalAllForegroundOpsLocked();
- }
- }
-
- @Override
- public void stopWatchingMode(IAppOpsCallback callback) {
- if (callback == null) {
- return;
- }
- synchronized (this) {
- ModeCallback cb = mModeWatchers.remove(callback.asBinder());
- if (cb != null) {
- cb.unlinkToDeath();
- mAppOpsServiceInterface.removeListener(cb);
- }
-
- evalAllForegroundOpsLocked();
- }
- }
-
- @Override
- public int checkOperation(int code, int uid, String packageName,
- @Nullable String attributionTag, boolean raw) {
- verifyIncomingOp(code);
- if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- return AppOpsManager.opToDefaultMode(code);
- }
-
- String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag, raw);
- }
-
- /**
- * Get the mode of an app-op.
- *
- * @param code The code of the op
- * @param uid The uid of the package the op belongs to
- * @param packageName The package the op belongs to
- * @param raw If the raw state of eval-ed state should be checked.
- * @return The mode of the op
- */
- private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, boolean raw) {
- PackageVerificationResult pvr;
- try {
- pvr = verifyAndGetBypass(uid, packageName, null);
- } catch (SecurityException e) {
- Slog.e(TAG, "checkOperation", e);
- return AppOpsManager.opToDefaultMode(code);
- }
-
- if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
- return AppOpsManager.MODE_IGNORED;
- }
- synchronized (this) {
- if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, true)) {
- return AppOpsManager.MODE_IGNORED;
- }
- code = AppOpsManager.opToSwitch(code);
- UidState uidState = getUidStateLocked(uid, false);
- 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.getMode() : op.uidState.evalMode(op.op, op.getMode());
- }
- }
-
- @Override
- public int checkPackage(int uid, String packageName) {
- Objects.requireNonNull(packageName);
- try {
- verifyAndGetBypass(uid, packageName, null);
- // When the caller is the system, it's possible that the packageName is the special
- // one (e.g., "root") which isn't actually existed.
- if (resolveUid(packageName) == uid
- || (isPackageExisted(packageName)
- && !filterAppAccessUnlocked(packageName, UserHandle.getUserId(uid)))) {
- return AppOpsManager.MODE_ALLOWED;
- }
- return AppOpsManager.MODE_ERRORED;
- } catch (SecurityException ignored) {
- return AppOpsManager.MODE_ERRORED;
- }
- }
-
- private boolean isPackageExisted(String packageName) {
- return getPackageManagerInternal().getPackageStateInternal(packageName) != null;
- }
-
- /**
- * This method will check with PackageManager to determine if the package provided should
- * be visible to the {@link Binder#getCallingUid()}.
- *
- * NOTE: This must not be called while synchronized on {@code this} to avoid dead locks
- */
- private boolean filterAppAccessUnlocked(String packageName, int userId) {
- final int callingUid = Binder.getCallingUid();
- return LocalServices.getService(PackageManagerInternal.class)
- .filterAppAccess(packageName, callingUid, userId);
- }
-
- @Override
- public int noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable String message) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- return AppOpsManager.MODE_ERRORED;
- }
-
- String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
- return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
- Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
- }
-
- @Override
- public int noteOperationUnchecked(int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, int proxyUid, String proxyPackageName,
- @Nullable String proxyAttributionTag, @OpFlags int flags) {
- PackageVerificationResult pvr;
- try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
- if (!pvr.isAttributionTagValid) {
- attributionTag = null;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "noteOperation", e);
- return AppOpsManager.MODE_ERRORED;
- }
-
- synchronized (this) {
- final Ops ops = getOpsLocked(uid, packageName, attributionTag,
- pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
- if (ops == null) {
- scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- AppOpsManager.MODE_IGNORED);
- if (DEBUG) {
- Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
- + " package " + packageName + "flags: "
- + AppOpsManager.flagsToString(flags));
- }
- return AppOpsManager.MODE_ERRORED;
- }
- final Op op = getOpLocked(ops, code, uid, true);
- final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
- if (attributedOp.isRunning()) {
- Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
- + code + " startTime of in progress event="
- + attributedOp.mInProgressEvents.valueAt(0).getStartTime());
- }
-
- final int switchCode = AppOpsManager.opToSwitch(code);
- final UidState uidState = ops.uidState;
- if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, false)) {
- attributedOp.rejected(uidState.getState(), flags);
- scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- AppOpsManager.MODE_IGNORED);
- return AppOpsManager.MODE_IGNORED;
- }
- // 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.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 "
- + packageName + " flags: " + AppOpsManager.flagsToString(flags));
- }
- attributedOp.rejected(uidState.getState(), flags);
- scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- uidMode);
- return uidMode;
- }
- } else {
- final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
- : op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
- if (mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) {
- Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package "
- + packageName + " flags: " + AppOpsManager.flagsToString(flags));
- }
- attributedOp.rejected(uidState.getState(), flags);
- scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- mode);
- return mode;
- }
- }
- if (DEBUG) {
- Slog.d(TAG,
- "noteOperation: allowing code " + code + " uid " + uid + " package "
- + packageName + (attributionTag == null ? ""
- : "." + attributionTag) + " flags: "
- + AppOpsManager.flagsToString(flags));
- }
- scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- AppOpsManager.MODE_ALLOWED);
- attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
- uidState.getState(),
- flags);
-
- return AppOpsManager.MODE_ALLOWED;
- }
- }
-
- @Override
- public boolean isAttributionTagValid(int uid, @NonNull String packageName,
- @Nullable String attributionTag,
- @Nullable String proxyPackageName) {
- try {
- return verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName)
- .isAttributionTagValid;
- } catch (SecurityException ignored) {
- // We don't want to throw, this exception will be handled in the (c/n/s)Operation calls
- // when they need the bypass object.
- return false;
- }
- }
-
- // TODO moltmann: Allow watching for attribution ops
- @Override
- public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
- int watchedUid = Process.INVALID_UID;
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
- != PackageManager.PERMISSION_GRANTED) {
- watchedUid = callingUid;
- }
- if (ops != null) {
- Preconditions.checkArrayElementsInRange(ops, 0,
- AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops));
- }
- if (callback == null) {
- return;
- }
- synchronized (this) {
- SparseArray<ActiveCallback> callbacks = mActiveWatchers.get(callback.asBinder());
- if (callbacks == null) {
- callbacks = new SparseArray<>();
- mActiveWatchers.put(callback.asBinder(), callbacks);
- }
- final ActiveCallback activeCallback = new ActiveCallback(callback, watchedUid,
- callingUid, callingPid);
- for (int op : ops) {
- callbacks.put(op, activeCallback);
- }
- }
- }
-
- @Override
- public void stopWatchingActive(IAppOpsActiveCallback callback) {
- if (callback == null) {
- return;
- }
- synchronized (this) {
- final SparseArray<ActiveCallback> activeCallbacks =
- mActiveWatchers.remove(callback.asBinder());
- if (activeCallbacks == null) {
- return;
- }
- final int callbackCount = activeCallbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- activeCallbacks.valueAt(i).destroy();
- }
- }
- }
-
- @Override
- public void startWatchingStarted(int[] ops, @NonNull IAppOpsStartedCallback callback) {
- int watchedUid = Process.INVALID_UID;
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
- != PackageManager.PERMISSION_GRANTED) {
- watchedUid = callingUid;
- }
-
- Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
- Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
- "Invalid op code in: " + Arrays.toString(ops));
- Objects.requireNonNull(callback, "Callback cannot be null");
-
- synchronized (this) {
- SparseArray<StartedCallback> callbacks = mStartedWatchers.get(callback.asBinder());
- if (callbacks == null) {
- callbacks = new SparseArray<>();
- mStartedWatchers.put(callback.asBinder(), callbacks);
- }
-
- final StartedCallback startedCallback = new StartedCallback(callback, watchedUid,
- callingUid, callingPid);
- for (int op : ops) {
- callbacks.put(op, startedCallback);
- }
- }
- }
-
- @Override
- public void stopWatchingStarted(IAppOpsStartedCallback callback) {
- Objects.requireNonNull(callback, "Callback cannot be null");
-
- synchronized (this) {
- final SparseArray<StartedCallback> startedCallbacks =
- mStartedWatchers.remove(callback.asBinder());
- if (startedCallbacks == null) {
- return;
- }
-
- final int callbackCount = startedCallbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- startedCallbacks.valueAt(i).destroy();
- }
- }
- }
-
- @Override
- public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
- int watchedUid = Process.INVALID_UID;
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
- != PackageManager.PERMISSION_GRANTED) {
- watchedUid = callingUid;
- }
- Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
- Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
- "Invalid op code in: " + Arrays.toString(ops));
- Objects.requireNonNull(callback, "Callback cannot be null");
- synchronized (this) {
- SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
- if (callbacks == null) {
- callbacks = new SparseArray<>();
- mNotedWatchers.put(callback.asBinder(), callbacks);
- }
- final NotedCallback notedCallback = new NotedCallback(callback, watchedUid,
- callingUid, callingPid);
- for (int op : ops) {
- callbacks.put(op, notedCallback);
- }
- }
- }
-
- @Override
- public void stopWatchingNoted(IAppOpsNotedCallback callback) {
- Objects.requireNonNull(callback, "Callback cannot be null");
- synchronized (this) {
- final SparseArray<NotedCallback> notedCallbacks =
- mNotedWatchers.remove(callback.asBinder());
- if (notedCallbacks == null) {
- return;
- }
- final int callbackCount = notedCallbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- notedCallbacks.valueAt(i).destroy();
- }
- }
- }
-
- @Override
- public int startOperation(@NonNull IBinder clientId, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
- boolean startIfModeDefault, @NonNull String message,
- @AttributionFlags int attributionFlags, int attributionChainId) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- return AppOpsManager.MODE_ERRORED;
- }
-
- String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
-
- // As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution
- // purposes and not as a check, also make sure that the caller is allowed to access
- // the data gated by OP_RECORD_AUDIO.
- //
- // TODO: Revert this change before Android 12.
- if (code == OP_RECORD_AUDIO_HOTWORD || code == OP_RECEIVE_AMBIENT_TRIGGER_AUDIO) {
- int result = checkOperation(OP_RECORD_AUDIO, uid, packageName, null, false);
- if (result != AppOpsManager.MODE_ALLOWED) {
- return result;
- }
- }
- return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
- Process.INVALID_UID, null, null, OP_FLAG_SELF, startIfModeDefault,
- attributionFlags, attributionChainId, /*dryRun*/ false);
- }
-
- private boolean shouldStartForMode(int mode, boolean startIfModeDefault) {
- return (mode == MODE_ALLOWED || (mode == MODE_DEFAULT && startIfModeDefault));
- }
-
- @Override
- public int startOperationUnchecked(IBinder clientId, int code, int uid,
- @NonNull String packageName, @Nullable String attributionTag, int proxyUid,
- String proxyPackageName, @Nullable String proxyAttributionTag, @OpFlags int flags,
- boolean startIfModeDefault, @AttributionFlags int attributionFlags,
- int attributionChainId, boolean dryRun) {
- PackageVerificationResult pvr;
- try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
- if (!pvr.isAttributionTagValid) {
- attributionTag = null;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "startOperation", e);
- return AppOpsManager.MODE_ERRORED;
- }
-
- boolean isRestricted;
- int startType = START_TYPE_FAILED;
- synchronized (this) {
- final Ops ops = getOpsLocked(uid, packageName, attributionTag,
- pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
- if (ops == null) {
- if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
- flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags,
- attributionChainId);
- }
- if (DEBUG) {
- Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
- + " package " + packageName + " flags: "
- + AppOpsManager.flagsToString(flags));
- }
- return AppOpsManager.MODE_ERRORED;
- }
- final Op op = getOpLocked(ops, code, uid, true);
- final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
- final UidState uidState = ops.uidState;
- isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
- false);
- 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.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 "
- + switchCode + " (" + code + ") uid " + uid + " package "
- + packageName + " flags: " + AppOpsManager.flagsToString(flags));
- }
- if (!dryRun) {
- attributedOp.rejected(uidState.getState(), flags);
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
- flags, uidMode, startType, attributionFlags, attributionChainId);
- }
- return uidMode;
- }
- } else {
- final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
- : op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
- if (!shouldStartForMode(mode, startIfModeDefault)) {
- if (DEBUG) {
- Slog.d(TAG, "startOperation: reject #" + mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package "
- + packageName + " flags: " + AppOpsManager.flagsToString(flags));
- }
- if (!dryRun) {
- attributedOp.rejected(uidState.getState(), flags);
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
- flags, mode, startType, attributionFlags, attributionChainId);
- }
- return mode;
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName + " restricted: " + isRestricted
- + " flags: " + AppOpsManager.flagsToString(flags));
- }
- if (!dryRun) {
- try {
- if (isRestricted) {
- attributedOp.createPaused(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState.getState(), flags,
- attributionFlags, attributionChainId);
- } else {
- attributedOp.started(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState.getState(), flags,
- attributionFlags, attributionChainId);
- startType = START_TYPE_STARTED;
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags,
- attributionChainId);
- }
- }
-
- // Possible bug? The raw mode could have been MODE_DEFAULT to reach here.
- return isRestricted ? MODE_IGNORED : MODE_ALLOWED;
- }
-
- @Override
- public void finishOperation(IBinder clientId, int code, int uid, String packageName,
- String attributionTag) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- return;
- }
-
- String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return;
- }
-
- finishOperationUnchecked(clientId, code, uid, resolvedPackageName, attributionTag);
- }
-
- @Override
- public void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName,
- String attributionTag) {
- PackageVerificationResult pvr;
- try {
- pvr = verifyAndGetBypass(uid, packageName, attributionTag);
- if (!pvr.isAttributionTagValid) {
- attributionTag = null;
- }
- } catch (SecurityException e) {
- Slog.e(TAG, "Cannot finishOperation", e);
- return;
- }
-
- synchronized (this) {
- Op op = getOpLocked(code, uid, packageName, attributionTag, pvr.isAttributionTagValid,
- pvr.bypass, /* edit */ true);
- if (op == null) {
- Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "("
- + attributionTag + ") op=" + AppOpsManager.opToName(code));
- return;
- }
- final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
- if (attributedOp == null) {
- Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
- + attributionTag + ") op=" + AppOpsManager.opToName(code));
- return;
- }
-
- if (attributedOp.isRunning() || attributedOp.isPaused()) {
- attributedOp.finished(clientId);
- } else {
- Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "("
- + attributionTag + ") op=" + AppOpsManager.opToName(code));
- }
- }
- }
-
- void scheduleOpActiveChangedIfNeededLocked(int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, boolean active,
- @AttributionFlags int attributionFlags, int attributionChainId) {
- ArraySet<ActiveCallback> dispatchedCallbacks = null;
- final int callbackListCount = mActiveWatchers.size();
- for (int i = 0; i < callbackListCount; i++) {
- final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i);
- ActiveCallback callback = callbacks.get(code);
- if (callback != null) {
- if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
- continue;
- }
- if (dispatchedCallbacks == null) {
- dispatchedCallbacks = new ArraySet<>();
- }
- dispatchedCallbacks.add(callback);
- }
- }
- if (dispatchedCallbacks == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpActiveChanged,
- this, dispatchedCallbacks, code, uid, packageName, attributionTag, active,
- attributionFlags, attributionChainId));
- }
-
- private void notifyOpActiveChanged(ArraySet<ActiveCallback> callbacks,
- int code, int uid, @NonNull String packageName, @Nullable String attributionTag,
- boolean active, @AttributionFlags int attributionFlags, int attributionChainId) {
- // 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 {
- final int callbackCount = callbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- final ActiveCallback callback = callbacks.valueAt(i);
- try {
- if (shouldIgnoreCallback(code, callback.mCallingPid, callback.mCallingUid)) {
- continue;
- }
- callback.mCallback.opActiveChanged(code, uid, packageName, attributionTag,
- active, attributionFlags, attributionChainId);
- } catch (RemoteException e) {
- /* do nothing */
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
- String attributionTag, @OpFlags int flags, @Mode int result,
- @AppOpsManager.OnOpStartedListener.StartedType int startedType,
- @AttributionFlags int attributionFlags, int attributionChainId) {
- ArraySet<StartedCallback> dispatchedCallbacks = null;
- final int callbackListCount = mStartedWatchers.size();
- for (int i = 0; i < callbackListCount; i++) {
- final SparseArray<StartedCallback> callbacks = mStartedWatchers.valueAt(i);
-
- StartedCallback callback = callbacks.get(code);
- if (callback != null) {
- if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
- continue;
- }
-
- if (dispatchedCallbacks == null) {
- dispatchedCallbacks = new ArraySet<>();
- }
- dispatchedCallbacks.add(callback);
- }
- }
-
- if (dispatchedCallbacks == null) {
- return;
- }
-
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpStarted,
- this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags,
- result, startedType, attributionFlags, attributionChainId));
- }
-
- private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
- int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
- @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType,
- @AttributionFlags int attributionFlags, int attributionChainId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final int callbackCount = callbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- final StartedCallback callback = callbacks.valueAt(i);
- try {
- if (shouldIgnoreCallback(code, callback.mCallingPid, callback.mCallingUid)) {
- continue;
- }
- callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags,
- result, startedType, attributionFlags, attributionChainId);
- } catch (RemoteException e) {
- /* do nothing */
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
- String attributionTag, @OpFlags int flags, @Mode int result) {
- ArraySet<NotedCallback> dispatchedCallbacks = null;
- final int callbackListCount = mNotedWatchers.size();
- for (int i = 0; i < callbackListCount; i++) {
- final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i);
- final NotedCallback callback = callbacks.get(code);
- if (callback != null) {
- if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
- continue;
- }
- if (dispatchedCallbacks == null) {
- dispatchedCallbacks = new ArraySet<>();
- }
- dispatchedCallbacks.add(callback);
- }
- }
- if (dispatchedCallbacks == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyOpChecked,
- this, dispatchedCallbacks, code, uid, packageName, attributionTag, flags,
- result));
- }
-
- private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
- int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
- @Mode int result) {
- // There are features watching for checks in our process. The callbacks in
- // these features may require permissions our remote caller does not have.
- final long identity = Binder.clearCallingIdentity();
- try {
- final int callbackCount = callbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- final NotedCallback callback = callbacks.valueAt(i);
- try {
- if (shouldIgnoreCallback(code, callback.mCallingPid, callback.mCallingUid)) {
- continue;
- }
- callback.mCallback.opNoted(code, uid, packageName, attributionTag, flags,
- result);
- } catch (RemoteException e) {
- /* do nothing */
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void verifyIncomingUid(int uid) {
- if (uid == Binder.getCallingUid()) {
- return;
- }
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
- }
- mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
-
- private boolean shouldIgnoreCallback(int op, int watcherPid, int watcherUid) {
- // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission,
- // as watcher should not use this to signal if the value is changed.
- return opRestrictsRead(op) && mContext.checkPermission(Manifest.permission.MANAGE_APPOPS,
- watcherPid, watcherUid) != PackageManager.PERMISSION_GRANTED;
- }
-
- private void verifyIncomingOp(int op) {
- if (op >= 0 && op < AppOpsManager._NUM_OP) {
- // Enforce manage appops permission if it's a restricted read op.
- if (opRestrictsRead(op)) {
- mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
- Binder.getCallingPid(), Binder.getCallingUid(), "verifyIncomingOp");
- }
- return;
- }
- throw new IllegalArgumentException("Bad operation #" + op);
- }
-
- private boolean isIncomingPackageValid(@Nullable String packageName, @UserIdInt int userId) {
- final int callingUid = Binder.getCallingUid();
- // Handle the special UIDs that don't have actual packages (audioserver, cameraserver, etc).
- if (packageName == null || isSpecialPackage(callingUid, packageName)) {
- return true;
- }
-
- // If the package doesn't exist, #verifyAndGetBypass would throw a SecurityException in
- // the end. Although that exception would be caught and return, we could make it return
- // early.
- if (!isPackageExisted(packageName)) {
- return false;
- }
-
- if (getPackageManagerInternal().filterAppAccess(packageName, callingUid, userId)) {
- Slog.w(TAG, packageName + " not found from " + callingUid);
- return false;
- }
-
- return true;
- }
-
- private boolean isSpecialPackage(int callingUid, @Nullable String packageName) {
- final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid, packageName);
- return callingUid == Process.SYSTEM_UID
- || resolveUid(resolvedPackage) != Process.INVALID_UID;
- }
-
- private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
- UidState uidState = mUidStates.get(uid);
- if (uidState == null) {
- if (!edit) {
- return null;
- }
- uidState = new UidState(uid);
- mUidStates.put(uid, uidState);
- }
-
- return uidState;
- }
-
- @Override
- public void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
- synchronized (this) {
- getUidStateTracker().updateAppWidgetVisibility(uidPackageNames, visible);
- }
- }
-
- /**
- * @return {@link PackageManagerInternal}
- */
- private @NonNull PackageManagerInternal getPackageManagerInternal() {
- if (mPackageManagerInternal == null) {
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- }
-
- return mPackageManagerInternal;
- }
-
- @Override
- public void verifyPackage(int uid, String packageName) {
- verifyAndGetBypass(uid, packageName, null);
- }
-
- /**
- * Create a restriction description matching the properties of the package.
- *
- * @param pkg The package to create the restriction description for
- * @return The restriction matching the package
- */
- private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
- return new RestrictionBypass(pkg.getUid() == Process.SYSTEM_UID, pkg.isPrivileged(),
- mContext.checkPermission(android.Manifest.permission
- .EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
- == PackageManager.PERMISSION_GRANTED);
- }
-
- /**
- * @see #verifyAndGetBypass(int, String, String, String)
- */
- private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag) {
- return verifyAndGetBypass(uid, packageName, attributionTag, null);
- }
-
- /**
- * Verify that package belongs to uid and return the {@link RestrictionBypass bypass
- * description} for the package, along with a boolean indicating whether the attribution tag is
- * valid.
- *
- * @param uid The uid the package belongs to
- * @param packageName The package the might belong to the uid
- * @param attributionTag attribution tag or {@code null} if no need to verify
- * @param proxyPackageName The proxy package, from which the attribution tag is to be pulled
- * @return PackageVerificationResult containing {@link RestrictionBypass} and whether the
- * attribution tag is valid
- */
- private @NonNull PackageVerificationResult verifyAndGetBypass(int uid, String packageName,
- @Nullable String attributionTag, @Nullable String proxyPackageName) {
- if (uid == Process.ROOT_UID) {
- // For backwards compatibility, don't check package name for root UID.
- return new PackageVerificationResult(null,
- /* isAttributionTagValid */ true);
- }
- if (Process.isSdkSandboxUid(uid)) {
- // SDK sandbox processes run in their own UID range, but their associated
- // UID for checks should always be the UID of the package implementing SDK sandbox
- // service.
- // TODO: We will need to modify the callers of this function instead, so
- // modifications and checks against the app ops state are done with the
- // correct UID.
- try {
- final PackageManager pm = mContext.getPackageManager();
- final String supplementalPackageName = pm.getSdkSandboxPackageName();
- if (Objects.equals(packageName, supplementalPackageName)) {
- uid = pm.getPackageUidAsUser(supplementalPackageName,
- PackageManager.PackageInfoFlags.of(0), UserHandle.getUserId(uid));
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Shouldn't happen for the supplemental package
- e.printStackTrace();
- }
- }
-
-
- // Do not check if uid/packageName/attributionTag is already known.
- synchronized (this) {
- UidState uidState = mUidStates.get(uid);
- if (uidState != null && uidState.pkgOps != null) {
- Ops ops = uidState.pkgOps.get(packageName);
-
- if (ops != null && (attributionTag == null || ops.knownAttributionTags.contains(
- attributionTag)) && ops.bypass != null) {
- return new PackageVerificationResult(ops.bypass,
- ops.validAttributionTags.contains(attributionTag));
- }
- }
- }
-
- int callingUid = Binder.getCallingUid();
-
- // Allow any attribution tag for resolvable uids
- int pkgUid;
- if (Objects.equals(packageName, "com.android.shell")) {
- // Special case for the shell which is a package but should be able
- // to bypass app attribution tag restrictions.
- pkgUid = Process.SHELL_UID;
- } else {
- pkgUid = resolveUid(packageName);
- }
- if (pkgUid != Process.INVALID_UID) {
- if (pkgUid != UserHandle.getAppId(uid)) {
- Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
- + "Package \"" + packageName + "\" does not belong to uid " + uid + ".");
- String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
- throw new SecurityException("Specified package \"" + packageName + "\" under uid "
- + UserHandle.getAppId(uid) + otherUidMessage);
- }
- return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
- /* isAttributionTagValid */ true);
- }
-
- int userId = UserHandle.getUserId(uid);
- RestrictionBypass bypass = null;
- boolean isAttributionTagValid = false;
-
- final long ident = Binder.clearCallingIdentity();
- try {
- PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
- AndroidPackage pkg = pmInt.getPackage(packageName);
- if (pkg != null) {
- isAttributionTagValid = isAttributionInPackage(pkg, attributionTag);
- pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
- bypass = getBypassforPackage(pkg);
- }
- if (!isAttributionTagValid) {
- AndroidPackage proxyPkg = proxyPackageName != null
- ? pmInt.getPackage(proxyPackageName) : null;
- // Re-check in proxy.
- isAttributionTagValid = isAttributionInPackage(proxyPkg, attributionTag);
- String msg;
- if (pkg != null && isAttributionTagValid) {
- msg = "attributionTag " + attributionTag + " declared in manifest of the proxy"
- + " package " + proxyPackageName + ", this is not advised";
- } else if (pkg != null) {
- msg = "attributionTag " + attributionTag + " not declared in manifest of "
- + packageName;
- } else {
- msg = "package " + packageName + " not found, can't check for "
- + "attributionTag " + attributionTag;
- }
-
- try {
- if (!mPlatformCompat.isChangeEnabledByPackageName(
- SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
- userId) || !mPlatformCompat.isChangeEnabledByUid(
- SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE,
- callingUid)) {
- // Do not override tags if overriding is not enabled for this package
- isAttributionTagValid = true;
- }
- Slog.e(TAG, msg);
- } catch (RemoteException neverHappens) {
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (pkgUid != uid) {
- Slog.e(TAG, "Bad call made by uid " + callingUid + ". "
- + "Package \"" + packageName + "\" does not belong to uid " + uid + ".");
- String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
- throw new SecurityException("Specified package \"" + packageName + "\" under uid " + uid
- + otherUidMessage);
- }
-
- return new PackageVerificationResult(bypass, isAttributionTagValid);
- }
-
- private boolean isAttributionInPackage(@Nullable AndroidPackage pkg,
- @Nullable String attributionTag) {
- if (pkg == null) {
- return false;
- } else if (attributionTag == null) {
- return true;
- }
- if (pkg.getAttributions() != null) {
- int numAttributions = pkg.getAttributions().size();
- for (int i = 0; i < numAttributions; i++) {
- if (pkg.getAttributions().get(i).getTag().equals(attributionTag)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Get (and potentially create) ops.
- *
- * @param uid The uid the package belongs to
- * @param packageName The name of the package
- * @param attributionTag attribution tag
- * @param isAttributionTagValid whether the given attribution tag is valid
- * @param bypass When to bypass certain op restrictions (can be null if edit
- * == false)
- * @param edit If an ops does not exist, create the ops?
- * @return The ops
- */
- private Ops getOpsLocked(int uid, String packageName, @Nullable String attributionTag,
- boolean isAttributionTagValid, @Nullable RestrictionBypass bypass, boolean edit) {
- UidState uidState = getUidStateLocked(uid, edit);
- if (uidState == null) {
- return null;
- }
-
- if (uidState.pkgOps == null) {
- if (!edit) {
- return null;
- }
- uidState.pkgOps = new ArrayMap<>();
- }
-
- Ops ops = uidState.pkgOps.get(packageName);
- if (ops == null) {
- if (!edit) {
- return null;
- }
- ops = new Ops(packageName, uidState);
- uidState.pkgOps.put(packageName, ops);
- }
-
- if (edit) {
- if (bypass != null) {
- ops.bypass = bypass;
- }
-
- if (attributionTag != null) {
- ops.knownAttributionTags.add(attributionTag);
- if (isAttributionTagValid) {
- ops.validAttributionTags.add(attributionTag);
- } else {
- ops.validAttributionTags.remove(attributionTag);
- }
- }
- }
-
- return ops;
- }
-
- @Override
- public void scheduleWriteLocked() {
- if (!mWriteScheduled) {
- mWriteScheduled = true;
- mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
- }
- }
-
- @Override
- public void scheduleFastWriteLocked() {
- if (!mFastWriteScheduled) {
- mWriteScheduled = true;
- mFastWriteScheduled = true;
- mHandler.removeCallbacks(mWriteRunner);
- mHandler.postDelayed(mWriteRunner, 10 * 1000);
- }
- }
-
- /**
- * Get the state of an op for a uid.
- *
- * @param code The code of the op
- * @param uid The uid the of the package
- * @param packageName The package name for which to get the state for
- * @param attributionTag The attribution tag
- * @param isAttributionTagValid Whether the given attribution tag is valid
- * @param bypass When to bypass certain op restrictions (can be null if edit
- * == false)
- * @param edit Iff {@code true} create the {@link Op} object if not yet created
- * @return The {@link Op state} of the op
- */
- private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, boolean isAttributionTagValid,
- @Nullable RestrictionBypass bypass, boolean edit) {
- Ops ops = getOpsLocked(uid, packageName, attributionTag, isAttributionTagValid, bypass,
- edit);
- if (ops == null) {
- return null;
- }
- return getOpLocked(ops, code, uid, edit);
- }
-
- private Op getOpLocked(Ops ops, int code, int uid, boolean edit) {
- Op op = ops.get(code);
- if (op == null) {
- if (!edit) {
- return null;
- }
- op = new Op(ops.uidState, ops.packageName, code, uid);
- ops.put(code, op);
- }
- if (edit) {
- scheduleWriteLocked();
- }
- return op;
- }
-
- private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) {
- if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) {
- return false;
- }
- final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
- }
-
- private boolean isOpRestrictedLocked(int uid, int code, String packageName,
- String attributionTag, @Nullable RestrictionBypass appBypass, boolean isCheckOp) {
- int restrictionSetCount = mOpGlobalRestrictions.size();
-
- for (int i = 0; i < restrictionSetCount; i++) {
- ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i);
- if (restrictionState.hasRestriction(code)) {
- return true;
- }
- }
-
- int userHandle = UserHandle.getUserId(uid);
- restrictionSetCount = mOpUserRestrictions.size();
-
- for (int i = 0; i < restrictionSetCount; i++) {
- // For each client, check that the given op is not restricted, or that the given
- // package is exempt from the restriction.
- ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
- if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle,
- isCheckOp)) {
- RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
- if (opBypass != null) {
- // If we are the system, bypass user restrictions for certain codes
- synchronized (this) {
- if (opBypass.isSystemUid && appBypass != null && appBypass.isSystemUid) {
- return false;
- }
- if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
- return false;
- }
- if (opBypass.isRecordAudioRestrictionExcept && appBypass != null
- && appBypass.isRecordAudioRestrictionExcept) {
- return false;
- }
- }
- }
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void readState() {
- synchronized (mFile) {
- synchronized (this) {
- FileInputStream stream;
- try {
- stream = mFile.openRead();
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "No existing app ops " + mFile.getBaseFile() + "; starting empty");
- return;
- }
- boolean success = false;
- mUidStates.clear();
- mAppOpsServiceInterface.clearAllModes();
- try {
- TypedXmlPullParser parser = Xml.resolvePullParser(stream);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- // Parse next until we reach the start or end
- }
-
- if (type != XmlPullParser.START_TAG) {
- throw new IllegalStateException("no start tag found");
- }
-
- mVersionAtBoot = parser.getAttributeInt(null, "v", NO_VERSION);
-
- int outerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("pkg")) {
- readPackage(parser);
- } else if (tagName.equals("uid")) {
- readUidOps(parser);
- } else {
- Slog.w(TAG, "Unknown element under <app-ops>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- success = true;
- } catch (IllegalStateException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (NullPointerException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (IOException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (IndexOutOfBoundsException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } finally {
- if (!success) {
- mUidStates.clear();
- mAppOpsServiceInterface.clearAllModes();
- }
- try {
- stream.close();
- } catch (IOException e) {
- }
- }
- }
- }
- }
-
- @VisibleForTesting
- @GuardedBy("this")
- void upgradeRunAnyInBackgroundLocked() {
- for (int i = 0; i < mUidStates.size(); i++) {
- final UidState uidState = mUidStates.valueAt(i);
- if (uidState == null) {
- continue;
- }
- SparseIntArray opModes = uidState.getNonDefaultUidModes();
- if (opModes != null) {
- final int idx = opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
- if (idx >= 0) {
- uidState.setUidMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- opModes.valueAt(idx));
- }
- }
- if (uidState.pkgOps == null) {
- continue;
- }
- boolean changed = false;
- for (int j = 0; j < uidState.pkgOps.size(); j++) {
- Ops ops = uidState.pkgOps.valueAt(j);
- if (ops != null) {
- final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
- 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.setMode(op.getMode());
- ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
- changed = true;
- }
- }
- }
- if (changed) {
- uidState.evalForegroundOps();
- }
- }
- }
-
- /**
- * The interpretation of the default mode - MODE_DEFAULT - for OP_SCHEDULE_EXACT_ALARM is
- * changing. Simultaneously, we want to change this op's mode from MODE_DEFAULT to MODE_ALLOWED
- * for already installed apps. For newer apps, it will stay as MODE_DEFAULT.
- */
- @VisibleForTesting
- @GuardedBy("this")
- void upgradeScheduleExactAlarmLocked() {
- final PermissionManagerServiceInternal pmsi = LocalServices.getService(
- PermissionManagerServiceInternal.class);
- final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
- final PackageManagerInternal pmi = getPackageManagerInternal();
-
- final String[] packagesDeclaringPermission = pmsi.getAppOpPermissionPackages(
- AppOpsManager.opToPermission(OP_SCHEDULE_EXACT_ALARM));
- final int[] userIds = umi.getUserIds();
-
- for (final String pkg : packagesDeclaringPermission) {
- for (int userId : userIds) {
- final int uid = pmi.getPackageUid(pkg, 0, userId);
-
- UidState uidState = mUidStates.get(uid);
- if (uidState == null) {
- uidState = new UidState(uid);
- mUidStates.put(uid, uidState);
- }
- final int oldMode = uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM);
- if (oldMode == AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM)) {
- uidState.setUidMode(OP_SCHEDULE_EXACT_ALARM, MODE_ALLOWED);
- }
- }
- // This appop is meant to be controlled at a uid level. So we leave package modes as
- // they are.
- }
- }
-
- @GuardedBy("this")
- private void upgradeLocked(int oldVersion) {
- if (oldVersion == NO_FILE_VERSION || oldVersion >= CURRENT_VERSION) {
- return;
- }
- Slog.d(TAG, "Upgrading app-ops xml from version " + oldVersion + " to " + CURRENT_VERSION);
- switch (oldVersion) {
- case NO_VERSION:
- upgradeRunAnyInBackgroundLocked();
- // fall through
- case 1:
- upgradeScheduleExactAlarmLocked();
- // fall through
- case 2:
- // for future upgrades
- }
- scheduleFastWriteLocked();
- }
-
- private void readUidOps(TypedXmlPullParser parser) throws NumberFormatException,
- XmlPullParserException, IOException {
- final int uid = parser.getAttributeInt(null, "n");
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("op")) {
- final int code = parser.getAttributeInt(null, "n");
- final int mode = parser.getAttributeInt(null, "m");
- setUidMode(code, uid, mode, null);
- } else {
- Slog.w(TAG, "Unknown element under <uid-ops>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
- private void readPackage(TypedXmlPullParser parser)
- throws NumberFormatException, XmlPullParserException, IOException {
- String pkgName = parser.getAttributeValue(null, "n");
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("uid")) {
- readUid(parser, pkgName);
- } else {
- Slog.w(TAG, "Unknown element under <pkg>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
- private void readUid(TypedXmlPullParser parser, String pkgName)
- throws NumberFormatException, XmlPullParserException, IOException {
- int uid = parser.getAttributeInt(null, "n");
- final UidState uidState = getUidStateLocked(uid, true);
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- String tagName = parser.getName();
- if (tagName.equals("op")) {
- readOp(parser, uidState, pkgName);
- } else {
- Slog.w(TAG, "Unknown element under <pkg>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- uidState.evalForegroundOps();
- }
-
- private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent,
- @Nullable String attribution)
- throws NumberFormatException, IOException, XmlPullParserException {
- final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution);
-
- final long key = parser.getAttributeLong(null, "n");
- final int uidState = extractUidStateFromKey(key);
- final int opFlags = extractFlagsFromKey(key);
-
- final long accessTime = parser.getAttributeLong(null, "t", 0);
- final long rejectTime = parser.getAttributeLong(null, "r", 0);
- final long accessDuration = parser.getAttributeLong(null, "d", -1);
- final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
- final int proxyUid = parser.getAttributeInt(null, "pu", Process.INVALID_UID);
- final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc");
-
- if (accessTime > 0) {
- attributedOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg,
- proxyAttributionTag, uidState, opFlags);
- }
- if (rejectTime > 0) {
- attributedOp.rejected(rejectTime, uidState, opFlags);
- }
- }
-
- private void readOp(TypedXmlPullParser parser,
- @NonNull UidState uidState, @NonNull String pkgName)
- throws NumberFormatException, XmlPullParserException, IOException {
- int opCode = parser.getAttributeInt(null, "n");
- Op op = new Op(uidState, pkgName, opCode, uidState.uid);
-
- final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
- op.setMode(mode);
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- String tagName = parser.getName();
- if (tagName.equals("st")) {
- readAttributionOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
- } else {
- Slog.w(TAG, "Unknown element under <op>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- if (uidState.pkgOps == null) {
- uidState.pkgOps = new ArrayMap<>();
- }
- Ops ops = uidState.pkgOps.get(pkgName);
- if (ops == null) {
- ops = new Ops(pkgName, uidState);
- uidState.pkgOps.put(pkgName, ops);
- }
- ops.put(op.op, op);
- }
-
- @Override
- public void writeState() {
- synchronized (mFile) {
- FileOutputStream stream;
- try {
- stream = mFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to write state: " + e);
- return;
- }
-
- List<AppOpsManager.PackageOps> allOps = getPackagesForOps(null);
-
- try {
- TypedXmlSerializer out = Xml.resolveSerializer(stream);
- out.startDocument(null, true);
- out.startTag(null, "app-ops");
- out.attributeInt(null, "v", CURRENT_VERSION);
-
- SparseArray<SparseIntArray> uidStatesClone;
- synchronized (this) {
- uidStatesClone = new SparseArray<>(mUidStates.size());
-
- final int uidStateCount = mUidStates.size();
- for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
- UidState uidState = mUidStates.valueAt(uidStateNum);
- int uid = mUidStates.keyAt(uidStateNum);
-
- SparseIntArray opModes = uidState.getNonDefaultUidModes();
- if (opModes != null && opModes.size() > 0) {
- uidStatesClone.put(uid, opModes);
- }
- }
- }
-
- final int uidStateCount = uidStatesClone.size();
- for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
- SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum);
- if (opModes != null && opModes.size() > 0) {
- out.startTag(null, "uid");
- out.attributeInt(null, "n", uidStatesClone.keyAt(uidStateNum));
- final int opCount = opModes.size();
- for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
- final int op = opModes.keyAt(opCountNum);
- final int mode = opModes.valueAt(opCountNum);
- out.startTag(null, "op");
- out.attributeInt(null, "n", op);
- out.attributeInt(null, "m", mode);
- out.endTag(null, "op");
- }
- out.endTag(null, "uid");
- }
- }
-
- if (allOps != null) {
- String lastPkg = null;
- for (int i = 0; i < allOps.size(); i++) {
- AppOpsManager.PackageOps pkg = allOps.get(i);
- if (!Objects.equals(pkg.getPackageName(), lastPkg)) {
- if (lastPkg != null) {
- out.endTag(null, "pkg");
- }
- lastPkg = pkg.getPackageName();
- if (lastPkg != null) {
- out.startTag(null, "pkg");
- out.attribute(null, "n", lastPkg);
- }
- }
- out.startTag(null, "uid");
- out.attributeInt(null, "n", pkg.getUid());
- List<AppOpsManager.OpEntry> ops = pkg.getOps();
- for (int j = 0; j < ops.size(); j++) {
- AppOpsManager.OpEntry op = ops.get(j);
- out.startTag(null, "op");
- out.attributeInt(null, "n", op.getOp());
- if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
- out.attributeInt(null, "m", op.getMode());
- }
-
- for (String attributionTag : op.getAttributedOpEntries().keySet()) {
- final AttributedOpEntry attribution =
- op.getAttributedOpEntries().get(attributionTag);
-
- final ArraySet<Long> keys = attribution.collectKeys();
-
- final int keyCount = keys.size();
- for (int k = 0; k < keyCount; k++) {
- final long key = keys.valueAt(k);
-
- final int uidState = AppOpsManager.extractUidStateFromKey(key);
- final int flags = AppOpsManager.extractFlagsFromKey(key);
-
- final long accessTime = attribution.getLastAccessTime(uidState,
- uidState, flags);
- final long rejectTime = attribution.getLastRejectTime(uidState,
- uidState, flags);
- final long accessDuration = attribution.getLastDuration(
- uidState, uidState, flags);
- // Proxy information for rejections is not backed up
- final OpEventProxyInfo proxy = attribution.getLastProxyInfo(
- uidState, uidState, flags);
-
- if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
- && proxy == null) {
- continue;
- }
-
- String proxyPkg = null;
- String proxyAttributionTag = null;
- int proxyUid = Process.INVALID_UID;
- if (proxy != null) {
- proxyPkg = proxy.getPackageName();
- proxyAttributionTag = proxy.getAttributionTag();
- proxyUid = proxy.getUid();
- }
-
- out.startTag(null, "st");
- if (attributionTag != null) {
- out.attribute(null, "id", attributionTag);
- }
- out.attributeLong(null, "n", key);
- if (accessTime > 0) {
- out.attributeLong(null, "t", accessTime);
- }
- if (rejectTime > 0) {
- out.attributeLong(null, "r", rejectTime);
- }
- if (accessDuration > 0) {
- out.attributeLong(null, "d", accessDuration);
- }
- if (proxyPkg != null) {
- out.attribute(null, "pp", proxyPkg);
- }
- if (proxyAttributionTag != null) {
- out.attribute(null, "pc", proxyAttributionTag);
- }
- if (proxyUid >= 0) {
- out.attributeInt(null, "pu", proxyUid);
- }
- out.endTag(null, "st");
- }
- }
-
- out.endTag(null, "op");
- }
- out.endTag(null, "uid");
- }
- if (lastPkg != null) {
- out.endTag(null, "pkg");
- }
- }
-
- out.endTag(null, "app-ops");
- out.endDocument();
- mFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to write state, restoring backup.", e);
- mFile.failWrite(stream);
- }
- }
- mHistoricalRegistry.writeAndClearDiscreteHistory();
- }
-
- private void dumpHelp(PrintWriter pw) {
- pw.println("AppOps service (appops) dump options:");
- pw.println(" -h");
- pw.println(" Print this help text.");
- pw.println(" --op [OP]");
- pw.println(" Limit output to data associated with the given app op code.");
- pw.println(" --mode [MODE]");
- pw.println(" Limit output to data associated with the given app op mode.");
- pw.println(" --package [PACKAGE]");
- pw.println(" Limit output to data associated with the given package name.");
- pw.println(" --attributionTag [attributionTag]");
- pw.println(" Limit output to data associated with the given attribution tag.");
- pw.println(" --include-discrete [n]");
- pw.println(" Include discrete ops limited to n per dimension. Use zero for no limit.");
- pw.println(" --watchers");
- pw.println(" Only output the watcher sections.");
- pw.println(" --history");
- pw.println(" Only output history.");
- pw.println(" --uid-state-changes");
- pw.println(" Include logs about uid state changes.");
- }
-
- private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
- @HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now,
- @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
- final int numAttributions = op.mAttributions.size();
- for (int i = 0; i < numAttributions; i++) {
- if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(
- op.mAttributions.keyAt(i), filterAttributionTag)) {
- continue;
- }
-
- pw.print(prefix + op.mAttributions.keyAt(i) + "=[\n");
- dumpStatesLocked(pw, nowElapsed, op, op.mAttributions.keyAt(i), now, sdf, date,
- prefix + " ");
- pw.print(prefix + "]\n");
- }
- }
-
- private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
- @Nullable String attributionTag, long now, @NonNull SimpleDateFormat sdf,
- @NonNull Date date, @NonNull String prefix) {
-
- final AttributedOpEntry entry = op.createSingleAttributionEntryLocked(
- attributionTag).getAttributedOpEntries().get(attributionTag);
-
- final ArraySet<Long> keys = entry.collectKeys();
-
- final int keyCount = keys.size();
- for (int k = 0; k < keyCount; k++) {
- final long key = keys.valueAt(k);
-
- final int uidState = AppOpsManager.extractUidStateFromKey(key);
- final int flags = AppOpsManager.extractFlagsFromKey(key);
-
- final long accessTime = entry.getLastAccessTime(uidState, uidState, flags);
- final long rejectTime = entry.getLastRejectTime(uidState, uidState, flags);
- final long accessDuration = entry.getLastDuration(uidState, uidState, flags);
- final OpEventProxyInfo proxy = entry.getLastProxyInfo(uidState, uidState, flags);
-
- String proxyPkg = null;
- String proxyAttributionTag = null;
- int proxyUid = Process.INVALID_UID;
- if (proxy != null) {
- proxyPkg = proxy.getPackageName();
- proxyAttributionTag = proxy.getAttributionTag();
- proxyUid = proxy.getUid();
- }
-
- if (accessTime > 0) {
- pw.print(prefix);
- pw.print("Access: ");
- pw.print(AppOpsManager.keyToString(key));
- pw.print(" ");
- date.setTime(accessTime);
- pw.print(sdf.format(date));
- pw.print(" (");
- TimeUtils.formatDuration(accessTime - now, pw);
- pw.print(")");
- if (accessDuration > 0) {
- pw.print(" duration=");
- TimeUtils.formatDuration(accessDuration, pw);
- }
- if (proxyUid >= 0) {
- pw.print(" proxy[");
- pw.print("uid=");
- pw.print(proxyUid);
- pw.print(", pkg=");
- pw.print(proxyPkg);
- pw.print(", attributionTag=");
- pw.print(proxyAttributionTag);
- pw.print("]");
- }
- pw.println();
- }
-
- if (rejectTime > 0) {
- pw.print(prefix);
- pw.print("Reject: ");
- pw.print(AppOpsManager.keyToString(key));
- date.setTime(rejectTime);
- pw.print(sdf.format(date));
- pw.print(" (");
- TimeUtils.formatDuration(rejectTime - now, pw);
- pw.print(")");
- if (proxyUid >= 0) {
- pw.print(" proxy[");
- pw.print("uid=");
- pw.print(proxyUid);
- pw.print(", pkg=");
- pw.print(proxyPkg);
- pw.print(", attributionTag=");
- pw.print(proxyAttributionTag);
- pw.print("]");
- }
- pw.println();
- }
- }
-
- final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
- if (attributedOp.isRunning()) {
- long earliestElapsedTime = Long.MAX_VALUE;
- long maxNumStarts = 0;
- int numInProgressEvents = attributedOp.mInProgressEvents.size();
- for (int i = 0; i < numInProgressEvents; i++) {
- AttributedOp.InProgressStartOpEvent event =
- attributedOp.mInProgressEvents.valueAt(i);
-
- earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
- maxNumStarts = Math.max(maxNumStarts, event.mNumUnfinishedStarts);
- }
-
- pw.print(prefix + "Running start at: ");
- TimeUtils.formatDuration(nowElapsed - earliestElapsedTime, pw);
- pw.println();
-
- if (maxNumStarts > 1) {
- pw.print(prefix + "startNesting=");
- pw.println(maxNumStarts);
- }
- }
- }
-
- @NeverCompile // Avoid size overhead of debugging code.
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
-
- int dumpOp = OP_NONE;
- String dumpPackage = null;
- String dumpAttributionTag = null;
- int dumpUid = Process.INVALID_UID;
- int dumpMode = -1;
- boolean dumpWatchers = false;
- // TODO ntmyren: Remove the dumpHistory and dumpFilter
- boolean dumpHistory = false;
- boolean includeDiscreteOps = false;
- boolean dumpUidStateChangeLogs = false;
- int nDiscreteOps = 10;
- @HistoricalOpsRequestFilter int dumpFilter = 0;
- boolean dumpAll = false;
-
- if (args != null) {
- for (int i = 0; i < args.length; i++) {
- String arg = args[i];
- if ("-h".equals(arg)) {
- dumpHelp(pw);
- return;
- } else if ("-a".equals(arg)) {
- // dump all data
- dumpAll = true;
- } else if ("--op".equals(arg)) {
- i++;
- if (i >= args.length) {
- pw.println("No argument for --op option");
- return;
- }
- dumpOp = AppOpsService.Shell.strOpToOp(args[i], pw);
- dumpFilter |= FILTER_BY_OP_NAMES;
- if (dumpOp < 0) {
- return;
- }
- } else if ("--package".equals(arg)) {
- i++;
- if (i >= args.length) {
- pw.println("No argument for --package option");
- return;
- }
- dumpPackage = args[i];
- dumpFilter |= FILTER_BY_PACKAGE_NAME;
- try {
- dumpUid = AppGlobals.getPackageManager().getPackageUid(dumpPackage,
- PackageManager.MATCH_KNOWN_PACKAGES | PackageManager.MATCH_INSTANT,
- 0);
- } catch (RemoteException e) {
- }
- if (dumpUid < 0) {
- pw.println("Unknown package: " + dumpPackage);
- return;
- }
- dumpUid = UserHandle.getAppId(dumpUid);
- dumpFilter |= FILTER_BY_UID;
- } else if ("--attributionTag".equals(arg)) {
- i++;
- if (i >= args.length) {
- pw.println("No argument for --attributionTag option");
- return;
- }
- dumpAttributionTag = args[i];
- dumpFilter |= FILTER_BY_ATTRIBUTION_TAG;
- } else if ("--mode".equals(arg)) {
- i++;
- if (i >= args.length) {
- pw.println("No argument for --mode option");
- return;
- }
- dumpMode = AppOpsService.Shell.strModeToMode(args[i], pw);
- if (dumpMode < 0) {
- return;
- }
- } else if ("--watchers".equals(arg)) {
- dumpWatchers = true;
- } else if ("--include-discrete".equals(arg)) {
- i++;
- if (i >= args.length) {
- pw.println("No argument for --include-discrete option");
- return;
- }
- try {
- nDiscreteOps = Integer.valueOf(args[i]);
- } catch (NumberFormatException e) {
- pw.println("Wrong parameter: " + args[i]);
- return;
- }
- includeDiscreteOps = true;
- } else if ("--history".equals(arg)) {
- dumpHistory = true;
- } else if (arg.length() > 0 && arg.charAt(0) == '-') {
- pw.println("Unknown option: " + arg);
- return;
- } else if ("--uid-state-changes".equals(arg)) {
- dumpUidStateChangeLogs = true;
- } else {
- pw.println("Unknown command: " + arg);
- return;
- }
- }
- }
-
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- final Date date = new Date();
- synchronized (this) {
- pw.println("Current AppOps Service state:");
- if (!dumpHistory && !dumpWatchers) {
- mConstants.dump(pw);
- }
- pw.println();
- final long now = System.currentTimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
- boolean needSep = false;
- if (dumpFilter == 0 && dumpMode < 0 && mProfileOwners != null && !dumpWatchers
- && !dumpHistory) {
- pw.println(" Profile owners:");
- for (int poi = 0; poi < mProfileOwners.size(); poi++) {
- pw.print(" User #");
- pw.print(mProfileOwners.keyAt(poi));
- pw.print(": ");
- UserHandle.formatUid(pw, mProfileOwners.valueAt(poi));
- pw.println();
- }
- pw.println();
- }
-
- if (!dumpHistory) {
- needSep |= mAppOpsServiceInterface.dumpListeners(dumpOp, dumpUid, dumpPackage, pw);
- }
-
- if (mModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) {
- boolean printedHeader = false;
- for (int i = 0; i < mModeWatchers.size(); i++) {
- final ModeCallback cb = mModeWatchers.valueAt(i);
- if (dumpPackage != null
- && dumpUid != UserHandle.getAppId(cb.getWatchingUid())) {
- continue;
- }
- needSep = true;
- if (!printedHeader) {
- pw.println(" All op mode watchers:");
- printedHeader = true;
- }
- pw.print(" ");
- pw.print(Integer.toHexString(System.identityHashCode(mModeWatchers.keyAt(i))));
- pw.print(": ");
- pw.println(cb);
- }
- }
- if (mActiveWatchers.size() > 0 && dumpMode < 0) {
- needSep = true;
- boolean printedHeader = false;
- for (int watcherNum = 0; watcherNum < mActiveWatchers.size(); watcherNum++) {
- final SparseArray<ActiveCallback> activeWatchers =
- mActiveWatchers.valueAt(watcherNum);
- if (activeWatchers.size() <= 0) {
- continue;
- }
- final ActiveCallback cb = activeWatchers.valueAt(0);
- if (dumpOp >= 0 && activeWatchers.indexOfKey(dumpOp) < 0) {
- continue;
- }
- if (dumpPackage != null
- && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
- continue;
- }
- if (!printedHeader) {
- pw.println(" All op active watchers:");
- printedHeader = true;
- }
- pw.print(" ");
- pw.print(Integer.toHexString(System.identityHashCode(
- mActiveWatchers.keyAt(watcherNum))));
- pw.println(" ->");
- pw.print(" [");
- final int opCount = activeWatchers.size();
- for (int opNum = 0; opNum < opCount; opNum++) {
- if (opNum > 0) {
- pw.print(' ');
- }
- pw.print(AppOpsManager.opToName(activeWatchers.keyAt(opNum)));
- if (opNum < opCount - 1) {
- pw.print(',');
- }
- }
- pw.println("]");
- pw.print(" ");
- pw.println(cb);
- }
- }
- if (mStartedWatchers.size() > 0 && dumpMode < 0) {
- needSep = true;
- boolean printedHeader = false;
-
- final int watchersSize = mStartedWatchers.size();
- for (int watcherNum = 0; watcherNum < watchersSize; watcherNum++) {
- final SparseArray<StartedCallback> startedWatchers =
- mStartedWatchers.valueAt(watcherNum);
- if (startedWatchers.size() <= 0) {
- continue;
- }
-
- final StartedCallback cb = startedWatchers.valueAt(0);
- if (dumpOp >= 0 && startedWatchers.indexOfKey(dumpOp) < 0) {
- continue;
- }
-
- if (dumpPackage != null
- && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
- continue;
- }
-
- if (!printedHeader) {
- pw.println(" All op started watchers:");
- printedHeader = true;
- }
-
- pw.print(" ");
- pw.print(Integer.toHexString(System.identityHashCode(
- mStartedWatchers.keyAt(watcherNum))));
- pw.println(" ->");
-
- pw.print(" [");
- final int opCount = startedWatchers.size();
- for (int opNum = 0; opNum < opCount; opNum++) {
- if (opNum > 0) {
- pw.print(' ');
- }
-
- pw.print(AppOpsManager.opToName(startedWatchers.keyAt(opNum)));
- if (opNum < opCount - 1) {
- pw.print(',');
- }
- }
- pw.println("]");
-
- pw.print(" ");
- pw.println(cb);
- }
- }
- if (mNotedWatchers.size() > 0 && dumpMode < 0) {
- needSep = true;
- boolean printedHeader = false;
- for (int watcherNum = 0; watcherNum < mNotedWatchers.size(); watcherNum++) {
- final SparseArray<NotedCallback> notedWatchers =
- mNotedWatchers.valueAt(watcherNum);
- if (notedWatchers.size() <= 0) {
- continue;
- }
- final NotedCallback cb = notedWatchers.valueAt(0);
- if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
- continue;
- }
- if (dumpPackage != null
- && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
- continue;
- }
- if (!printedHeader) {
- pw.println(" All op noted watchers:");
- printedHeader = true;
- }
- pw.print(" ");
- pw.print(Integer.toHexString(System.identityHashCode(
- mNotedWatchers.keyAt(watcherNum))));
- pw.println(" ->");
- pw.print(" [");
- final int opCount = notedWatchers.size();
- for (int opNum = 0; opNum < opCount; opNum++) {
- if (opNum > 0) {
- pw.print(' ');
- }
- pw.print(AppOpsManager.opToName(notedWatchers.keyAt(opNum)));
- if (opNum < opCount - 1) {
- pw.print(',');
- }
- }
- pw.println("]");
- pw.print(" ");
- pw.println(cb);
- }
- }
- if (needSep) {
- pw.println();
- }
- for (int i = 0; i < mUidStates.size(); i++) {
- UidState uidState = mUidStates.valueAt(i);
- 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 || (opModes != null
- && opModes.indexOfKey(dumpOp) >= 0);
- boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i);
- boolean hasMode = dumpMode < 0;
- if (!hasMode && opModes != null) {
- for (int opi = 0; !hasMode && opi < opModes.size(); opi++) {
- if (opModes.valueAt(opi) == dumpMode) {
- hasMode = true;
- }
- }
- }
- if (pkgOps != null) {
- for (int pkgi = 0;
- (!hasOp || !hasPackage || !hasMode) && pkgi < pkgOps.size();
- pkgi++) {
- Ops ops = pkgOps.valueAt(pkgi);
- if (!hasOp && ops != null && ops.indexOfKey(dumpOp) >= 0) {
- hasOp = true;
- }
- if (!hasMode) {
- for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
- if (ops.valueAt(opi).getMode() == dumpMode) {
- hasMode = true;
- }
- }
- }
- if (!hasPackage && dumpPackage.equals(ops.packageName)) {
- hasPackage = true;
- }
- }
- }
- if (uidState.foregroundOps != null && !hasOp) {
- if (uidState.foregroundOps.indexOfKey(dumpOp) > 0) {
- hasOp = true;
- }
- }
- if (!hasOp || !hasPackage || !hasMode) {
- continue;
- }
- }
-
- pw.print(" Uid ");
- UserHandle.formatUid(pw, uidState.uid);
- pw.println(":");
- uidState.dump(pw, nowElapsed);
- if (uidState.foregroundOps != null && (dumpMode < 0
- || dumpMode == AppOpsManager.MODE_FOREGROUND)) {
- pw.println(" foregroundOps:");
- for (int j = 0; j < uidState.foregroundOps.size(); j++) {
- if (dumpOp >= 0 && dumpOp != uidState.foregroundOps.keyAt(j)) {
- continue;
- }
- pw.print(" ");
- pw.print(AppOpsManager.opToName(uidState.foregroundOps.keyAt(j)));
- pw.print(": ");
- pw.println(uidState.foregroundOps.valueAt(j) ? "WATCHER" : "SILENT");
- }
- pw.print(" hasForegroundWatchers=");
- pw.println(uidState.hasForegroundWatchers);
- }
- needSep = true;
-
- if (opModes != null) {
- final int opModeCount = opModes.size();
- for (int j = 0; j < opModeCount; j++) {
- final int code = opModes.keyAt(j);
- final int mode = opModes.valueAt(j);
- if (dumpOp >= 0 && dumpOp != code) {
- continue;
- }
- if (dumpMode >= 0 && dumpMode != mode) {
- continue;
- }
- pw.print(" ");
- pw.print(AppOpsManager.opToName(code));
- pw.print(": mode=");
- pw.println(AppOpsManager.modeToName(mode));
- }
- }
-
- if (pkgOps == null) {
- continue;
- }
-
- for (int pkgi = 0; pkgi < pkgOps.size(); pkgi++) {
- final Ops ops = pkgOps.valueAt(pkgi);
- if (dumpPackage != null && !dumpPackage.equals(ops.packageName)) {
- continue;
- }
- boolean printedPackage = false;
- for (int j = 0; j < ops.size(); j++) {
- final Op op = ops.valueAt(j);
- final int opCode = op.op;
- if (dumpOp >= 0 && dumpOp != opCode) {
- continue;
- }
- if (dumpMode >= 0 && dumpMode != op.getMode()) {
- continue;
- }
- if (!printedPackage) {
- pw.print(" Package ");
- pw.print(ops.packageName);
- pw.println(":");
- printedPackage = true;
- }
- pw.print(" ");
- pw.print(AppOpsManager.opToName(opCode));
- 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
- ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
- pw.print("=");
- pw.print(AppOpsManager.modeToName(mode));
- }
- pw.println("): ");
- dumpStatesLocked(pw, dumpAttributionTag, dumpFilter, nowElapsed, op, now,
- sdf, date, " ");
- }
- }
- }
- if (needSep) {
- pw.println();
- }
-
- boolean showUserRestrictions = !(dumpMode < 0 && !dumpWatchers && !dumpHistory);
- mAppOpsRestrictions.dumpRestrictions(pw, dumpOp, dumpPackage, showUserRestrictions);
-
- if (dumpAll || dumpUidStateChangeLogs) {
- pw.println();
- pw.println("Uid State Changes Event Log:");
- getUidStateTracker().dumpEvents(pw);
- }
- }
-
- // Must not hold the appops lock
- if (dumpHistory && !dumpWatchers) {
- mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp,
- dumpFilter);
- }
- if (includeDiscreteOps) {
- pw.println("Discrete accesses: ");
- mHistoricalRegistry.dumpDiscreteData(pw, dumpUid, dumpPackage, dumpAttributionTag,
- dumpFilter, dumpOp, sdf, date, " ", nDiscreteOps);
- }
- }
-
- @Override
- public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
- checkSystemUid("setUserRestrictions");
- Objects.requireNonNull(restrictions);
- Objects.requireNonNull(token);
- for (int i = 0; i < AppOpsManager._NUM_OP; i++) {
- String restriction = AppOpsManager.opToRestriction(i);
- if (restriction != null) {
- setUserRestrictionNoCheck(i, restrictions.getBoolean(restriction, false), token,
- userHandle, null);
- }
- }
- }
-
- @Override
- public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
- PackageTagsList excludedPackageTags) {
- if (Binder.getCallingPid() != Process.myPid()) {
- mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
- if (userHandle != UserHandle.getCallingUserId()) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission
- .INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED
- && mContext.checkCallingOrSelfPermission(Manifest.permission
- .INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Need INTERACT_ACROSS_USERS_FULL or"
- + " INTERACT_ACROSS_USERS to interact cross user ");
- }
- }
- verifyIncomingOp(code);
- Objects.requireNonNull(token);
- setUserRestrictionNoCheck(code, restricted, token, userHandle, excludedPackageTags);
- }
-
- private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
- int userHandle, PackageTagsList excludedPackageTags) {
- synchronized (AppOpsServiceImpl.this) {
- ClientUserRestrictionState restrictionState = mOpUserRestrictions.get(token);
-
- if (restrictionState == null) {
- try {
- restrictionState = new ClientUserRestrictionState(token);
- } catch (RemoteException e) {
- return;
- }
- mOpUserRestrictions.put(token, restrictionState);
- }
-
- if (restrictionState.setRestriction(code, restricted, excludedPackageTags,
- userHandle)) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyWatchersOfChange, this, code, UID_ANY));
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::updateStartedOpModeForUser, this, code,
- restricted, userHandle));
- }
-
- if (restrictionState.isDefault()) {
- mOpUserRestrictions.remove(token);
- restrictionState.destroy();
- }
- }
- }
-
- @Override
- public void setGlobalRestriction(int code, boolean restricted, IBinder token) {
- if (Binder.getCallingPid() != Process.myPid()) {
- throw new SecurityException("Only the system can set global restrictions");
- }
-
- synchronized (this) {
- ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.get(token);
-
- if (restrictionState == null) {
- try {
- restrictionState = new ClientGlobalRestrictionState(token);
- } catch (RemoteException e) {
- return;
- }
- mOpGlobalRestrictions.put(token, restrictionState);
- }
-
- if (restrictionState.setRestriction(code, restricted)) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::notifyWatchersOfChange, this, code, UID_ANY));
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsServiceImpl::updateStartedOpModeForUser, this, code,
- restricted, UserHandle.USER_ALL));
- }
-
- if (restrictionState.isDefault()) {
- mOpGlobalRestrictions.remove(token);
- restrictionState.destroy();
- }
- }
- }
-
- @Override
- public int getOpRestrictionCount(int code, UserHandle user, String pkg,
- String attributionTag) {
- int number = 0;
- synchronized (this) {
- int numRestrictions = mOpUserRestrictions.size();
- for (int i = 0; i < numRestrictions; i++) {
- if (mOpUserRestrictions.valueAt(i)
- .hasRestriction(code, pkg, attributionTag, user.getIdentifier(),
- false)) {
- number++;
- }
- }
-
- numRestrictions = mOpGlobalRestrictions.size();
- for (int i = 0; i < numRestrictions; i++) {
- if (mOpGlobalRestrictions.valueAt(i).hasRestriction(code)) {
- number++;
- }
- }
- }
-
- return number;
- }
-
- private void updateStartedOpModeForUser(int code, boolean restricted, int userId) {
- synchronized (AppOpsServiceImpl.this) {
- int numUids = mUidStates.size();
- for (int uidNum = 0; uidNum < numUids; uidNum++) {
- int uid = mUidStates.keyAt(uidNum);
- if (userId != UserHandle.USER_ALL && UserHandle.getUserId(uid) != userId) {
- continue;
- }
- updateStartedOpModeForUidLocked(code, restricted, uid);
- }
- }
- }
-
- private void updateStartedOpModeForUidLocked(int code, boolean restricted, int uid) {
- UidState uidState = mUidStates.get(uid);
- if (uidState == null || uidState.pkgOps == null) {
- return;
- }
-
- int numPkgOps = uidState.pkgOps.size();
- 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.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
- continue;
- }
- int numAttrTags = op.mAttributions.size();
- for (int attrNum = 0; attrNum < numAttrTags; attrNum++) {
- AttributedOp attrOp = op.mAttributions.valueAt(attrNum);
- if (restricted && attrOp.isRunning()) {
- attrOp.pause();
- } else if (attrOp.isPaused()) {
- attrOp.resume();
- }
- }
- }
- }
-
- @Override
- public void notifyWatchersOfChange(int code, int uid) {
- final ArraySet<OnOpModeChangedListener> modeChangedListenerSet;
- synchronized (this) {
- modeChangedListenerSet = mAppOpsServiceInterface.getOpModeChangedListeners(code);
- if (modeChangedListenerSet == null) {
- return;
- }
- }
-
- notifyOpChanged(modeChangedListenerSet, code, uid, null);
- }
-
- @Override
- public void removeUser(int userHandle) throws RemoteException {
- checkSystemUid("removeUser");
- synchronized (AppOpsServiceImpl.this) {
- final int tokenCount = mOpUserRestrictions.size();
- for (int i = tokenCount - 1; i >= 0; i--) {
- ClientUserRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
- opRestrictions.removeUser(userHandle);
- }
- removeUidsForUserLocked(userHandle);
- }
- }
-
- @Override
- public boolean isOperationActive(int code, int uid, String packageName) {
- if (Binder.getCallingUid() != uid) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
- != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- }
- verifyIncomingOp(code);
- if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
- return false;
- }
-
- final String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return false;
- }
- // TODO moltmann: Allow to check for attribution op activeness
- synchronized (AppOpsServiceImpl.this) {
- Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, false, null, false);
- if (pkgOps == null) {
- return false;
- }
-
- Op op = pkgOps.get(code);
- if (op == null) {
- return false;
- }
-
- return op.isRunning();
- }
- }
-
- @Override
- public boolean isProxying(int op, @NonNull String proxyPackageName,
- @NonNull String proxyAttributionTag, int proxiedUid,
- @NonNull String proxiedPackageName) {
- Objects.requireNonNull(proxyPackageName);
- Objects.requireNonNull(proxiedPackageName);
- final long callingUid = Binder.getCallingUid();
- final long identity = Binder.clearCallingIdentity();
- try {
- final List<AppOpsManager.PackageOps> packageOps = getOpsForPackage(proxiedUid,
- proxiedPackageName, new int[]{op});
- if (packageOps == null || packageOps.isEmpty()) {
- return false;
- }
- final List<OpEntry> opEntries = packageOps.get(0).getOps();
- if (opEntries.isEmpty()) {
- return false;
- }
- final OpEntry opEntry = opEntries.get(0);
- if (!opEntry.isRunning()) {
- return false;
- }
- final OpEventProxyInfo proxyInfo = opEntry.getLastProxyInfo(
- OP_FLAG_TRUSTED_PROXIED | AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED);
- return proxyInfo != null && callingUid == proxyInfo.getUid()
- && proxyPackageName.equals(proxyInfo.getPackageName())
- && Objects.equals(proxyAttributionTag, proxyInfo.getAttributionTag());
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public void resetPackageOpsNoHistory(@NonNull String packageName) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "resetPackageOpsNoHistory");
- synchronized (AppOpsServiceImpl.this) {
- final int uid = mPackageManagerInternal.getPackageUid(packageName, 0,
- UserHandle.getCallingUserId());
- if (uid == Process.INVALID_UID) {
- return;
- }
- UidState uidState = mUidStates.get(uid);
- if (uidState == null || uidState.pkgOps == null) {
- return;
- }
- Ops removedOps = uidState.pkgOps.remove(packageName);
- mAppOpsServiceInterface.removePackage(packageName, UserHandle.getUserId(uid));
- if (removedOps != null) {
- scheduleFastWriteLocked();
- }
- }
- }
-
- @Override
- public void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
- long baseSnapshotInterval, int compressionStep) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "setHistoryParameters");
- // Must not hold the appops lock
- mHistoricalRegistry.setHistoryParameters(mode, baseSnapshotInterval, compressionStep);
- }
-
- @Override
- public void offsetHistory(long offsetMillis) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "offsetHistory");
- // Must not hold the appops lock
- mHistoricalRegistry.offsetHistory(offsetMillis);
- mHistoricalRegistry.offsetDiscreteHistory(offsetMillis);
- }
-
- @Override
- public void addHistoricalOps(HistoricalOps ops) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "addHistoricalOps");
- // Must not hold the appops lock
- mHistoricalRegistry.addHistoricalOps(ops);
- }
-
- @Override
- public void resetHistoryParameters() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "resetHistoryParameters");
- // Must not hold the appops lock
- mHistoricalRegistry.resetHistoryParameters();
- }
-
- @Override
- public void clearHistory() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "clearHistory");
- // Must not hold the appops lock
- mHistoricalRegistry.clearAllHistory();
- }
-
- @Override
- public void rebootHistory(long offlineDurationMillis) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
- "rebootHistory");
-
- Preconditions.checkArgument(offlineDurationMillis >= 0);
-
- // Must not hold the appops lock
- mHistoricalRegistry.shutdown();
-
- if (offlineDurationMillis > 0) {
- SystemClock.sleep(offlineDurationMillis);
- }
-
- mHistoricalRegistry = new HistoricalRegistry(mHistoricalRegistry);
- mHistoricalRegistry.systemReady(mContext.getContentResolver());
- mHistoricalRegistry.persistPendingHistory();
- }
-
- @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);
- }
- }
- }
-
- private void checkSystemUid(String function) {
- int uid = Binder.getCallingUid();
- if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(function + " must by called by the system");
- }
- }
-
- private static int resolveUid(String packageName) {
- if (packageName == null) {
- return Process.INVALID_UID;
- }
- switch (packageName) {
- case "root":
- return Process.ROOT_UID;
- case "shell":
- case "dumpstate":
- return Process.SHELL_UID;
- case "media":
- return Process.MEDIA_UID;
- case "audioserver":
- return Process.AUDIOSERVER_UID;
- case "cameraserver":
- return Process.CAMERASERVER_UID;
- }
- return Process.INVALID_UID;
- }
-
- private static String[] getPackagesForUid(int uid) {
- String[] packageNames = null;
-
- // Very early during boot the package manager is not yet or not yet fully started. At this
- // time there are no packages yet.
- if (AppGlobals.getPackageManager() != null) {
- try {
- packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
- } catch (RemoteException e) {
- /* ignore - local call */
- }
- }
- if (packageNames == null) {
- return EmptyArray.STRING;
- }
- return packageNames;
- }
-
- private final class ClientUserRestrictionState implements DeathRecipient {
- private final IBinder mToken;
-
- ClientUserRestrictionState(IBinder token)
- throws RemoteException {
- token.linkToDeath(this, 0);
- this.mToken = token;
- }
-
- public boolean setRestriction(int code, boolean restricted,
- PackageTagsList excludedPackageTags, int userId) {
- return mAppOpsRestrictions.setUserRestriction(mToken, userId, code,
- restricted, excludedPackageTags);
- }
-
- public boolean hasRestriction(int code, String packageName, String attributionTag,
- int userId, boolean isCheckOp) {
- return mAppOpsRestrictions.getUserRestriction(mToken, userId, code, packageName,
- attributionTag, isCheckOp);
- }
-
- public void removeUser(int userId) {
- mAppOpsRestrictions.clearUserRestrictions(mToken, userId);
- }
-
- public boolean isDefault() {
- return !mAppOpsRestrictions.hasUserRestrictions(mToken);
- }
-
- @Override
- public void binderDied() {
- synchronized (AppOpsServiceImpl.this) {
- mAppOpsRestrictions.clearUserRestrictions(mToken);
- mOpUserRestrictions.remove(mToken);
- destroy();
- }
- }
-
- public void destroy() {
- mToken.unlinkToDeath(this, 0);
- }
- }
-
- private final class ClientGlobalRestrictionState implements DeathRecipient {
- final IBinder mToken;
-
- ClientGlobalRestrictionState(IBinder token)
- throws RemoteException {
- token.linkToDeath(this, 0);
- this.mToken = token;
- }
-
- boolean setRestriction(int code, boolean restricted) {
- return mAppOpsRestrictions.setGlobalRestriction(mToken, code, restricted);
- }
-
- boolean hasRestriction(int code) {
- return mAppOpsRestrictions.getGlobalRestriction(mToken, code);
- }
-
- boolean isDefault() {
- return !mAppOpsRestrictions.hasGlobalRestrictions(mToken);
- }
-
- @Override
- public void binderDied() {
- mAppOpsRestrictions.clearGlobalRestrictions(mToken);
- mOpGlobalRestrictions.remove(mToken);
- destroy();
- }
-
- void destroy() {
- mToken.unlinkToDeath(this, 0);
- }
- }
-
- @Override
- public void setDeviceAndProfileOwners(SparseIntArray owners) {
- synchronized (this) {
- mProfileOwners = owners;
- }
- }
-}
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
index 8420fcbd346f..18f659e4c62a 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -13,482 +13,197 @@
* 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.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.AttributionSource;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.PackageTagsList;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.SparseArray;
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager.Mode;
+import android.util.ArraySet;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import com.android.internal.app.IAppOpsActiveCallback;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsNotedCallback;
-import com.android.internal.app.IAppOpsStartedCallback;
-
-import dalvik.annotation.optimization.NeverCompile;
-
-import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.List;
/**
- *
+ * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
+ * This interface also includes functions for added and removing op mode watchers.
+ * In the future this interface will also include op restrictions.
*/
-public interface AppOpsServiceInterface extends PersistenceScheduler {
-
- /**
- *
- */
- void systemReady();
-
- /**
- *
- */
- void shutdown();
-
- /**
- *
- * @param uid
- * @param packageName
- */
- void verifyPackage(int uid, String packageName);
-
- /**
- *
- * @param op
- * @param packageName
- * @param flags
- * @param callback
- */
- void startWatchingModeWithFlags(int op, String packageName, int flags,
- IAppOpsCallback callback);
-
- /**
- *
- * @param callback
- */
- void stopWatchingMode(IAppOpsCallback callback);
-
- /**
- *
- * @param ops
- * @param callback
- */
- void startWatchingActive(int[] ops, IAppOpsActiveCallback callback);
-
- /**
- *
- * @param callback
- */
- void stopWatchingActive(IAppOpsActiveCallback callback);
-
- /**
- *
- * @param ops
- * @param callback
- */
- void startWatchingStarted(int[] ops, @NonNull IAppOpsStartedCallback callback);
-
+public interface AppOpsServiceInterface {
/**
- *
- * @param callback
+ * 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.
*/
- void stopWatchingStarted(IAppOpsStartedCallback callback);
+ SparseIntArray getNonDefaultUidModes(int uid);
/**
- *
- * @param ops
- * @param callback
+ * 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.
*/
- void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback);
+ int getUidMode(int uid, int op);
/**
- *
- * @param callback
+ * 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.
*/
- void stopWatchingNoted(IAppOpsNotedCallback callback);
+ boolean setUidMode(int uid, int op, @Mode int mode);
/**
- * @param clientId
- * @param code
- * @param uid
- * @param packageName
- * @param attributionTag
- * @param startIfModeDefault
- * @param message
- * @param attributionFlags
- * @param attributionChainId
- * @return
+ * 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.
+ * @param userId user id associated with the package.
+ * @return the mode of the app-op.
*/
- int startOperation(@NonNull IBinder clientId, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
- boolean startIfModeDefault, @NonNull String message,
- @AppOpsManager.AttributionFlags int attributionFlags,
- int attributionChainId);
-
-
- int startOperationUnchecked(IBinder clientId, int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, int proxyUid, String proxyPackageName,
- @Nullable String proxyAttributionTag, @AppOpsManager.OpFlags int flags,
- boolean startIfModeDefault, @AppOpsManager.AttributionFlags int attributionFlags,
- int attributionChainId, boolean dryRun);
+ int getPackageMode(@NonNull String packageName, int op, @UserIdInt int userId);
/**
+ * 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.
+ * @param userId user id associated with the package.
*
- * @param clientId
- * @param code
- * @param uid
- * @param packageName
- * @param attributionTag
*/
- void finishOperation(IBinder clientId, int code, int uid, String packageName,
- String attributionTag);
+ void setPackageMode(@NonNull String packageName, int op, @Mode int mode, @UserIdInt int userId);
/**
- *
- * @param clientId
- * @param code
- * @param uid
- * @param packageName
- * @param attributionTag
+ * Stop tracking any app-op modes for a package.
+ * @param packageName Name of the package for which we want to remove all mode tracking.
+ * @param userId user id associated with the package.
*/
- void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName,
- String attributionTag);
+ boolean removePackage(@NonNull String packageName, @UserIdInt int userId);
/**
- *
- * @param uidPackageNames
- * @param visible
+ * Stop tracking any app-op modes for this uid.
+ * @param uid user id for which we want to remove all tracking.
*/
- void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible);
+ void removeUid(int uid);
/**
- *
+ * Returns true if all uid modes for this uid are
+ * in default state.
+ * @param uid user id
*/
- void readState();
+ boolean areUidModesDefault(int uid);
/**
- *
+ * Returns true if all package modes for this package name are
+ * in default state.
+ * @param packageName package name.
+ * @param userId user id associated with the package.
*/
- void writeState();
+ boolean arePackageModesDefault(String packageName, @UserIdInt int userId);
/**
- *
- * @param uid
- * @param packageName
+ * Stop tracking app-op modes for all uid and packages.
*/
- void packageRemoved(int uid, String packageName);
+ void clearAllModes();
/**
- *
- * @param uid
+ * Registers changedListener to listen to op's mode change.
+ * @param changedListener the listener that must be trigger on the op's mode change.
+ * @param op op representing the app-op whose mode change needs to be listened to.
*/
- void uidRemoved(int uid);
+ void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op);
/**
- *
- * @param uid
- * @param procState
- * @param capability
+ * Registers changedListener to listen to package's app-op's mode change.
+ * @param changedListener the listener that must be trigger on the mode change.
+ * @param packageName of the package whose app-op's mode change needs to be listened to.
*/
- void updateUidProcState(int uid, int procState,
- @ActivityManager.ProcessCapability int capability);
+ void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
+ @NonNull String packageName);
/**
- *
- * @param ops
- * @return
+ * Stop the changedListener from triggering on any mode change.
+ * @param changedListener the listener that needs to be removed.
*/
- List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops);
+ void removeListener(@NonNull OnOpModeChangedListener changedListener);
/**
- *
- * @param uid
- * @param packageName
- * @param ops
- * @return
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Returns a set of OnOpModeChangedListener that are listening for op's mode changes.
+ * @param op app-op whose mode change is being listened to.
*/
- List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
- int[] ops);
+ ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op);
/**
- *
- * @param uid
- * @param packageName
- * @param attributionTag
- * @param opNames
- * @param dataType
- * @param filter
- * @param beginTimeMillis
- * @param endTimeMillis
- * @param flags
- * @param callback
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes.
+ * @param packageName of package whose app-op's mode change is being listened to.
*/
- void getHistoricalOps(int uid, String packageName, String attributionTag,
- List<String> opNames, int dataType, int filter, long beginTimeMillis,
- long endTimeMillis, int flags, RemoteCallback callback);
+ ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName);
/**
- *
- * @param uid
- * @param packageName
- * @param attributionTag
- * @param opNames
- * @param dataType
- * @param filter
- * @param beginTimeMillis
- * @param endTimeMillis
- * @param flags
- * @param callback
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Notify that the app-op's mode is changed by triggering the change listener.
+ * @param op App-op whose mode has changed
+ * @param uid user id associated with the app-op (or, if UID_ANY, notifies all users)
*/
- void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
- List<String> opNames, int dataType, int filter, long beginTimeMillis,
- long endTimeMillis, int flags, RemoteCallback callback);
+ void notifyWatchersOfChange(int op, int uid);
/**
- *
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Notify that the app-op's mode is changed by triggering the change listener.
+ * @param changedListener the change listener.
+ * @param op App-op whose mode has changed
+ * @param uid user id associated with the app-op
+ * @param packageName package name that is associated with the app-op
*/
- void reloadNonHistoricalState();
+ void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
+ @Nullable String packageName);
/**
- *
- * @param uid
- * @param ops
- * @return
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Notify that the app-op's mode is changed to all packages associated with the uid by
+ * triggering the appropriate change listener.
+ * @param op App-op whose mode has changed
+ * @param uid user id associated with the app-op
+ * @param onlyForeground true if only watchers that
+ * @param callbackToIgnore callback that should be ignored.
*/
- List<AppOpsManager.PackageOps> getUidOps(int uid, int[] ops);
+ void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
+ @Nullable OnOpModeChangedListener callbackToIgnore);
/**
- *
- * @param owners
+ * TODO: Move hasForegroundWatchers and foregroundOps into this.
+ * Go over the list of app-ops for the uid and mark app-ops with MODE_FOREGROUND in
+ * foregroundOps.
+ * @param uid for which the app-op's mode needs to be marked.
+ * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
+ * @return foregroundOps.
*/
- void setDeviceAndProfileOwners(SparseIntArray owners);
+ SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps);
- // used in audio restriction calls, might just copy the logic to avoid having this call.
/**
- *
- * @param callingPid
- * @param callingUid
- * @param targetUid
- */
- void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid);
-
- /**
- *
- * @param code
- * @param uid
- * @param mode
- * @param permissionPolicyCallback
- */
- void setUidMode(int code, int uid, int mode,
- @Nullable IAppOpsCallback permissionPolicyCallback);
-
- /**
- *
- * @param code
- * @param uid
- * @param packageName
- * @param mode
- * @param permissionPolicyCallback
+ * Go over the list of app-ops for the package name and mark app-ops with MODE_FOREGROUND in
+ * foregroundOps.
+ * @param packageName for which the app-op's mode needs to be marked.
+ * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
+ * @param userId user id associated with the package.
+ * @return foregroundOps.
*/
- void setMode(int code, int uid, @NonNull String packageName, int mode,
- @Nullable IAppOpsCallback permissionPolicyCallback);
+ SparseBooleanArray evalForegroundPackageOps(String packageName,
+ SparseBooleanArray foregroundOps, @UserIdInt int userId);
/**
- *
- * @param reqUserId
- * @param reqPackageName
- */
- void resetAllModes(int reqUserId, String reqPackageName);
-
- /**
- *
- * @param code
- * @param uid
- * @param packageName
- * @param attributionTag
- * @param raw
- * @return
- */
- int checkOperation(int code, int uid, String packageName,
- @Nullable String attributionTag, boolean raw);
-
- /**
- *
- * @param uid
- * @param packageName
- * @return
- */
- int checkPackage(int uid, String packageName);
-
- /**
- *
- * @param code
- * @param uid
- * @param packageName
- * @param attributionTag
- * @param message
- * @return
- */
- int noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable String message);
-
- /**
- *
- * @param code
- * @param uid
- * @param packageName
- * @param attributionTag
- * @param proxyUid
- * @param proxyPackageName
- * @param proxyAttributionTag
- * @param flags
- * @return
- */
- @AppOpsManager.Mode
- int noteOperationUnchecked(int code, int uid, @NonNull String packageName,
- @Nullable String attributionTag, int proxyUid, String proxyPackageName,
- @Nullable String proxyAttributionTag, @AppOpsManager.OpFlags int flags);
-
- boolean isAttributionTagValid(int uid, @NonNull String packageName,
- @Nullable String attributionTag, @Nullable String proxyPackageName);
-
- /**
- *
- * @param fd
- * @param pw
- * @param args
- */
- @NeverCompile
- // Avoid size overhead of debugging code.
- void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
- /**
- *
- * @param restrictions
- * @param token
- * @param userHandle
- */
- void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle);
-
- /**
- *
- * @param code
- * @param restricted
- * @param token
- * @param userHandle
- * @param excludedPackageTags
- */
- void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
- PackageTagsList excludedPackageTags);
-
- /**
- *
- * @param code
- * @param restricted
- * @param token
- */
- void setGlobalRestriction(int code, boolean restricted, IBinder token);
-
- /**
- *
- * @param code
- * @param user
- * @param pkg
- * @param attributionTag
- * @return
- */
- int getOpRestrictionCount(int code, UserHandle user, String pkg,
- String attributionTag);
-
- /**
- *
- * @param code
- * @param uid
- */
- // added to interface for audio restriction stuff
- void notifyWatchersOfChange(int code, int uid);
-
- /**
- *
- * @param userHandle
- * @throws RemoteException
- */
- void removeUser(int userHandle) throws RemoteException;
-
- /**
- *
- * @param code
- * @param uid
- * @param packageName
- * @return
- */
- boolean isOperationActive(int code, int uid, String packageName);
-
- /**
- *
- * @param op
- * @param proxyPackageName
- * @param proxyAttributionTag
- * @param proxiedUid
- * @param proxiedPackageName
- * @return
- */
- // TODO this one might not need to be in the interface
- boolean isProxying(int op, @NonNull String proxyPackageName,
- @NonNull String proxyAttributionTag, int proxiedUid,
- @NonNull String proxiedPackageName);
-
- /**
- *
- * @param packageName
- */
- void resetPackageOpsNoHistory(@NonNull String packageName);
-
- /**
- *
- * @param mode
- * @param baseSnapshotInterval
- * @param compressionStep
- */
- void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
- long baseSnapshotInterval, int compressionStep);
-
- /**
- *
- * @param offsetMillis
- */
- void offsetHistory(long offsetMillis);
-
- /**
- *
- * @param ops
- */
- void addHistoricalOps(AppOpsManager.HistoricalOps ops);
-
- /**
- *
- */
- void resetHistoryParameters();
-
- /**
- *
- */
- void clearHistory();
-
- /**
- *
- * @param offlineDurationMillis
+ * Dump op mode and package mode listeners and their details.
+ * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's set to an
+ * app-op, only the watchers for that app-op are dumped.
+ * @param dumpUid uid for which we want to dump op mode watchers.
+ * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package name.
+ * @param printWriter writer to dump to.
*/
- void rebootHistory(long offlineDurationMillis);
+ boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, PrintWriter printWriter);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index c1434e4d9f4d..5114bd59f084 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -59,7 +59,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
private final DelayableExecutor mExecutor;
private final Clock mClock;
private ActivityManagerInternal mActivityManagerInternal;
- private AppOpsServiceImpl.Constants mConstants;
+ private AppOpsService.Constants mConstants;
private SparseIntArray mUidStates = new SparseIntArray();
private SparseIntArray mPendingUidStates = new SparseIntArray();
@@ -85,7 +85,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
Handler handler, Executor lockingExecutor, Clock clock,
- AppOpsServiceImpl.Constants constants) {
+ AppOpsService.Constants constants) {
this(activityManagerInternal, new DelayableExecutor() {
@Override
@@ -102,7 +102,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
@VisibleForTesting
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
- DelayableExecutor executor, Clock clock, AppOpsServiceImpl.Constants constants,
+ DelayableExecutor executor, Clock clock, AppOpsService.Constants constants,
Thread executorThread) {
mActivityManagerInternal = activityManagerInternal;
mExecutor = executor;
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 797026908619..dcc36bcf6149 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -40,9 +40,9 @@ import java.util.List;
import java.util.NoSuchElementException;
final class AttributedOp {
- private final @NonNull AppOpsServiceImpl mAppOpsService;
+ private final @NonNull AppOpsService mAppOpsService;
public final @Nullable String tag;
- public final @NonNull AppOpsServiceImpl.Op parent;
+ public final @NonNull AppOpsService.Op parent;
/**
* Last successful accesses (noteOp + finished startOp) for each uidState/opFlag combination
@@ -80,8 +80,8 @@ final class AttributedOp {
// @GuardedBy("mAppOpsService")
@Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents;
- AttributedOp(@NonNull AppOpsServiceImpl appOpsService, @Nullable String tag,
- @NonNull AppOpsServiceImpl.Op parent) {
+ AttributedOp(@NonNull AppOpsService appOpsService, @Nullable String tag,
+ @NonNull AppOpsService.Op parent) {
mAppOpsService = appOpsService;
this.tag = tag;
this.parent = parent;
@@ -131,8 +131,8 @@ final class AttributedOp {
AppOpsManager.OpEventProxyInfo proxyInfo = null;
if (proxyUid != Process.INVALID_UID) {
- proxyInfo = mAppOpsService.mOpEventProxyInfoPool.acquire(proxyUid,
- proxyPackageName, proxyAttributionTag);
+ proxyInfo = mAppOpsService.mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+ proxyAttributionTag);
}
AppOpsManager.NoteOpEvent existingEvent = mAccessEvents.get(key);
@@ -238,7 +238,7 @@ final class AttributedOp {
if (event == null) {
event = mAppOpsService.mInProgressStartOpEventPool.acquire(startTime,
SystemClock.elapsedRealtime(), clientId, tag,
- PooledLambda.obtainRunnable(AppOpsServiceImpl::onClientDeath, this, clientId),
+ PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags,
attributionFlags, attributionChainId);
events.put(clientId, event);
@@ -251,9 +251,9 @@ final class AttributedOp {
event.mNumUnfinishedStarts++;
if (isStarted) {
- mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op,
- parent.uid, parent.packageName, tag, uidState, flags, startTime,
- attributionFlags, attributionChainId);
+ mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, uidState, flags, startTime, attributionFlags,
+ attributionChainId);
}
}
@@ -309,8 +309,8 @@ final class AttributedOp {
mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()),
finishedEvent);
- mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op,
- parent.uid, parent.packageName, tag, event.getUidState(),
+ mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
+ parent.packageName, tag, event.getUidState(),
event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
event.getAttributionFlags(), event.getAttributionChainId());
@@ -334,13 +334,13 @@ final class AttributedOp {
@SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) {
if (!isPaused()) {
- Slog.wtf(AppOpsServiceImpl.TAG, "No ops running or paused");
+ Slog.wtf(AppOpsService.TAG, "No ops running or paused");
return;
}
int indexOfToken = mPausedInProgressEvents.indexOfKey(clientId);
if (indexOfToken < 0) {
- Slog.wtf(AppOpsServiceImpl.TAG, "No op running or paused for the client");
+ Slog.wtf(AppOpsService.TAG, "No op running or paused for the client");
return;
} else if (isPausing) {
// already paused
@@ -416,9 +416,9 @@ final class AttributedOp {
mInProgressEvents.put(event.getClientId(), event);
event.setStartElapsedTime(SystemClock.elapsedRealtime());
event.setStartTime(startTime);
- mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op,
- parent.uid, parent.packageName, tag, event.getUidState(), event.getFlags(),
- startTime, event.getAttributionFlags(), event.getAttributionChainId());
+ mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, event.getUidState(), event.getFlags(), startTime,
+ event.getAttributionFlags(), event.getAttributionChainId());
if (shouldSendActive) {
mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, tag, true, event.getAttributionFlags(),
@@ -503,8 +503,8 @@ final class AttributedOp {
newEvent.mNumUnfinishedStarts += numPreviousUnfinishedStarts - 1;
}
} catch (RemoteException e) {
- if (AppOpsServiceImpl.DEBUG) {
- Slog.e(AppOpsServiceImpl.TAG,
+ if (AppOpsService.DEBUG) {
+ Slog.e(AppOpsService.TAG,
"Cannot switch to new uidState " + newState);
}
}
@@ -555,8 +555,8 @@ final class AttributedOp {
ArrayMap<IBinder, InProgressStartOpEvent> ignoredEvents =
opToAdd.isRunning()
? opToAdd.mInProgressEvents : opToAdd.mPausedInProgressEvents;
- Slog.w(AppOpsServiceImpl.TAG, "Ignoring " + ignoredEvents.size()
- + " app-ops, running: " + opToAdd.isRunning());
+ Slog.w(AppOpsService.TAG, "Ignoring " + ignoredEvents.size() + " app-ops, running: "
+ + opToAdd.isRunning());
int numInProgressEvents = ignoredEvents.size();
for (int i = 0; i < numInProgressEvents; i++) {
@@ -668,22 +668,16 @@ final class AttributedOp {
/**
* Create a new {@link InProgressStartOpEvent}.
*
- * @param startTime The time {@link AppOpCheckingServiceInterface#startOperation}
- * was called
- * @param startElapsedTime The elapsed time whe
- * {@link AppOpCheckingServiceInterface#startOperation} was called
- * @param clientId The client id of the caller of
- * {@link AppOpCheckingServiceInterface#startOperation}
+ * @param startTime The time {@link #startOperation} was called
+ * @param startElapsedTime The elapsed time when {@link #startOperation} was called
+ * @param clientId The client id of the caller of {@link #startOperation}
* @param attributionTag The attribution tag for the operation.
* @param onDeath The code to execute on client death
- * @param uidState The uidstate of the app
- * {@link AppOpCheckingServiceInterface#startOperation} was called
- * for
+ * @param uidState The uidstate of the app {@link #startOperation} was called for
* @param attributionFlags the attribution flags for this operation.
* @param attributionChainId the unique id of the attribution chain this op is a part of.
- * @param proxy The proxy information, if
- * {@link AppOpCheckingServiceInterface#startProxyOperation} was
- * called
+ * @param proxy The proxy information, if {@link #startProxyOperation} was
+ * called
* @param flags The trusted/nontrusted/self flags.
* @throws RemoteException If the client is dying
*/
@@ -724,21 +718,15 @@ final class AttributedOp {
/**
* Reinit existing object with new state.
*
- * @param startTime The time {@link AppOpCheckingServiceInterface#startOperation}
- * was called
- * @param startElapsedTime The elapsed time when
- * {@link AppOpCheckingServiceInterface#startOperation} was called
- * @param clientId The client id of the caller of
- * {@link AppOpCheckingServiceInterface#startOperation}
+ * @param startTime The time {@link #startOperation} was called
+ * @param startElapsedTime The elapsed time when {@link #startOperation} was called
+ * @param clientId The client id of the caller of {@link #startOperation}
* @param attributionTag The attribution tag for this operation.
* @param onDeath The code to execute on client death
- * @param uidState The uidstate of the app
- * {@link AppOpCheckingServiceInterface#startOperation} was called
- * for
+ * @param uidState The uidstate of the app {@link #startOperation} was called for
* @param flags The flags relating to the proxy
- * @param proxy The proxy information, if
- * {@link AppOpCheckingServiceInterface#startProxyOperation was
- * called
+ * @param proxy The proxy information, if {@link #startProxyOperation}
+ * was called
* @param attributionFlags the attribution flags for this operation.
* @param attributionChainId the unique id of the attribution chain this op is a part of.
* @param proxyPool The pool to release
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
index 587fb0410bca..f6fff351c232 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -20,7 +20,7 @@ import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager.opRestrictsRead;
-import static com.android.server.appop.AppOpsServiceImpl.ModeCallback.ALL_OPS;
+import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
import android.Manifest;
import android.annotation.NonNull;
@@ -56,7 +56,7 @@ import java.util.Objects;
* 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 AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface {
+public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface {
static final String TAG = "LegacyAppOpsServiceInterfaceImpl";
@@ -84,9 +84,9 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
private static final int UID_ANY = -2;
- AppOpsCheckingServiceImpl(PersistenceScheduler persistenceScheduler,
- @NonNull Object lock, Handler handler, Context context,
- SparseArray<int[]> switchedOps) {
+ LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler,
+ @NonNull Object lock, Handler handler, Context context,
+ SparseArray<int[]> switchedOps) {
this.mPersistenceScheduler = persistenceScheduler;
this.mLock = lock;
this.mHandler = handler;
@@ -218,7 +218,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public boolean arePackageModesDefault(@NonNull String packageMode, @UserIdInt int userId) {
+ public boolean arePackageModesDefault(String packageMode, @UserIdInt int userId) {
synchronized (mLock) {
ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
if (packageModes == null) {
@@ -456,7 +456,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
final ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
if (reportedPackageNames == null) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsCheckingServiceImpl::notifyOpChanged,
+ LegacyAppOpsServiceInterfaceImpl::notifyOpChanged,
this, callback, code, uid, (String) null));
} else {
@@ -464,7 +464,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
for (int j = 0; j < reportedPackageCount; j++) {
final String reportedPackageName = reportedPackageNames.valueAt(j);
mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsCheckingServiceImpl::notifyOpChanged,
+ LegacyAppOpsServiceInterfaceImpl::notifyOpChanged,
this, callback, code, uid, reportedPackageName));
}
}
@@ -490,16 +490,15 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public SparseBooleanArray evalForegroundUidOps(int uid,
- @Nullable SparseBooleanArray foregroundOps) {
+ public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) {
synchronized (mLock) {
return evalForegroundOps(mUidModes.get(uid), foregroundOps);
}
}
@Override
- public SparseBooleanArray evalForegroundPackageOps(@NonNull String packageName,
- @Nullable SparseBooleanArray foregroundOps, @UserIdInt int userId) {
+ public SparseBooleanArray evalForegroundPackageOps(String packageName,
+ SparseBooleanArray foregroundOps, @UserIdInt int userId) {
synchronized (mLock) {
ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
return evalForegroundOps(packageModes == null ? null : packageModes.get(packageName),
@@ -538,8 +537,8 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public boolean dumpListeners(int dumpOp, int dumpUid, @Nullable String dumpPackage,
- @NonNull PrintWriter printWriter) {
+ public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage,
+ PrintWriter printWriter) {
boolean needSep = false;
if (mOpModeWatchers.size() > 0) {
boolean printedHeader = false;
diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
index 73c2cccc550d..45837e2cdeb3 100644
--- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
@@ -21,9 +21,9 @@ import com.android.internal.annotations.Keep
import com.android.server.LocalManagerRegistry
import com.android.server.LocalServices
import com.android.server.SystemService
-import com.android.server.appop.AppOpsCheckingServiceInterface
+import com.android.server.appop.AppOpsServiceInterface
import com.android.server.permission.access.appop.AppOpService
-import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.collection.IntSet
import com.android.server.permission.access.permission.PermissionService
import com.android.server.pm.PackageManagerLocal
import com.android.server.pm.UserManagerService
@@ -51,7 +51,7 @@ class AccessCheckingService(context: Context) : SystemService(context) {
appOpService = AppOpService(this)
permissionService = PermissionService(this)
- LocalServices.addService(AppOpsCheckingServiceInterface::class.java, appOpService)
+ LocalServices.addService(AppOpsServiceInterface::class.java, appOpService)
LocalServices.addService(PermissionManagerServiceInterface::class.java, permissionService)
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index b8d6aa3b4e49..f606f86281ea 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -19,14 +19,14 @@ package com.android.server.permission.access.appop
import android.util.ArraySet
import android.util.SparseBooleanArray
import android.util.SparseIntArray
-import com.android.server.appop.AppOpsCheckingServiceInterface
+import com.android.server.appop.AppOpsServiceInterface
import com.android.server.appop.OnOpModeChangedListener
import com.android.server.permission.access.AccessCheckingService
import java.io.PrintWriter
class AppOpService(
private val service: AccessCheckingService
-) : AppOpsCheckingServiceInterface {
+) : AppOpsServiceInterface {
fun initialize() {
TODO("Not yet implemented")
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
index be13bad70e16..5dc12510368c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
@@ -48,7 +48,7 @@ public class AppOpsLegacyRestrictionsTest {
StaticMockitoSession mSession;
@Mock
- AppOpsServiceImpl.Constants mConstants;
+ AppOpsService.Constants mConstants;
@Mock
Context mContext;
@@ -57,7 +57,7 @@ public class AppOpsLegacyRestrictionsTest {
Handler mHandler;
@Mock
- AppOpsCheckingServiceInterface mLegacyAppOpsService;
+ AppOpsServiceInterface mLegacyAppOpsService;
AppOpsRestrictions mAppOpsRestrictions;
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 7d4bc6f47ad4..c0688d131610 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -22,8 +22,6 @@ import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_READ_SMS;
import static android.app.AppOpsManager.OP_WIFI_SCAN;
import static android.app.AppOpsManager.OP_WRITE_SMS;
-import static android.app.AppOpsManager.resolvePackageName;
-import static android.os.Process.INVALID_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -41,7 +39,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.ContentResolver;
@@ -89,13 +86,13 @@ public class AppOpsServiceTest {
private File mAppOpsFile;
private Handler mHandler;
- private AppOpsServiceImpl mAppOpsService;
+ private AppOpsService mAppOpsService;
private int mMyUid;
private long mTestStartMillis;
private StaticMockitoSession mMockingSession;
private void setupAppOpsService() {
- mAppOpsService = new AppOpsServiceImpl(mAppOpsFile, mHandler, spy(sContext));
+ mAppOpsService = new AppOpsService(mAppOpsFile, mHandler, spy(sContext));
mAppOpsService.mHistoricalRegistry.systemReady(sContext.getContentResolver());
// Always approve all permission checks
@@ -164,20 +161,17 @@ public class AppOpsServiceTest {
@Test
public void testNoteOperationAndGetOpsForPackage() {
- mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED, null);
- mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED, null);
+ mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
+ mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
// Note an op that's allowed.
- mAppOpsService.noteOperationUnchecked(OP_READ_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
// Note another op that's not allowed.
- mAppOpsService.noteOperationUnchecked(OP_WRITE_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null,
+ false);
loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED);
@@ -191,20 +185,18 @@ public class AppOpsServiceTest {
@Test
public void testNoteOperationAndGetOpsForPackage_controlledByDifferentOp() {
// This op controls WIFI_SCAN
- mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED, null);
+ mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED);
- assertThat(mAppOpsService.noteOperationUnchecked(OP_WIFI_SCAN, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF)).isEqualTo(MODE_ALLOWED);
+ assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
+ null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, -1,
MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
// Now set COARSE_LOCATION to ERRORED -> this will make WIFI_SCAN disabled as well.
- mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED, null);
- assertThat(mAppOpsService.noteOperationUnchecked(OP_WIFI_SCAN, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF)).isEqualTo(MODE_ERRORED);
+ mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED);
+ assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
+ null, false).getOpMode()).isEqualTo(MODE_ERRORED);
assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, mTestStartMillis,
MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
@@ -213,14 +205,11 @@ public class AppOpsServiceTest {
// Tests the dumping and restoring of the in-memory state to/from XML.
@Test
public void testStatePersistence() {
- mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED, null);
- mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED, null);
- mAppOpsService.noteOperationUnchecked(OP_READ_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
- mAppOpsService.noteOperationUnchecked(OP_WRITE_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
+ mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+ mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null,
+ false);
mAppOpsService.writeState();
// Create a new app ops service which will initialize its state from XML.
@@ -235,10 +224,8 @@ public class AppOpsServiceTest {
// Tests that ops are persisted during shutdown.
@Test
public void testShutdown() {
- mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED, null);
- mAppOpsService.noteOperationUnchecked(OP_READ_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
mAppOpsService.shutdown();
// Create a new app ops service which will initialize its state from XML.
@@ -251,10 +238,8 @@ public class AppOpsServiceTest {
@Test
public void testGetOpsForPackage() {
- mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED, null);
- mAppOpsService.noteOperationUnchecked(OP_READ_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
// Query all ops
List<PackageOps> loggedOps = mAppOpsService.getOpsForPackage(
@@ -282,10 +267,8 @@ public class AppOpsServiceTest {
@Test
public void testPackageRemoved() {
- mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED, null);
- mAppOpsService.noteOperationUnchecked(OP_READ_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -339,10 +322,8 @@ public class AppOpsServiceTest {
@Test
public void testUidRemoved() {
- mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED, null);
- mAppOpsService.noteOperationUnchecked(OP_READ_SMS, mMyUid,
- resolvePackageName(mMyUid, sMyPackageName), null,
- INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+ mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index 3efd5e701013..98e895a86f9e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -76,7 +76,7 @@ public class AppOpsUidStateTrackerTest {
ActivityManagerInternal mAmi;
@Mock
- AppOpsServiceImpl.Constants mConstants;
+ AppOpsService.Constants mConstants;
AppOpsUidStateTrackerTestExecutor mExecutor = new AppOpsUidStateTrackerTestExecutor();
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 302fa0f0c528..9eed6ada3a37 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -121,13 +121,13 @@ public class AppOpsUpgradeTest {
}
}
- private void assertSameModes(SparseArray<AppOpsServiceImpl.UidState> uidStates,
+ private void assertSameModes(SparseArray<AppOpsService.UidState> uidStates,
int op1, int op2) {
int numberOfNonDefaultOps = 0;
final int defaultModeOp1 = AppOpsManager.opToDefaultMode(op1);
final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
for (int i = 0; i < uidStates.size(); i++) {
- final AppOpsServiceImpl.UidState uidState = uidStates.valueAt(i);
+ final AppOpsService.UidState uidState = uidStates.valueAt(i);
SparseIntArray opModes = uidState.getNonDefaultUidModes();
if (opModes != null) {
final int uidMode1 = opModes.get(op1, defaultModeOp1);
@@ -141,12 +141,12 @@ public class AppOpsUpgradeTest {
continue;
}
for (int j = 0; j < uidState.pkgOps.size(); j++) {
- final AppOpsServiceImpl.Ops ops = uidState.pkgOps.valueAt(j);
+ final AppOpsService.Ops ops = uidState.pkgOps.valueAt(j);
if (ops == null) {
continue;
}
- final AppOpsServiceImpl.Op _op1 = ops.get(op1);
- final AppOpsServiceImpl.Op _op2 = ops.get(op2);
+ final AppOpsService.Op _op1 = ops.get(op1);
+ final AppOpsService.Op _op2 = ops.get(op2);
final int mode1 = (_op1 == null) ? defaultModeOp1 : _op1.getMode();
final int mode2 = (_op2 == null) ? defaultModeOp2 : _op2.getMode();
assertEquals(mode1, mode2);
@@ -199,7 +199,7 @@ public class AppOpsUpgradeTest {
public void upgradeRunAnyInBackground() {
extractAppOpsFile(APP_OPS_UNVERSIONED_ASSET_PATH);
- AppOpsServiceImpl testService = new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext);
+ AppOpsService testService = new AppOpsService(sAppOpsFile, mHandler, mTestContext);
testService.upgradeRunAnyInBackgroundLocked();
assertSameModes(testService.mUidStates, AppOpsManager.OP_RUN_IN_BACKGROUND,
@@ -244,7 +244,7 @@ public class AppOpsUpgradeTest {
return UserHandle.getUid(userId, appIds[index]);
}).when(mPackageManagerInternal).getPackageUid(anyString(), anyLong(), anyInt());
- AppOpsServiceImpl testService = new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext);
+ AppOpsService testService = new AppOpsService(sAppOpsFile, mHandler, mTestContext);
testService.upgradeScheduleExactAlarmLocked();
@@ -259,7 +259,7 @@ public class AppOpsUpgradeTest {
} else {
expectedMode = previousMode;
}
- final AppOpsServiceImpl.UidState uidState = testService.mUidStates.get(uid);
+ final AppOpsService.UidState uidState = testService.mUidStates.get(uid);
assertEquals(expectedMode, uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM));
}
}
@@ -268,7 +268,7 @@ public class AppOpsUpgradeTest {
int[] unrelatedUidsInFile = {10225, 10178};
for (int uid : unrelatedUidsInFile) {
- final AppOpsServiceImpl.UidState uidState = testService.mUidStates.get(uid);
+ final AppOpsService.UidState uidState = testService.mUidStates.get(uid);
assertEquals(AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM),
uidState.getUidMode(OP_SCHEDULE_EXACT_ALARM));
}
@@ -278,8 +278,8 @@ public class AppOpsUpgradeTest {
public void upgradeFromNoFile() {
assertFalse(sAppOpsFile.exists());
- AppOpsServiceImpl testService = spy(
- new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext));
+ AppOpsService testService = spy(
+ new AppOpsService(sAppOpsFile, mHandler, mTestContext));
doNothing().when(testService).upgradeRunAnyInBackgroundLocked();
doNothing().when(testService).upgradeScheduleExactAlarmLocked();
@@ -296,7 +296,7 @@ public class AppOpsUpgradeTest {
AppOpsDataParser parser = new AppOpsDataParser(sAppOpsFile);
assertTrue(parser.parse());
- assertEquals(AppOpsServiceImpl.CURRENT_VERSION, parser.mVersion);
+ assertEquals(AppOpsService.CURRENT_VERSION, parser.mVersion);
}
@Test
@@ -306,8 +306,8 @@ public class AppOpsUpgradeTest {
assertTrue(parser.parse());
assertEquals(AppOpsDataParser.NO_VERSION, parser.mVersion);
- AppOpsServiceImpl testService = spy(
- new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext));
+ AppOpsService testService = spy(
+ new AppOpsService(sAppOpsFile, mHandler, mTestContext));
doNothing().when(testService).upgradeRunAnyInBackgroundLocked();
doNothing().when(testService).upgradeScheduleExactAlarmLocked();
@@ -320,7 +320,7 @@ public class AppOpsUpgradeTest {
testService.writeState();
assertTrue(parser.parse());
- assertEquals(AppOpsServiceImpl.CURRENT_VERSION, parser.mVersion);
+ assertEquals(AppOpsService.CURRENT_VERSION, parser.mVersion);
}
@Test
@@ -330,8 +330,8 @@ public class AppOpsUpgradeTest {
assertTrue(parser.parse());
assertEquals(1, parser.mVersion);
- AppOpsServiceImpl testService = spy(
- new AppOpsServiceImpl(sAppOpsFile, mHandler, mTestContext));
+ AppOpsService testService = spy(
+ new AppOpsService(sAppOpsFile, mHandler, mTestContext));
doNothing().when(testService).upgradeRunAnyInBackgroundLocked();
doNothing().when(testService).upgradeScheduleExactAlarmLocked();
@@ -344,7 +344,7 @@ public class AppOpsUpgradeTest {
testService.writeState();
assertTrue(parser.parse());
- assertEquals(AppOpsServiceImpl.CURRENT_VERSION, parser.mVersion);
+ assertEquals(AppOpsService.CURRENT_VERSION, parser.mVersion);
}
/**