diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java index f520f6a9ef492..cb2c54349c6d0 100644 --- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java +++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java @@ -17,32 +17,21 @@ package com.android.server.appop; import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.OP_NONE; +import static android.app.AppOpsManager.MODE_FOREGROUND; import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM; -import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES; -import static android.app.AppOpsManager.opRestrictsRead; import static android.app.AppOpsManager.opToDefaultMode; -import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS; - -import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.Mode; import android.content.Context; -import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.UserPackage; import android.os.AsyncTask; -import android.os.Binder; import android.os.Handler; -import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; @@ -53,15 +42,12 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; 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.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; -import libcore.util.EmptyArray; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -70,11 +56,8 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Objects; /** @@ -128,10 +111,6 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface @GuardedBy("mLock") final SparseArray> mUserPackageModes = new SparseArray<>(); - final SparseArray> mOpModeWatchers = new SparseArray<>(); - final ArrayMap> mPackageModeWatchers = - new ArrayMap<>(); - final AtomicFile mFile; final Runnable mWriteRunner = new Runnable() { public void run() { @@ -153,10 +132,6 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface boolean mWriteScheduled; boolean mFastWriteScheduled; - - // Constant meaning that any UID should be matched when dispatching callbacks - private static final int UID_ANY = -2; - AppOpsCheckingServiceImpl(File storageFile, @NonNull Object lock, Handler handler, Context context, SparseArray switchedOps) { @@ -351,348 +326,43 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface } @Override - public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, - int op) { - Objects.requireNonNull(changedListener); + public SparseBooleanArray getForegroundOps(int uid) { + SparseBooleanArray result = new SparseBooleanArray(); synchronized (mLock) { - ArraySet modeWatcherSet = mOpModeWatchers.get(op); - if (modeWatcherSet == null) { - modeWatcherSet = new ArraySet<>(); - mOpModeWatchers.put(op, modeWatcherSet); + SparseIntArray modes = mUidModes.get(uid); + if (modes == null) { + return result; + } + for (int i = 0; i < modes.size(); i++) { + if (modes.valueAt(i) == MODE_FOREGROUND) { + result.put(modes.keyAt(i), true); + } } - modeWatcherSet.add(changedListener); } + + return result; } @Override - public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, - @NonNull String packageName) { - Objects.requireNonNull(changedListener); - Objects.requireNonNull(packageName); + public SparseBooleanArray getForegroundOps(String packageName, int userId) { + SparseBooleanArray result = new SparseBooleanArray(); synchronized (mLock) { - ArraySet modeWatcherSet = - mPackageModeWatchers.get(packageName); - if (modeWatcherSet == null) { - modeWatcherSet = new ArraySet<>(); - mPackageModeWatchers.put(packageName, modeWatcherSet); + ArrayMap packageModes = mUserPackageModes.get(userId); + if (packageModes == null) { + return result; } - modeWatcherSet.add(changedListener); - } - } - - @Override - public void removeListener(@NonNull OnOpModeChangedListener changedListener) { - Objects.requireNonNull(changedListener); - - synchronized (mLock) { - for (int i = mOpModeWatchers.size() - 1; i >= 0; i--) { - ArraySet cbs = mOpModeWatchers.valueAt(i); - cbs.remove(changedListener); - if (cbs.size() <= 0) { - mOpModeWatchers.removeAt(i); - } + SparseIntArray modes = packageModes.get(packageName); + if (modes == null) { + return result; } - - for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) { - ArraySet cbs = mPackageModeWatchers.valueAt(i); - cbs.remove(changedListener); - if (cbs.size() <= 0) { - mPackageModeWatchers.removeAt(i); - } - } - } - } - - @Override - public ArraySet getOpModeChangedListeners(int op) { - synchronized (mLock) { - ArraySet modeChangedListenersSet = mOpModeWatchers.get(op); - if (modeChangedListenersSet == null) { - return new ArraySet<>(); - } - return new ArraySet<>(modeChangedListenersSet); - } - } - - @Override - public ArraySet getPackageModeChangedListeners( - @NonNull String packageName) { - Objects.requireNonNull(packageName); - - synchronized (mLock) { - ArraySet modeChangedListenersSet = - mPackageModeWatchers.get(packageName); - if (modeChangedListenersSet == null) { - return new ArraySet<>(); - } - return new ArraySet<>(modeChangedListenersSet); - } - } - - @Override - public void notifyWatchersOfChange(int code, int uid) { - ArraySet listenerSet = getOpModeChangedListeners(code); - if (listenerSet == null) { - return; - } - for (int i = 0; i < listenerSet.size(); i++) { - final OnOpModeChangedListener listener = listenerSet.valueAt(i); - notifyOpChanged(listener, code, uid, null); - } - } - - @Override - public void notifyOpChanged(@NonNull OnOpModeChangedListener onModeChangedListener, int code, - int uid, @Nullable String packageName) { - Objects.requireNonNull(onModeChangedListener); - - if (uid != UID_ANY && onModeChangedListener.getWatchingUid() >= 0 - && onModeChangedListener.getWatchingUid() != uid) { - return; - } - - // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE - int[] switchedCodes; - if (onModeChangedListener.getWatchedOpCode() == ALL_OPS) { - switchedCodes = mSwitchedOps.get(code); - } else if (onModeChangedListener.getWatchedOpCode() == OP_NONE) { - switchedCodes = new int[]{code}; - } else { - switchedCodes = new int[]{onModeChangedListener.getWatchedOpCode()}; - } - - for (int switchedCode : switchedCodes) { - // There are features watching for mode changes such as window manager - // and location manager which are in our process. The callbacks in these - // features may require permissions our remote caller does not have. - final long identity = Binder.clearCallingIdentity(); - try { - if (shouldIgnoreCallback(switchedCode, onModeChangedListener.getCallingPid(), - onModeChangedListener.getCallingUid())) { - continue; - } - onModeChangedListener.onOpModeChanged(switchedCode, uid, packageName); - } catch (RemoteException e) { - /* ignore */ - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - 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; - } - - @Override - public void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground, - @Nullable OnOpModeChangedListener callbackToIgnore) { - String[] uidPackageNames = getPackagesForUid(uid); - ArrayMap> callbackSpecs = null; - - synchronized (mLock) { - ArraySet callbacks = mOpModeWatchers.get(code); - if (callbacks != null) { - final int callbackCount = callbacks.size(); - for (int i = 0; i < callbackCount; i++) { - OnOpModeChangedListener callback = callbacks.valueAt(i); - - if (onlyForeground && (callback.getFlags() - & WATCH_FOREGROUND_CHANGES) == 0) { - continue; - } - - ArraySet changedPackages = new ArraySet<>(); - Collections.addAll(changedPackages, uidPackageNames); - if (callbackSpecs == null) { - callbackSpecs = new ArrayMap<>(); - } - callbackSpecs.put(callback, changedPackages); - } - } - - for (String uidPackageName : uidPackageNames) { - callbacks = mPackageModeWatchers.get(uidPackageName); - if (callbacks != null) { - if (callbackSpecs == null) { - callbackSpecs = new ArrayMap<>(); - } - final int callbackCount = callbacks.size(); - for (int i = 0; i < callbackCount; i++) { - OnOpModeChangedListener callback = callbacks.valueAt(i); - - if (onlyForeground && (callback.getFlags() - & WATCH_FOREGROUND_CHANGES) == 0) { - continue; - } - - ArraySet changedPackages = callbackSpecs.get(callback); - if (changedPackages == null) { - changedPackages = new ArraySet<>(); - callbackSpecs.put(callback, changedPackages); - } - changedPackages.add(uidPackageName); - } - } - } - - if (callbackSpecs != null && callbackToIgnore != null) { - callbackSpecs.remove(callbackToIgnore); - } - } - - if (callbackSpecs == null) { - return; - } - - for (int i = 0; i < callbackSpecs.size(); i++) { - final OnOpModeChangedListener callback = callbackSpecs.keyAt(i); - final ArraySet reportedPackageNames = callbackSpecs.valueAt(i); - if (reportedPackageNames == null) { - mHandler.sendMessage(PooledLambda.obtainMessage( - AppOpsCheckingServiceImpl::notifyOpChanged, - this, callback, code, uid, (String) null)); - - } else { - final int reportedPackageCount = reportedPackageNames.size(); - for (int j = 0; j < reportedPackageCount; j++) { - final String reportedPackageName = reportedPackageNames.valueAt(j); - mHandler.sendMessage(PooledLambda.obtainMessage( - AppOpsCheckingServiceImpl::notifyOpChanged, - this, callback, code, uid, reportedPackageName)); - } - } - } - } - - 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; - } - - @Override - public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) { - synchronized (mLock) { - return evalForegroundOps(mUidModes.get(uid), foregroundOps); - } - } - - @Override - public SparseBooleanArray evalForegroundPackageOps(String packageName, - SparseBooleanArray foregroundOps, @UserIdInt int userId) { - synchronized (mLock) { - ArrayMap packageModes = mUserPackageModes.get(userId, null); - return evalForegroundOps(packageModes == null ? null : packageModes.get(packageName), - foregroundOps); - } - } - - private SparseBooleanArray evalForegroundOps(SparseIntArray opModes, - SparseBooleanArray foregroundOps) { - SparseBooleanArray tempForegroundOps = foregroundOps; - if (opModes != null) { - for (int i = opModes.size() - 1; i >= 0; i--) { - if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) { - if (tempForegroundOps == null) { - tempForegroundOps = new SparseBooleanArray(); - } - evalForegroundWatchers(opModes.keyAt(i), tempForegroundOps); - } - } - } - return tempForegroundOps; - } - - private void evalForegroundWatchers(int op, SparseBooleanArray foregroundOps) { - boolean curValue = foregroundOps.get(op, false); - ArraySet listenerSet = mOpModeWatchers.get(op); - if (listenerSet != null) { - for (int cbi = listenerSet.size() - 1; !curValue && cbi >= 0; cbi--) { - if ((listenerSet.valueAt(cbi).getFlags() - & AppOpsManager.WATCH_FOREGROUND_CHANGES) != 0) { - curValue = true; - } - } - } - foregroundOps.put(op, curValue); - } - - @Override - public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, - PrintWriter printWriter) { - boolean needSep = false; - if (mOpModeWatchers.size() > 0) { - boolean printedHeader = false; - for (int i = 0; i < mOpModeWatchers.size(); i++) { - if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) { - continue; - } - boolean printedOpHeader = false; - ArraySet modeChangedListenerSet = - mOpModeWatchers.valueAt(i); - for (int j = 0; j < modeChangedListenerSet.size(); j++) { - final OnOpModeChangedListener listener = modeChangedListenerSet.valueAt(j); - if (dumpPackage != null - && dumpUid != UserHandle.getAppId(listener.getWatchingUid())) { - continue; - } - needSep = true; - if (!printedHeader) { - printWriter.println(" Op mode watchers:"); - printedHeader = true; - } - if (!printedOpHeader) { - printWriter.print(" Op "); - printWriter.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); - printWriter.println(":"); - printedOpHeader = true; - } - printWriter.print(" #"); printWriter.print(j); printWriter.print(": "); - printWriter.println(listener.toString()); + for (int i = 0; i < modes.size(); i++) { + if (modes.valueAt(i) == MODE_FOREGROUND) { + result.put(modes.keyAt(i), true); } } } - if (mPackageModeWatchers.size() > 0 && dumpOp < 0) { - boolean printedHeader = false; - for (int i = 0; i < mPackageModeWatchers.size(); i++) { - if (dumpPackage != null - && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) { - continue; - } - needSep = true; - if (!printedHeader) { - printWriter.println(" Package mode watchers:"); - printedHeader = true; - } - printWriter.print(" Pkg "); printWriter.print(mPackageModeWatchers.keyAt(i)); - printWriter.println(":"); - ArraySet modeChangedListenerSet = - mPackageModeWatchers.valueAt(i); - - for (int j = 0; j < modeChangedListenerSet.size(); j++) { - printWriter.print(" #"); printWriter.print(j); printWriter.print(": "); - printWriter.println(modeChangedListenerSet.valueAt(j).toString()); - } - } - } - return needSep; + return result; } private void scheduleWriteLocked() { diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java index 90968983d4d7f..76f1f8ada8304 100644 --- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java +++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java @@ -16,17 +16,13 @@ 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 com.android.internal.annotations.VisibleForTesting; -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. @@ -148,99 +144,18 @@ public interface AppOpsCheckingServiceInterface { 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. + * @param uid UID to query foreground ops for. + * @return SparseBooleanArray where the keys are the op codes for which their modes are + * MODE_FOREGROUND for the passed UID. */ - void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op); + SparseBooleanArray getForegroundOps(int uid); /** - * 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. + * + * @param packageName Package name to check for. + * @param userId User ID to check for. + * @return SparseBooleanArray where the keys are the op codes for which their modes are + * MODE_FOREGROUND for the passed package name and user ID. */ - 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 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 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, 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(String packageName, - 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, String dumpPackage, PrintWriter printWriter); + SparseBooleanArray getForegroundOps(String packageName, int userId); } diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java index 0094b86975bbe..32dcb5aff8ea6 100644 --- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java +++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java @@ -17,14 +17,10 @@ package com.android.server.appop; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.ArraySet; import android.util.Log; import android.util.SparseBooleanArray; import android.util.SparseIntArray; -import java.io.PrintWriter; - /** * Logging decorator for {@link AppOpsCheckingServiceInterface}. */ @@ -134,83 +130,15 @@ public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServ } @Override - public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, - int op) { - Log.i(LOG_TAG, "startWatchingOpModeChanged(changedListener = " + changedListener + ", op = " - + op + ")"); - mService.startWatchingOpModeChanged(changedListener, op); + public SparseBooleanArray getForegroundOps(int uid) { + Log.i(LOG_TAG, "getForegroundOps(uid = " + uid + ")"); + return mService.getForegroundOps(uid); } @Override - public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, - @NonNull String packageName) { - Log.i(LOG_TAG, "startWatchingPackageModeChanged(changedListener = " + changedListener - + ", packageName = " + packageName + ")"); - mService.startWatchingPackageModeChanged(changedListener, packageName); - } - - @Override - public void removeListener(@NonNull OnOpModeChangedListener changedListener) { - Log.i(LOG_TAG, "removeListener(changedListener = " + changedListener + ")"); - mService.removeListener(changedListener); - } - - @Override - public ArraySet getOpModeChangedListeners(int op) { - Log.i(LOG_TAG, "getOpModeChangedListeners(op = " + op + ")"); - return mService.getOpModeChangedListeners(op); - } - - @Override - public ArraySet getPackageModeChangedListeners( - @NonNull String packageName) { - Log.i(LOG_TAG, "getPackageModeChangedListeners(packageName = " + packageName + ")"); - return mService.getPackageModeChangedListeners(packageName); - } - - @Override - public void notifyWatchersOfChange(int op, int uid) { - Log.i(LOG_TAG, "notifyWatchersOfChange(op = " + op + ", uid = " + uid + ")"); - mService.notifyWatchersOfChange(op, uid); - } - - @Override - public void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid, - @Nullable String packageName) { - Log.i(LOG_TAG, "notifyOpChanged(changedListener = " + changedListener + ", op = " + op - + ", uid = " + uid + ", packageName = " + packageName + ")"); - mService.notifyOpChanged(changedListener, op, uid, packageName); - } - - @Override - public void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground, - @Nullable OnOpModeChangedListener callbackToIgnore) { - Log.i(LOG_TAG, "notifyOpChangedForAllPkgsInUid(op = " + op + ", uid = " + uid - + ", onlyForeground = " + onlyForeground + ", callbackToIgnore = " - + callbackToIgnore + ")"); - mService.notifyOpChangedForAllPkgsInUid(op, uid, onlyForeground, callbackToIgnore); - } - - @Override - public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) { - Log.i(LOG_TAG, "evalForegroundUidOps(uid = " + uid + ", foregroundOps = " + foregroundOps + public SparseBooleanArray getForegroundOps(String packageName, int userId) { + Log.i(LOG_TAG, "getForegroundOps(packageName = " + packageName + ", userId = " + userId + ")"); - return mService.evalForegroundUidOps(uid, foregroundOps); - } - - @Override - public SparseBooleanArray evalForegroundPackageOps(String packageName, - SparseBooleanArray foregroundOps, int userId) { - Log.i(LOG_TAG, "evalForegroundPackageOps(packageName = " + packageName - + ", foregroundOps = " + foregroundOps + ", userId = " + userId + ")"); - return mService.evalForegroundPackageOps(packageName, foregroundOps, userId); - } - - @Override - public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, - PrintWriter printWriter) { - Log.i(LOG_TAG, "dumpListeners(dumpOp = " + dumpOp + ", dumpUid = " + dumpUid - + ", dumpPackage = " + dumpPackage + ", printWriter = " + printWriter + ")"); - return mService.dumpListeners(dumpOp, dumpUid, dumpPackage, printWriter); + return mService.getForegroundOps(packageName, userId); } } diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java index a028ae16da2f3..91dbc23246ed8 100644 --- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java +++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java @@ -17,16 +17,12 @@ 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. @@ -205,128 +201,22 @@ public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServ } @Override - public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, - int op) { + public SparseBooleanArray getForegroundOps(int uid) { Trace.traceBegin(TRACE_TAG, - "TaggedTracingAppOpsCheckingServiceInterfaceImpl#startWatchingOpModeChanged"); + "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getForegroundOps"); try { - mService.startWatchingOpModeChanged(changedListener, op); + return mService.getForegroundOps(uid); } finally { Trace.traceEnd(TRACE_TAG); } } @Override - public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, - @NonNull String packageName) { + public SparseBooleanArray getForegroundOps(String packageName, int userId) { Trace.traceBegin(TRACE_TAG, - "TaggedTracingAppOpsCheckingServiceInterfaceImpl#startWatchingPackageModeChanged"); + "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getForegroundOps"); 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 getOpModeChangedListeners(int op) { - Trace.traceBegin(TRACE_TAG, - "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getOpModeChangedListeners"); - try { - return mService.getOpModeChangedListeners(op); - } finally { - Trace.traceEnd(TRACE_TAG); - } - } - - @Override - public ArraySet 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); + return mService.getForegroundOps(packageName, userId); } finally { Trace.traceEnd(TRACE_TAG); } diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictions.java b/services/core/java/com/android/server/appop/AppOpsRestrictions.java index f7ccd3429f114..0241d02f364bd 100644 --- a/services/core/java/com/android/server/appop/AppOpsRestrictions.java +++ b/services/core/java/com/android/server/appop/AppOpsRestrictions.java @@ -144,4 +144,11 @@ public interface AppOpsRestrictions { */ void dumpRestrictions(PrintWriter printWriter, int dumpOp, String dumpPackage, boolean showUserRestrictions); + + /** + * Listener for when an appop restriction is removed. + */ + interface AppOpsRestrictionRemovedListener { + void onAppOpsRestrictionRemoved(int code); + } } diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java index f51200f2bf0c3..ae93991d3945a 100644 --- a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java +++ b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java @@ -42,7 +42,8 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { private Context mContext; private Handler mHandler; - private AppOpsCheckingServiceInterface mAppOpsCheckingServiceInterface; + + private AppOpsRestrictionRemovedListener mAppOpsRestrictionRemovedListener; // Map from (Object token) to (int code) to (boolean restricted) private final ArrayMap mGlobalRestrictions = new ArrayMap<>(); @@ -56,10 +57,10 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { mUserRestrictionExcludedPackageTags = new ArrayMap<>(); public AppOpsRestrictionsImpl(Context context, Handler handler, - AppOpsCheckingServiceInterface appOpsCheckingServiceInterface) { + AppOpsRestrictionRemovedListener appOpsRestrictionRemovedListener) { mContext = context; mHandler = handler; - mAppOpsCheckingServiceInterface = appOpsCheckingServiceInterface; + mAppOpsRestrictionRemovedListener = appOpsRestrictionRemovedListener; } @Override @@ -211,15 +212,11 @@ public class AppOpsRestrictionsImpl implements AppOpsRestrictions { return allRestrictedCodes; } - // TODO: For clearUserRestrictions, we are calling notifyOpChanged from within the - // LegacyAppOpsServiceInterfaceImpl class. But, for all other changes to restrictions, we're - // calling it from within AppOpsService. This is awkward, and we should probably do it one - // way or the other. private void notifyAllUserRestrictions(SparseBooleanArray allUserRestrictedCodes) { int restrictedCodesSize = allUserRestrictedCodes.size(); for (int j = 0; j < restrictedCodesSize; j++) { int code = allUserRestrictedCodes.keyAt(j); - mHandler.post(() -> mAppOpsCheckingServiceInterface.notifyWatchersOfChange(code, UID_ANY)); + mHandler.post(() -> mAppOpsRestrictionRemovedListener.onAppOpsRestrictionRemoved(code)); } } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 67489b8e9cf32..018db17b18a92 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -52,6 +52,7 @@ 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.WATCH_FOREGROUND_CHANGES; import static android.app.AppOpsManager._NUM_OP; import static android.app.AppOpsManager.extractFlagsFromKey; import static android.app.AppOpsManager.extractUidStateFromKey; @@ -293,6 +294,11 @@ public class AppOpsService extends IAppOpsService.Stub { private final ArrayMap, ArrayList> mUnforwardedAsyncNotedOps = new ArrayMap<>(); + private final SparseArray> mOpModeWatchers = + new SparseArray<>(); + private final ArrayMap> mPackageModeWatchers = + new ArrayMap<>(); + boolean mWriteNoteOpsScheduled; boolean mWriteScheduled; @@ -316,6 +322,8 @@ public class AppOpsService extends IAppOpsService.Stub { @GuardedBy("this") @VisibleForTesting final SparseArray mUidStates = new SparseArray<>(); + @GuardedBy("this") + private boolean mUidStatesInitialized; volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this); @@ -342,8 +350,6 @@ public class AppOpsService extends IAppOpsService.Stub { */ private final SparseArray mSwitchedOps = new SparseArray<>(); - private ActivityManagerInternal mActivityManagerInternal; - /** Package sampled for message collection in the current session */ @GuardedBy("this") private String mSampledPackage = null; @@ -506,11 +512,6 @@ public class AppOpsService extends IAppOpsService.Stub { @NonNull public final ArrayMap pkgOps = new ArrayMap<>(); - // 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; } @@ -541,25 +542,6 @@ public class AppOpsService extends IAppOpsService.Stub { return getUidStateTracker().evalMode(uid, op, mode); } - public void evalForegroundOps() { - foregroundOps = null; - foregroundOps = mAppOpsCheckingService.evalForegroundUidOps(uid, foregroundOps); - for (int i = pkgOps.size() - 1; i >= 0; i--) { - foregroundOps = mAppOpsCheckingService - .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); @@ -941,7 +923,8 @@ public class AppOpsService extends IAppOpsService.Stub { storageFile, this, handler, context, mSwitchedOps)); //mAppOpsCheckingService = new AppOpsCheckingServiceLoggingDecorator( // LocalServices.getService(AppOpsCheckingServiceInterface.class)); - mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler, mAppOpsCheckingService); + mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler, + code -> notifyWatchersOfChange(code, UID_ANY)); LockGuard.installLock(this, LockGuard.INDEX_APP_OPS); mStorageFile = new AtomicFile(storageFile, "appops_legacy"); @@ -1109,8 +1092,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int code : OPS_RESTRICTED_ON_SUSPEND) { ArraySet onModeChangedListeners; synchronized (AppOpsService.this) { - onModeChangedListeners = - mAppOpsCheckingService.getOpModeChangedListeners(code); + onModeChangedListeners = mOpModeWatchers.get(code); if (onModeChangedListeners == null) { continue; } @@ -1150,8 +1132,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } }); - - mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } @VisibleForTesting @@ -1223,6 +1203,7 @@ public class AppOpsService extends IAppOpsService.Stub { for (int uid : NON_PACKAGE_UIDS) { mUidStates.put(uid, new UidState(uid)); } + mUidStatesInitialized = true; } } @@ -1266,8 +1247,6 @@ public class AppOpsService extends IAppOpsService.Stub { ops.put(code, new Op(uidState, packageName, code, uid)); } } - - uidState.evalForegroundOps(); } /** @@ -1343,12 +1322,40 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { UidState uidState = getUidStateLocked(uid, false); - if (uidState != null && foregroundModeMayChange && uidState.hasForegroundWatchers) { - for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) { - if (!uidState.foregroundOps.valueAt(fgi)) { + boolean hasForegroundWatchers = false; + + for (int i = 0; i < mModeWatchers.size(); i++) { + ModeCallback cb = mModeWatchers.valueAt(i); + if (cb.isWatchingUid(uid) && (cb.getFlags() & WATCH_FOREGROUND_CHANGES) != 0) { + hasForegroundWatchers = true; + break; + } + } + + if (uidState != null && foregroundModeMayChange && hasForegroundWatchers) { + + SparseBooleanArray foregroundOps = new SparseBooleanArray(); + + SparseBooleanArray uidForegroundOps = mAppOpsCheckingService.getForegroundOps(uid); + for (int i = 0; i < uidForegroundOps.size(); i++) { + foregroundOps.put(uidForegroundOps.keyAt(i), true); + } + String[] uidPackageNames = getPackagesForUid(uid); + + int userId = UserHandle.getUserId(uid); + for (String packageName : uidPackageNames) { + SparseBooleanArray packageForegroundOps = + mAppOpsCheckingService.getForegroundOps(packageName, userId); + for (int i = 0; i < packageForegroundOps.size(); i++) { + foregroundOps.put(packageForegroundOps.keyAt(i), true); + } + } + + for (int fgi = foregroundOps.size() - 1; fgi >= 0; fgi--) { + if (!foregroundOps.valueAt(fgi)) { continue; } - final int code = uidState.foregroundOps.keyAt(fgi); + final int code = foregroundOps.keyAt(fgi); if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code) && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) { @@ -1357,7 +1364,7 @@ public class AppOpsService extends IAppOpsService.Stub { this, code, uidState.uid, true, null)); } else if (!uidState.pkgOps.isEmpty()) { final ArraySet listenerSet = - mAppOpsCheckingService.getOpModeChangedListeners(code); + mOpModeWatchers.get(code); if (listenerSet != null) { for (int cbi = listenerSet.size() - 1; cbi >= 0; cbi--) { final OnOpModeChangedListener listener = listenerSet.valueAt(cbi); @@ -1823,7 +1830,6 @@ public class AppOpsService extends IAppOpsService.Stub { if (!uidState.setUidMode(code, mode)) { return; } - uidState.evalForegroundOps(); if (mode != MODE_ERRORED && mode != previousMode) { updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid); } @@ -1842,10 +1848,85 @@ public class AppOpsService extends IAppOpsService.Stub { */ private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground, @Nullable IAppOpsCallback callbackToIgnore) { - ModeCallback listenerToIgnore = callbackToIgnore != null - ? mModeWatchers.get(callbackToIgnore.asBinder()) : null; - mAppOpsCheckingService.notifyOpChangedForAllPkgsInUid(code, uid, onlyForeground, - listenerToIgnore); + String[] uidPackageNames = getPackagesForUid(uid); + ArrayMap> callbackSpecs = null; + synchronized (this) { + ArraySet callbacks = mOpModeWatchers.get(code); + if (callbacks != null) { + final int callbackCount = callbacks.size(); + for (int i = 0; i < callbackCount; i++) { + OnOpModeChangedListener callback = callbacks.valueAt(i); + + if (!callback.isWatchingUid(uid)) { + continue; + } + + if (onlyForeground && (callback.getFlags() + & WATCH_FOREGROUND_CHANGES) == 0) { + continue; + } + + ArraySet changedPackages = new ArraySet<>(); + Collections.addAll(changedPackages, uidPackageNames); + if (callbackSpecs == null) { + callbackSpecs = new ArrayMap<>(); + } + callbackSpecs.put(callback, changedPackages); + } + } + + for (String uidPackageName : uidPackageNames) { + callbacks = mPackageModeWatchers.get(uidPackageName); + if (callbacks != null) { + if (callbackSpecs == null) { + callbackSpecs = new ArrayMap<>(); + } + final int callbackCount = callbacks.size(); + for (int i = 0; i < callbackCount; i++) { + OnOpModeChangedListener callback = callbacks.valueAt(i); + + if (onlyForeground && (callback.getFlags() + & WATCH_FOREGROUND_CHANGES) == 0) { + continue; + } + + ArraySet changedPackages = callbackSpecs.get(callback); + if (changedPackages == null) { + changedPackages = new ArraySet<>(); + callbackSpecs.put(callback, changedPackages); + } + changedPackages.add(uidPackageName); + } + } + } + + if (callbackSpecs != null && callbackToIgnore != null) { + callbackSpecs.remove(mModeWatchers.get(callbackToIgnore.asBinder())); + } + } + + if (callbackSpecs == null) { + return; + } + + for (int i = 0; i < callbackSpecs.size(); i++) { + final OnOpModeChangedListener callback = callbackSpecs.keyAt(i); + final ArraySet reportedPackageNames = callbackSpecs.valueAt(i); + if (reportedPackageNames == null) { + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, callback, code, uid, (String) null)); + + } else { + final int reportedPackageCount = reportedPackageNames.size(); + for (int j = 0; j < reportedPackageCount; j++) { + final String reportedPackageName = reportedPackageNames.valueAt(j); + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, callback, code, uid, reportedPackageName)); + } + } + } } private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) { @@ -1992,19 +2073,15 @@ public class AppOpsService extends IAppOpsService.Stub { if (op.getMode() != mode) { previousMode = op.getMode(); op.setMode(mode); - - if (uidState != null) { - uidState.evalForegroundOps(); - } ArraySet cbs = - mAppOpsCheckingService.getOpModeChangedListeners(code); + mOpModeWatchers.get(code); if (cbs != null) { if (repCbs == null) { repCbs = new ArraySet<>(); } repCbs.addAll(cbs); } - cbs = mAppOpsCheckingService.getPackageModeChangedListeners(packageName); + cbs = mPackageModeWatchers.get(packageName); if (cbs != null) { if (repCbs == null) { repCbs = new ArraySet<>(); @@ -2043,9 +2120,42 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void notifyOpChanged(OnOpModeChangedListener callback, int code, + private void notifyOpChanged(OnOpModeChangedListener onModeChangedListener, int code, int uid, String packageName) { - mAppOpsCheckingService.notifyOpChanged(callback, code, uid, packageName); + Objects.requireNonNull(onModeChangedListener); + + if (uid != UID_ANY && onModeChangedListener.getWatchingUid() >= 0 + && onModeChangedListener.getWatchingUid() != uid) { + return; + } + + // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE + int[] switchedCodes; + if (onModeChangedListener.getWatchedOpCode() == ALL_OPS) { + switchedCodes = mSwitchedOps.get(code); + } else if (onModeChangedListener.getWatchedOpCode() == OP_NONE) { + switchedCodes = new int[]{code}; + } else { + switchedCodes = new int[]{onModeChangedListener.getWatchedOpCode()}; + } + + for (int switchedCode : switchedCodes) { + // There are features watching for mode changes such as window manager + // and location manager which are in our process. The callbacks in these + // features may require permissions our remote caller does not have. + final long identity = Binder.clearCallingIdentity(); + try { + if (shouldIgnoreCallback(switchedCode, onModeChangedListener.getCallingPid(), + onModeChangedListener.getCallingUid())) { + continue; + } + onModeChangedListener.onOpModeChanged(switchedCode, uid, packageName); + } catch (RemoteException e) { + /* ignore */ + } finally { + Binder.restoreCallingIdentity(identity); + } + } } private static ArrayList addChange(ArrayList reports, @@ -2144,11 +2254,9 @@ public class AppOpsService extends IAppOpsService.Stub { uidState.setUidMode(code, newMode); for (String packageName : getPackagesForUid(uidState.uid)) { callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, - previousMode, - mAppOpsCheckingService.getOpModeChangedListeners(code)); + previousMode, mOpModeWatchers.get(code)); callbacks = addCallbacks(callbacks, code, uidState.uid, packageName, - previousMode, mAppOpsCheckingService - .getPackageModeChangedListeners(packageName)); + previousMode, mPackageModeWatchers.get(packageName)); allChanges = addChange(allChanges, code, uidState.uid, packageName, previousMode); @@ -2197,11 +2305,9 @@ public class AppOpsService extends IAppOpsService.Stub { uidChanged = true; final int uid = curOp.uidState.uid; callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, - previousMode, - mAppOpsCheckingService.getOpModeChangedListeners(curOp.op)); + previousMode, mOpModeWatchers.get(curOp.op)); callbacks = addCallbacks(callbacks, curOp.op, uid, packageName, - previousMode, mAppOpsCheckingService - .getPackageModeChangedListeners(packageName)); + previousMode, mPackageModeWatchers.get(packageName)); allChanges = addChange(allChanges, curOp.op, uid, packageName, previousMode); @@ -2217,9 +2323,6 @@ public class AppOpsService extends IAppOpsService.Stub { UserHandle.getUserId(uidState.uid)); } } - if (uidChanged) { - uidState.evalForegroundOps(); - } } if (changed) { @@ -2296,15 +2399,6 @@ public class AppOpsService extends IAppOpsService.Stub { 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 startWatchingMode(int op, String packageName, IAppOpsCallback callback) { startWatchingModeWithFlags(op, packageName, 0, callback); @@ -2348,12 +2442,21 @@ public class AppOpsService extends IAppOpsService.Stub { mModeWatchers.put(callback.asBinder(), cb); } if (switchOp != AppOpsManager.OP_NONE) { - mAppOpsCheckingService.startWatchingOpModeChanged(cb, switchOp); + ArraySet cbs = mOpModeWatchers.get(switchOp); + if (cbs == null) { + cbs = new ArraySet<>(); + mOpModeWatchers.put(switchOp, cbs); + } + cbs.add(cb); } if (mayWatchPackageName) { - mAppOpsCheckingService.startWatchingPackageModeChanged(cb, packageName); + ArraySet cbs = mPackageModeWatchers.get(packageName); + if (cbs == null) { + cbs = new ArraySet<>(); + mPackageModeWatchers.put(packageName, cbs); + } + cbs.add(cb); } - evalAllForegroundOpsLocked(); } } @@ -2366,10 +2469,21 @@ public class AppOpsService extends IAppOpsService.Stub { ModeCallback cb = mModeWatchers.remove(callback.asBinder()); if (cb != null) { cb.unlinkToDeath(); - mAppOpsCheckingService.removeListener(cb); + for (int i = mOpModeWatchers.size() - 1; i >= 0; i--) { + ArraySet cbs = mOpModeWatchers.valueAt(i); + cbs.remove(cb); + if (cbs.size() <= 0) { + mOpModeWatchers.removeAt(i); + } + } + for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) { + ArraySet cbs = mPackageModeWatchers.valueAt(i); + cbs.remove(cb); + if (cbs.size() <= 0) { + mPackageModeWatchers.removeAt(i); + } + } } - - evalAllForegroundOpsLocked(); } } @@ -3722,7 +3836,7 @@ public class AppOpsService extends IAppOpsService.Stub { /** * Create a restriction description matching the properties of the package. * - * @param pkg The package to create the restriction description for + * @param packageState The package to create the restriction description for * * @return The restriction matching the package */ @@ -5152,8 +5266,55 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(); } - if (!dumpHistory) { - needSep |= mAppOpsCheckingService.dumpListeners(dumpOp, dumpUid, dumpPackage, pw); + if (mOpModeWatchers.size() > 0 && !dumpHistory) { + boolean printedHeader = false; + for (int i = 0; i < mOpModeWatchers.size(); i++) { + if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) { + continue; + } + boolean printedOpHeader = false; + ArraySet callbacks = mOpModeWatchers.valueAt(i); + for (int j = 0; j < callbacks.size(); j++) { + final OnOpModeChangedListener cb = callbacks.valueAt(j); + if (dumpPackage != null + && dumpUid != UserHandle.getAppId(cb.getWatchingUid())) { + continue; + } + needSep = true; + if (!printedHeader) { + pw.println(" Op mode watchers:"); + printedHeader = true; + } + if (!printedOpHeader) { + pw.print(" Op "); + pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); + pw.println(":"); + printedOpHeader = true; + } + pw.print(" #"); pw.print(j); pw.print(": "); + pw.println(cb); + } + } + } + if (mPackageModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) { + boolean printedHeader = false; + for (int i = 0; i < mPackageModeWatchers.size(); i++) { + if (dumpPackage != null && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) { + continue; + } + needSep = true; + if (!printedHeader) { + pw.println(" Package mode watchers:"); + printedHeader = true; + } + pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i)); + pw.println(":"); + ArraySet callbacks = mPackageModeWatchers.valueAt(i); + for (int j = 0; j < callbacks.size(); j++) { + pw.print(" #"); pw.print(j); pw.print(": "); + pw.println(callbacks.valueAt(j)); + } + } } if (mModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) { @@ -5353,11 +5514,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } } - if (uidState.foregroundOps != null && !hasOp) { - if (uidState.foregroundOps.indexOfKey(dumpOp) > 0) { - hasOp = true; - } - } if (!hasOp || !hasPackage || !hasMode) { continue; } @@ -5365,21 +5521,6 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -5581,7 +5722,7 @@ public class AppOpsService extends IAppOpsService.Stub { private void notifyWatchersOfChange(int code, int uid) { final ArraySet modeChangedListenerSet; synchronized (this) { - modeChangedListenerSet = mAppOpsCheckingService.getOpModeChangedListeners(code); + modeChangedListenerSet = mOpModeWatchers.get(code); if (modeChangedListenerSet == null) { return; } diff --git a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java new file mode 100644 index 0000000000000..c605375e1d3f5 --- /dev/null +++ b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java @@ -0,0 +1,204 @@ +/* + * 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.util.SparseBooleanArray; +import android.util.SparseIntArray; + +import java.util.Objects; + +/** + * A testing shim, which supports running two variants of an AppOpsServiceInterface at once, + * and checking the results of both. + */ +public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface { + + private AppOpsCheckingServiceInterface mOldImplementation; + private AppOpsCheckingServiceInterface mNewImplementation; + + public AppOpsServiceTestingShim(AppOpsCheckingServiceInterface oldValImpl, + AppOpsCheckingServiceInterface newImpl) { + mOldImplementation = oldValImpl; + mNewImplementation = newImpl; + } + + private void signalImplDifference(String message) { + //TODO b/252886104 implement + } + + @Override + public void writeState() { + mOldImplementation.writeState(); + mNewImplementation.writeState(); + } + + @Override + public void readState() { + mOldImplementation.readState(); + mNewImplementation.readState(); + } + + @Override + public void shutdown() { + mOldImplementation.shutdown(); + mNewImplementation.shutdown(); + } + + @Override + public void systemReady() { + mOldImplementation.systemReady(); + mNewImplementation.systemReady(); + } + + @Override + public SparseIntArray getNonDefaultUidModes(int uid) { + SparseIntArray oldVal = mOldImplementation.getNonDefaultUidModes(uid); + SparseIntArray newVal = mNewImplementation.getNonDefaultUidModes(uid); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getNonDefaultUidModes"); + } + + return newVal; + } + + @Override + public SparseIntArray getNonDefaultPackageModes(String packageName, int userId) { + SparseIntArray oldVal = mOldImplementation.getNonDefaultPackageModes(packageName, userId); + SparseIntArray newVal = mNewImplementation.getNonDefaultPackageModes(packageName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getNonDefaultPackageModes"); + } + + return newVal; + } + + @Override + public int getUidMode(int uid, int op) { + int oldVal = mOldImplementation.getUidMode(uid, op); + int newVal = mNewImplementation.getUidMode(uid, op); + + if (oldVal != newVal) { + signalImplDifference("getUidMode"); + } + + return newVal; + } + + @Override + public boolean setUidMode(int uid, int op, int mode) { + boolean oldVal = mOldImplementation.setUidMode(uid, op, mode); + boolean newVal = mNewImplementation.setUidMode(uid, op, mode); + + if (oldVal != newVal) { + signalImplDifference("setUidMode"); + } + + return newVal; + } + + @Override + public int getPackageMode(String packageName, int op, int userId) { + int oldVal = mOldImplementation.getPackageMode(packageName, op, userId); + int newVal = mNewImplementation.getPackageMode(packageName, op, userId); + + if (oldVal != newVal) { + signalImplDifference("getPackageMode"); + } + + return newVal; + } + + @Override + public void setPackageMode(String packageName, int op, int mode, int userId) { + mOldImplementation.setPackageMode(packageName, op, mode, userId); + mNewImplementation.setPackageMode(packageName, op, mode, userId); + } + + @Override + public boolean removePackage(String packageName, int userId) { + boolean oldVal = mOldImplementation.removePackage(packageName, userId); + boolean newVal = mNewImplementation.removePackage(packageName, userId); + + if (oldVal != newVal) { + signalImplDifference("removePackage"); + } + + return newVal; + } + + @Override + public void removeUid(int uid) { + mOldImplementation.removeUid(uid); + mNewImplementation.removeUid(uid); + } + + @Override + public boolean areUidModesDefault(int uid) { + boolean oldVal = mOldImplementation.areUidModesDefault(uid); + boolean newVal = mNewImplementation.areUidModesDefault(uid); + + if (oldVal != newVal) { + signalImplDifference("areUidModesDefault"); + } + + return newVal; + } + + @Override + public boolean arePackageModesDefault(String packageName, int userId) { + boolean oldVal = mOldImplementation.arePackageModesDefault(packageName, userId); + boolean newVal = mNewImplementation.arePackageModesDefault(packageName, userId); + + if (oldVal != newVal) { + signalImplDifference("arePackageModesDefault"); + } + + return newVal; + } + + @Override + public void clearAllModes() { + mOldImplementation.clearAllModes(); + mNewImplementation.clearAllModes(); + } + + @Override + public SparseBooleanArray getForegroundOps(int uid) { + SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(uid); + SparseBooleanArray newVal = mNewImplementation.getForegroundOps(uid); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getForegroundOps"); + } + + return newVal; + } + + @Override + public SparseBooleanArray getForegroundOps(String packageName, int userId) { + SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(packageName, userId); + SparseBooleanArray newVal = mNewImplementation.getForegroundOps(packageName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getForegroundOps"); + } + + return newVal; + } +} diff --git a/services/core/java/com/android/server/pm/permission/AccessTestingShimFactory.java b/services/core/java/com/android/server/pm/permission/AccessTestingShimFactory.java new file mode 100644 index 0000000000000..0682e92509939 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/AccessTestingShimFactory.java @@ -0,0 +1,75 @@ +/* + * 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.pm.permission; + +import static android.provider.DeviceConfig.NAMESPACE_PRIVACY; + +import android.content.Context; +import android.provider.DeviceConfig; + +import com.android.server.appop.AppOpsCheckingServiceInterface; +import com.android.server.appop.AppOpsServiceTestingShim; + +import java.util.function.Supplier; + +/** + * A factory which will select one or both implementations of a PermissionManagerServiceInterface or + * AppOpsCheckingServiceInterface, based upon either a DeviceConfig value, or a hard coded config. + */ +public class AccessTestingShimFactory { + + private static final int RUN_OLD_SUBSYSTEM = 0; + private static final int RUN_NEW_SUBSYSTEM = 1; + private static final int RUN_BOTH_SUBSYSTEMS = 2; + public static final String DEVICE_CONFIG_SETTING = "selected_access_subsystem"; + + /** + * Get the PermissionManagerServiceInterface, based upon the current config state. + */ + public static PermissionManagerServiceInterface getPms(Context context, + Supplier oldImpl, + Supplier newImpl) { + int selectedSystem = DeviceConfig.getInt(NAMESPACE_PRIVACY, + DEVICE_CONFIG_SETTING, RUN_OLD_SUBSYSTEM); + switch (selectedSystem) { + case RUN_BOTH_SUBSYSTEMS: + return new PermissionManagerServiceTestingShim(oldImpl.get(), newImpl.get()); + case RUN_NEW_SUBSYSTEM: + return newImpl.get(); + default: + return oldImpl.get(); + } + } + + /** + * Get the AppOpsCheckingServiceInterface, based upon the current config state. + */ + public static AppOpsCheckingServiceInterface getAos(Context context, + Supplier oldImpl, + Supplier newImpl) { + int selectedSystem = DeviceConfig.getInt(NAMESPACE_PRIVACY, + DEVICE_CONFIG_SETTING, RUN_OLD_SUBSYSTEM); + switch (selectedSystem) { + case RUN_BOTH_SUBSYSTEMS: + return new AppOpsServiceTestingShim(oldImpl.get(), newImpl.get()); + case RUN_NEW_SUBSYSTEM: + return newImpl.get(); + default: + return oldImpl.get(); + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java new file mode 100644 index 0000000000000..3db08deab7b4d --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java @@ -0,0 +1,565 @@ +/* + * 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.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.permission.SplitPermissionInfoParcelable; +import android.permission.IOnPermissionsChangeListener; + +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageState; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * A testing shim, which supports running two variants of a PermissionManagerServiceInterface at + * once, and checking the results of both. + */ +public class PermissionManagerServiceTestingShim implements PermissionManagerServiceInterface { + + private PermissionManagerServiceInterface mOldImplementation; + private PermissionManagerServiceInterface mNewImplementation; + + public PermissionManagerServiceTestingShim(PermissionManagerServiceInterface oldImpl, + PermissionManagerServiceInterface newImpl) { + mOldImplementation = oldImpl; + mNewImplementation = newImpl; + } + + private void signalImplDifference(String message) { + //TODO b/252886104 implement + } + + + @Nullable + @Override + public byte[] backupRuntimePermissions(int userId) { + byte[] oldVal = mOldImplementation.backupRuntimePermissions(userId); + byte[] newVal = mNewImplementation.backupRuntimePermissions(userId); + if (!Arrays.equals(oldVal, newVal)) { + signalImplDifference("backupRuntimePermissions"); + } + + return newVal; + } + + @Override + public void restoreRuntimePermissions(@NonNull byte[] backup, int userId) { + mOldImplementation.backupRuntimePermissions(userId); + mNewImplementation.backupRuntimePermissions(userId); + } + + @Override + public void restoreDelayedRuntimePermissions(@NonNull String packageName, int userId) { + mOldImplementation.restoreDelayedRuntimePermissions(packageName, userId); + mNewImplementation.restoreDelayedRuntimePermissions(packageName, userId); + + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mOldImplementation.dump(fd, pw, args); + mNewImplementation.dump(fd, pw, args); + } + + @Override + public List getAllPermissionGroups(int flags) { + List oldVal = mOldImplementation.getAllPermissionGroups(flags); + List newVal = mNewImplementation.getAllPermissionGroups(flags); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getAllPermissionGroups"); + } + return newVal; + } + + @Override + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { + PermissionGroupInfo oldVal = mOldImplementation.getPermissionGroupInfo(groupName, flags); + PermissionGroupInfo newVal = mNewImplementation.getPermissionGroupInfo(groupName, flags); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getPermissionGroupInfo"); + } + return newVal; + } + + @Override + public PermissionInfo getPermissionInfo(@NonNull String permName, int flags, + @NonNull String opPackageName) { + PermissionInfo oldVal = mOldImplementation.getPermissionInfo(permName, flags, + opPackageName); + PermissionInfo newVal = mNewImplementation.getPermissionInfo(permName, flags, + opPackageName); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getPermissionInfo"); + } + return newVal; + } + + @Override + public List queryPermissionsByGroup(String groupName, int flags) { + List oldVal = mOldImplementation.queryPermissionsByGroup(groupName, + flags); + List newVal = mNewImplementation.queryPermissionsByGroup(groupName, flags); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("queryPermissionsByGroup"); + } + return newVal; + } + + @Override + public boolean addPermission(PermissionInfo info, boolean async) { + boolean oldVal = mOldImplementation.addPermission(info, async); + boolean newVal = mNewImplementation.addPermission(info, async); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("addPermission"); + } + return newVal; + } + + @Override + public void removePermission(String permName) { + mOldImplementation.removePermission(permName); + mNewImplementation.removePermission(permName); + } + + @Override + public int getPermissionFlags(String packageName, String permName, int userId) { + int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, userId); + int newVal = mNewImplementation.getPermissionFlags(packageName, permName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getPermissionFlags"); + } + return newVal; + } + + @Override + public void updatePermissionFlags(String packageName, String permName, int flagMask, + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { + mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues, + checkAdjustPolicyFlagPermission, userId); + mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues, + checkAdjustPolicyFlagPermission, userId); + } + + @Override + public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { + mOldImplementation.updatePermissionFlagsForAllApps(flagMask, flagValues, userId); + mNewImplementation.updatePermissionFlagsForAllApps(flagMask, flagValues, userId); + } + + @Override + public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { + mOldImplementation.addOnPermissionsChangeListener(listener); + mNewImplementation.addOnPermissionsChangeListener(listener); + } + + @Override + public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { + mOldImplementation.removeOnPermissionsChangeListener(listener); + mNewImplementation.removeOnPermissionsChangeListener(listener); + } + + @Override + public boolean addAllowlistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, int flags, int userId) { + boolean oldVal = mOldImplementation.addAllowlistedRestrictedPermission(packageName, + permName, + flags, userId); + boolean newVal = mNewImplementation.addAllowlistedRestrictedPermission(packageName, + permName, flags, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("addAllowlistedRestrictedPermission"); + } + return newVal; + } + + @Override + public List getAllowlistedRestrictedPermissions(@NonNull String packageName, int flags, + int userId) { + List oldVal = mOldImplementation.getAllowlistedRestrictedPermissions(packageName, + flags, userId); + List newVal = mNewImplementation.getAllowlistedRestrictedPermissions(packageName, + flags, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getAllowlistedRestrictedPermissions"); + } + return newVal; + } + + @Override + public boolean removeAllowlistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, int flags, int userId) { + boolean oldVal = mOldImplementation.removeAllowlistedRestrictedPermission(packageName, + permName, flags, userId); + boolean newVal = mNewImplementation.removeAllowlistedRestrictedPermission(packageName, + permName, flags, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("removeAllowlistedRestrictedPermission"); + } + return newVal; + } + + @Override + public void grantRuntimePermission(String packageName, String permName, int userId) { + mOldImplementation.grantRuntimePermission(packageName, permName, userId); + mNewImplementation.grantRuntimePermission(packageName, permName, userId); + } + + @Override + public void revokeRuntimePermission(String packageName, String permName, int userId, + String reason) { + mOldImplementation.grantRuntimePermission(packageName, permName, userId); + mNewImplementation.grantRuntimePermission(packageName, permName, userId); + } + + @Override + public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) { + mOldImplementation.revokePostNotificationPermissionWithoutKillForTest(packageName, + userId); + mNewImplementation.revokePostNotificationPermissionWithoutKillForTest(packageName, userId); + } + + @Override + public boolean shouldShowRequestPermissionRationale(String packageName, String permName, + int userId) { + boolean oldVal = mOldImplementation + .shouldShowRequestPermissionRationale(packageName, permName, userId); + boolean newVal = mNewImplementation + .shouldShowRequestPermissionRationale(packageName, permName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("shouldShowRequestPermissionRationale"); + } + return newVal; + } + + @Override + public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) { + boolean oldVal = mOldImplementation + .isPermissionRevokedByPolicy(packageName, permName, userId); + boolean newVal = mNewImplementation.isPermissionRevokedByPolicy(packageName, permName, + userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("isPermissionRevokedByPolicy"); + } + return newVal; + } + + @Override + public List getSplitPermissions() { + List oldVal = mOldImplementation.getSplitPermissions(); + List newVal = mNewImplementation.getSplitPermissions(); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getSplitPermissions"); + } + return newVal; + } + + @Override + public int checkPermission(String pkgName, String permName, int userId) { + int oldVal = mOldImplementation.checkPermission(pkgName, permName, userId); + int newVal = mNewImplementation.checkPermission(pkgName, permName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("checkPermission"); + } + return newVal; + } + + @Override + public int checkUidPermission(int uid, String permName) { + int oldVal = mOldImplementation.checkUidPermission(uid, permName); + int newVal = mNewImplementation.checkUidPermission(uid, permName); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("checkUidPermission"); + } + return newVal; + } + + @Override + public void addOnRuntimePermissionStateChangedListener(@NonNull + PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener listener) { + mOldImplementation.addOnRuntimePermissionStateChangedListener(listener); + mNewImplementation.addOnRuntimePermissionStateChangedListener(listener); + } + + @Override + public void removeOnRuntimePermissionStateChangedListener(@NonNull + PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener listener) { + mOldImplementation.removeOnRuntimePermissionStateChangedListener(listener); + mNewImplementation.removeOnRuntimePermissionStateChangedListener(listener); + } + + @Override + public Map> getAllAppOpPermissionPackages() { + Map> oldVal = mOldImplementation.getAllAppOpPermissionPackages(); + Map> newVal = mNewImplementation.getAllAppOpPermissionPackages(); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getAllAppOpPermissionPackages"); + } + return newVal; + } + + @Override + public boolean isPermissionsReviewRequired(@NonNull String packageName, int userId) { + boolean oldVal = mOldImplementation.isPermissionsReviewRequired(packageName, userId); + boolean newVal = mNewImplementation.isPermissionsReviewRequired(packageName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("isPermissionsReviewRequired"); + } + return newVal; + } + + @Override + public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) { + mOldImplementation.resetRuntimePermissions(pkg, userId); + mNewImplementation.resetRuntimePermissions(pkg, userId); + } + + @Override + public void resetRuntimePermissionsForUser(int userId) { + mOldImplementation.resetRuntimePermissionsForUser(userId); + mNewImplementation.resetRuntimePermissionsForUser(userId); + } + + @Override + public void readLegacyPermissionStateTEMP() { + mOldImplementation.readLegacyPermissionStateTEMP(); + mNewImplementation.readLegacyPermissionStateTEMP(); + } + + @Override + public void writeLegacyPermissionStateTEMP() { + mOldImplementation.writeLegacyPermissionStateTEMP(); + mNewImplementation.writeLegacyPermissionStateTEMP(); + } + + @Override + public Set getInstalledPermissions(String packageName) { + Set oldVal = mOldImplementation.getInstalledPermissions(packageName); + Set newVal = mNewImplementation.getInstalledPermissions(packageName); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getInstalledPermissions"); + } + return newVal; + } + + @NonNull + @Override + public Set getGrantedPermissions(@NonNull String packageName, int userId) { + Set oldVal = mOldImplementation.getGrantedPermissions(packageName, userId); + Set newVal = mNewImplementation.getGrantedPermissions(packageName, userId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getGrantedPermissions"); + } + return newVal; + } + + @NonNull + @Override + public int[] getPermissionGids(@NonNull String permissionName, int userId) { + int[] oldVal = mOldImplementation.getPermissionGids(permissionName, userId); + int[] newVal = mNewImplementation.getPermissionGids(permissionName, userId); + + if (!Arrays.equals(oldVal, newVal)) { + signalImplDifference("getPermissionGids"); + } + return newVal; + } + + @NonNull + @Override + public String[] getAppOpPermissionPackages(@NonNull String permissionName) { + String[] oldVal = mOldImplementation.getAppOpPermissionPackages(permissionName); + String[] newVal = mNewImplementation.getAppOpPermissionPackages(permissionName); + + if (!Arrays.equals(oldVal, newVal)) { + signalImplDifference("getAppOpPermissionPackages"); + } + return newVal; + } + + @Nullable + @Override + public Permission getPermissionTEMP(@NonNull String permName) { + Permission oldVal = mOldImplementation.getPermissionTEMP(permName); + Permission newVal = mNewImplementation.getPermissionTEMP(permName); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getPermissionTEMP"); + } + return newVal; + } + + @NonNull + @Override + public List getAllPermissionsWithProtection(int protection) { + List oldVal = mOldImplementation.getAllPermissionsWithProtection( + protection); + List newVal = mNewImplementation.getAllPermissionsWithProtection( + protection); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getAllPermissionsWithProtection"); + } + return newVal; + } + + @NonNull + @Override + public List getAllPermissionsWithProtectionFlags(int protectionFlags) { + List oldVal = mOldImplementation + .getAllPermissionsWithProtectionFlags(protectionFlags); + List newVal = mNewImplementation.getAllPermissionsWithProtectionFlags( + protectionFlags); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getAllPermissionsWithProtectionFlags"); + } + return newVal; + } + + @NonNull + @Override + public List getLegacyPermissions() { + List oldVal = mOldImplementation.getLegacyPermissions(); + List newVal = mNewImplementation.getLegacyPermissions(); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getLegacyPermissions"); + } + return newVal; + } + + @NonNull + @Override + public LegacyPermissionState getLegacyPermissionState(int appId) { + LegacyPermissionState oldVal = mOldImplementation.getLegacyPermissionState(appId); + LegacyPermissionState newVal = mNewImplementation.getLegacyPermissionState(appId); + + if (!Objects.equals(oldVal, newVal)) { + signalImplDifference("getLegacyPermissionState"); + } + return newVal; + } + + @Override + public void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + mOldImplementation.readLegacyPermissionsTEMP(legacyPermissionSettings); + mNewImplementation.readLegacyPermissionsTEMP(legacyPermissionSettings); + } + + @Override + public void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + mOldImplementation.writeLegacyPermissionsTEMP(legacyPermissionSettings); + mNewImplementation.writeLegacyPermissionsTEMP(legacyPermissionSettings); + } + + @Override + public void onSystemReady() { + mOldImplementation.onSystemReady(); + mNewImplementation.onSystemReady(); + } + + @Override + public void onStorageVolumeMounted(@NonNull String volumeUuid, boolean fingerprintChanged) { + mOldImplementation.onStorageVolumeMounted(volumeUuid, fingerprintChanged); + mNewImplementation.onStorageVolumeMounted(volumeUuid, fingerprintChanged); + } + + @NonNull + @Override + public int[] getGidsForUid(int uid) { + int[] oldVal = mOldImplementation.getGidsForUid(uid); + int[] newVal = mNewImplementation.getGidsForUid(uid); + + if (!Arrays.equals(oldVal, newVal)) { + signalImplDifference("getGidsForUid"); + } + return newVal; + } + + @Override + public void onUserCreated(int userId) { + mOldImplementation.onUserCreated(userId); + mNewImplementation.onUserCreated(userId); + } + + @Override + public void onUserRemoved(int userId) { + mOldImplementation.onUserRemoved(userId); + mNewImplementation.onUserRemoved(userId); + } + + @Override + public void onPackageAdded(@NonNull PackageState pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg) { + mOldImplementation.onPackageAdded(pkg, isInstantApp, oldPkg); + mNewImplementation.onPackageAdded(pkg, isInstantApp, oldPkg); + } + + @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, + @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, int userId) { + mOldImplementation.onPackageInstalled(pkg, previousAppId, params, userId); + mNewImplementation.onPackageInstalled(pkg, previousAppId, params, userId); + } + + @Override + public void onPackageRemoved(@NonNull AndroidPackage pkg) { + mOldImplementation.onPackageRemoved(pkg); + mNewImplementation.onPackageRemoved(pkg); + } + + @Override + public void onPackageUninstalled(@NonNull String packageName, int appId, + @NonNull PackageState packageState, @Nullable AndroidPackage pkg, + @NonNull List sharedUserPkgs, int userId) { + mOldImplementation.onPackageUninstalled(packageName, appId, packageState, pkg, + sharedUserPkgs, userId); + mNewImplementation.onPackageUninstalled(packageName, appId, packageState, pkg, + sharedUserPkgs, userId); + } +} 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 4caf6cc257b50..17c92acd84a27 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 @@ -16,30 +16,21 @@ package com.android.server.permission.access.appop -import android.Manifest -import android.annotation.UserIdInt -import android.app.AppGlobals import android.app.AppOpsManager -import android.content.pm.PackageManager -import android.os.Binder import android.os.Handler -import android.os.RemoteException import android.os.UserHandle -import android.util.SparseBooleanArray import android.util.SparseIntArray import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.ArrayUtils -import com.android.internal.util.function.pooled.PooledLambda import com.android.server.appop.AppOpsCheckingServiceInterface -import com.android.server.appop.OnOpModeChangedListener import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.AppOpUri import com.android.server.permission.access.PackageUri import com.android.server.permission.access.UidUri -import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports -import com.android.server.permission.access.util.hasBits -import libcore.util.EmptyArray -import java.io.PrintWriter +import com.android.server.permission.access.collection.IndexedMap +import com.android.server.permission.access.collection.IntBooleanMap +import com.android.server.permission.access.collection.IntMap +import com.android.server.permission.access.collection.forEachIndexed class AppOpService( private val service: AccessCheckingService @@ -184,308 +175,23 @@ class AppOpService( // and we have our own persistence. } - // code -> listeners - private val opModeWatchers = IntMap>() - - // packageName -> listeners - private val packageModeWatchers = IndexedMap>() - - override fun startWatchingOpModeChanged(changedListener: OnOpModeChangedListener, op: Int) { - synchronized(lock) { - opModeWatchers.getOrPut(op) { IndexedSet() } += changedListener - } - } - - override fun startWatchingPackageModeChanged( - changedListener: OnOpModeChangedListener, - packageName: String - ) { - synchronized(lock) { - packageModeWatchers.getOrPut(packageName) { IndexedSet() } += changedListener - } - } - - override fun removeListener(changedListener: OnOpModeChangedListener) { - synchronized(lock) { - opModeWatchers.removeAllIndexed { _, _, listeners -> - listeners -= changedListener - listeners.isEmpty() - } - packageModeWatchers.removeAllIndexed { _, _, listeners -> - listeners -= changedListener - listeners.isEmpty() - } - } - } - - override fun getOpModeChangedListeners(op: Int): IndexedSet { - synchronized(lock) { - val listeners = opModeWatchers[op] - return if (listeners == null) { - IndexedSet() - } else { - IndexedSet(listeners) - } - } - } - - override fun getPackageModeChangedListeners( - packageName: String - ): IndexedSet { - synchronized(lock) { - val listeners = packageModeWatchers[packageName] - return if (listeners == null) { - IndexedSet() - } else { - IndexedSet(listeners) - } - } - } - - override fun notifyWatchersOfChange(op: Int, uid: Int) { - val listeners = getOpModeChangedListeners(op) - listeners.forEachIndexed { _, listener -> - notifyOpChanged(listener, op, uid, null) - } - } - - override fun notifyOpChanged( - changedListener: OnOpModeChangedListener, - op: Int, - uid: Int, - packageName: String? - ) { - if (uid != UID_ANY && - changedListener.watchingUid >= 0 && - changedListener.watchingUid != uid - ) { - return - } - - // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE - val switchedCodes = when (changedListener.watchedOpCode) { - ALL_OPS -> switchedOps.get(op) - AppOpsManager.OP_NONE -> intArrayOf(op) - else -> intArrayOf(changedListener.watchedOpCode) - } - - for (switchedCode in switchedCodes) { - // There are features watching for mode changes such as window manager - // and location manager which are in our process. The callbacks in these - // features may require permissions our remote caller does not have. - val identity = Binder.clearCallingIdentity() - try { - if (!shouldIgnoreCallback(switchedCode, changedListener)) { - changedListener.onOpModeChanged(switchedCode, uid, packageName) - } - } catch (e: RemoteException) { - /* ignore */ - } finally { - Binder.restoreCallingIdentity(identity) - } - } - } - - private fun shouldIgnoreCallback(op: Int, listener: OnOpModeChangedListener): Boolean { - // 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 AppOpsManager.opRestrictsRead(op) && context.checkPermission( - Manifest.permission.MANAGE_APPOPS, - listener.callingPid, - listener.callingUid - ) != PackageManager.PERMISSION_GRANTED - } - - /** - * Construct a map from each listener (listening to the given op, uid) to all of its associated - * packageNames (by reverse-indexing opModeWatchers and packageModeWatchers), then invoke - * notifyOpChanged for each listener. - */ - override fun notifyOpChangedForAllPkgsInUid( - op: Int, - uid: Int, - onlyForeground: Boolean, - callbackToIgnore: OnOpModeChangedListener? - ) { - val uidPackageNames = getPackagesForUid(uid) - val callbackSpecs = IndexedMap>() - - fun associateListenerWithPackageNames( - listener: OnOpModeChangedListener, - packageNames: Array - ) { - val listenerIsForeground = - listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES) - if (onlyForeground && !listenerIsForeground) { - return - } - val changedPackages = callbackSpecs.getOrPut(listener) { IndexedSet() } - changedPackages.addAll(packageNames) - } - - synchronized(lock) { - // Collect all listeners from opModeWatchers and pckageModeWatchers - val listeners = opModeWatchers[op] - listeners?.forEachIndexed { _, listener -> - associateListenerWithPackageNames(listener, uidPackageNames) - } - uidPackageNames.forEachIndexed { _, uidPackageName -> - val packageListeners = packageModeWatchers[uidPackageName] - packageListeners?.forEachIndexed { _, listener -> - associateListenerWithPackageNames(listener, arrayOf(uidPackageName)) - } - } - // Remove ignored listeners - if (callbackToIgnore != null) { - callbackSpecs.remove(callbackToIgnore) - } - } - - // For each (listener, packageName) pair, invoke notifyOpChanged - callbackSpecs.forEachIndexed { _, listener, reportedPackageNames -> - reportedPackageNames.forEachIndexed { _, reportedPackageName -> - handler.sendMessage( - PooledLambda.obtainMessage( - AppOpService::notifyOpChanged, this, listener, - op, uid, reportedPackageName - ) - ) - } - } - } - - private fun getPackagesForUid(uid: Int): Array { - // Very early during boot the package manager is not yet or not yet fully started. At this - // time there are no packages yet. - return try { - AppGlobals.getPackageManager()?.getPackagesForUid(uid) ?: EmptyArray.STRING - } catch (e: RemoteException) { - EmptyArray.STRING - } - } - - override fun evalForegroundUidOps( - uid: Int, - foregroundOps: SparseBooleanArray? - ): SparseBooleanArray? { - synchronized(lock) { - val uidModes = getUidModes(uid) - return evalForegroundOps(uidModes, foregroundOps) - } - } - - override fun evalForegroundPackageOps( - packageName: String, - foregroundOps: SparseBooleanArray?, - @UserIdInt userId: Int - ): SparseBooleanArray? { - synchronized(lock) { - val ops = service.getState { getPackageModes(packageName, userId) } - return evalForegroundOps(ops, foregroundOps) - } - } - - private fun evalForegroundOps( - ops: IndexedMap?, - foregroundOps: SparseBooleanArray? - ): SparseBooleanArray? { - var foregroundOps = foregroundOps - ops?.forEachIndexed { _, opName, opMode -> - if (opMode == AppOpsManager.MODE_FOREGROUND) { - if (foregroundOps == null) { - foregroundOps = SparseBooleanArray() - } - evalForegroundWatchers(opName, foregroundOps!!) - } - } - return foregroundOps - } - - private fun evalForegroundWatchers(opName: String, foregroundOps: SparseBooleanArray) { - val opCode = AppOpsManager.strOpToOp(opName) - val listeners = opModeWatchers[opCode] - val hasForegroundListeners = foregroundOps[opCode] || listeners?.anyIndexed { _, listener -> - listener.flags.hasBits(AppOpsManager.WATCH_FOREGROUND_CHANGES) - } ?: false - foregroundOps.put(opCode, hasForegroundListeners) - } - - override fun dumpListeners( - dumpOp: Int, - dumpUid: Int, - dumpPackage: String?, - printWriter: PrintWriter - ): Boolean { - var needSep = false - if (opModeWatchers.size() > 0) { - var printedHeader = false - opModeWatchers.forEachIndexed { _, op, modeChangedListenerSet -> - if (dumpOp >= 0 && dumpOp != op) { - return@forEachIndexed // continue - } - val opName = AppOpsManager.opToName(op) - var printedOpHeader = false - modeChangedListenerSet.forEachIndexed listenerLoop@ { listenerIndex, listener -> - with(printWriter) { - if (dumpPackage != null && - dumpUid != UserHandle.getAppId(listener.watchingUid)) { - return@listenerLoop // continue - } - needSep = true - if (!printedHeader) { - println(" Op mode watchers:") - printedHeader = true - } - if (!printedOpHeader) { - print(" Op ") - print(opName) - println(":") - printedOpHeader = true - } - print(" #") - print(listenerIndex) - print(opName) - print(": ") - println(listener.toString()) - } + override fun getForegroundOps(uid: Int): IntBooleanMap { + return IntBooleanMap().apply { + getUidModes(uid)?.forEachIndexed { _, code, mode -> + if (mode == AppOpsManager.MODE_FOREGROUND) { + put(AppOpsManager.strOpToOp(code), true) } } } + } - if (packageModeWatchers.size > 0 && dumpOp < 0) { - var printedHeader = false - packageModeWatchers.forEachIndexed { _, packageName, listeners -> - with(printWriter) { - if (dumpPackage != null && dumpPackage != packageName) { - return@forEachIndexed // continue - } - needSep = true - if (!printedHeader) { - println(" Package mode watchers:") - printedHeader = true - } - print(" Pkg ") - print(packageName) - println(":") - listeners.forEachIndexed { listenerIndex, listener -> - print(" #") - print(listenerIndex) - print(": ") - println(listener.toString()) - } + override fun getForegroundOps(packageName: String, userId: Int): IntBooleanMap { + return IntBooleanMap().apply { + getPackageModes(packageName, userId)?.forEachIndexed { _, code, mode -> + if (mode == AppOpsManager.MODE_FOREGROUND) { + put(AppOpsManager.strOpToOp(code), true) } } } - return needSep - } - - companion object { - private val LOG_TAG = AppOpService::class.java.simpleName - - // Constant meaning that any UID should be matched when dispatching callbacks - private const val UID_ANY = -2 - - // If watchedOpCode==ALL_OPS, notify for ops affected by the switch-op - private const val ALL_OPS = -2 } } 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 021d01cca381c..197342874b2aa 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java @@ -57,7 +57,7 @@ public class AppOpsLegacyRestrictionsTest { Handler mHandler; @Mock - AppOpsCheckingServiceInterface mLegacyAppOpsService; + AppOpsRestrictions.AppOpsRestrictionRemovedListener mRestrictionRemovedListener; AppOpsRestrictions mAppOpsRestrictions; @@ -75,7 +75,8 @@ public class AppOpsLegacyRestrictionsTest { r.run(); return true; }); - mAppOpsRestrictions = new AppOpsRestrictionsImpl(mContext, mHandler, mLegacyAppOpsService); + mAppOpsRestrictions = new AppOpsRestrictionsImpl(mContext, mHandler, + mRestrictionRemovedListener); } @After @@ -271,7 +272,7 @@ public class AppOpsLegacyRestrictionsTest { public void testNotify() { mAppOpsRestrictions.setUserRestriction(mClientToken, mUserId1, mOpCode1, true, null); mAppOpsRestrictions.clearUserRestrictions(mClientToken); - Mockito.verify(mLegacyAppOpsService, Mockito.times(1)) - .notifyWatchersOfChange(mOpCode1, UID_ANY); + Mockito.verify(mRestrictionRemovedListener, Mockito.times(1)) + .onAppOpsRestrictionRemoved(mOpCode1); } }