Loading services/core/java/com/android/server/appop/AppOpsService.java +102 −290 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/appop/AppOpsServiceInterface.java +98 −1 Original line number Diff line number Diff line Loading @@ -14,12 +14,20 @@ * limitations under the License. */ package com.android.server.appop; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager.Mode; import android.util.ArraySet; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import java.io.PrintWriter; /** * Interface for accessing and modifying modes for app-ops i.e. package and uid modes. * In the future this interface will also include mode callbacks and op restrictions. * This interface also includes functions for added and removing op mode watchers. * In the future this interface will also include op restrictions. */ public interface AppOpsServiceInterface { /** Loading Loading @@ -95,4 +103,93 @@ public interface AppOpsServiceInterface { * Stop tracking app-op modes for all uid and packages. */ void clearAllModes(); /** * Registers changedListener to listen to op's mode change. * @param changedListener the listener that must be trigger on the op's mode change. * @param op op representing the app-op whose mode change needs to be listened to. */ void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op); /** * Registers changedListener to listen to package's app-op's mode change. * @param changedListener the listener that must be trigger on the mode change. * @param packageName of the package whose app-op's mode change needs to be listened to. */ void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, @NonNull String packageName); /** * Stop the changedListener from triggering on any mode change. * @param changedListener the listener that needs to be removed. */ void removeListener(@NonNull OnOpModeChangedListener changedListener); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Returns a set of OnOpModeChangedListener that are listening for op's mode changes. * @param op app-op whose mode change is being listened to. */ ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes. * @param packageName of package whose app-op's mode change is being listened to. */ ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Notify that the app-op's mode is changed by triggering the change listener. * @param 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. * @return foregroundOps. */ SparseBooleanArray evalForegroundPackageOps(String packageName, SparseBooleanArray foregroundOps); /** * 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); } services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java +375 −4 Original line number Diff line number Diff line Loading @@ -16,15 +16,39 @@ package com.android.server.appop; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES; import static android.app.AppOpsManager.opRestrictsRead; import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.Mode; import android.content.Context; import android.content.pm.PackageManager; 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.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; import libcore.util.EmptyArray; import java.io.PrintWriter; import java.util.Collections; import java.util.Objects; /** Loading @@ -33,8 +57,13 @@ import com.android.internal.annotations.VisibleForTesting; */ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface { // Should be the same object that the AppOpsService is using for locking. static final String TAG = "LegacyAppOpsServiceInterfaceImpl"; // Must be the same object that the AppOpsService is using for locking. final Object mLock; final Handler mHandler; final Context mContext; final SparseArray<int[]> mSwitchedOps; @GuardedBy("mLock") @VisibleForTesting Loading @@ -43,13 +72,25 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface @GuardedBy("mLock") final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>(); final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers = new SparseArray<>(); final ArrayMap<String, ArraySet<OnOpModeChangedListener>> mPackageModeWatchers = new ArrayMap<>(); final PersistenceScheduler mPersistenceScheduler; // Constant meaning that any UID should be matched when dispatching callbacks private static final int UID_ANY = -2; LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler, @NonNull Object lock) { @NonNull Object lock, Handler handler, Context context, SparseArray<int[]> switchedOps) { this.mPersistenceScheduler = persistenceScheduler; this.mLock = lock; this.mHandler = handler; this.mContext = context; this.mSwitchedOps = switchedOps; } @Override Loading Loading @@ -158,7 +199,6 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface } } @Override public boolean areUidModesDefault(int uid) { synchronized (mLock) { Loading Loading @@ -195,4 +235,335 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface } } @Override public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op) { Objects.requireNonNull(changedListener); synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeWatcherSet = mOpModeWatchers.get(op); if (modeWatcherSet == null) { modeWatcherSet = new ArraySet<>(); mOpModeWatchers.put(op, modeWatcherSet); } modeWatcherSet.add(changedListener); } } @Override public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, @NonNull String packageName) { Objects.requireNonNull(changedListener); Objects.requireNonNull(packageName); synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeWatcherSet = mPackageModeWatchers.get(packageName); if (modeWatcherSet == null) { modeWatcherSet = new ArraySet<>(); mPackageModeWatchers.put(packageName, modeWatcherSet); } 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<OnOpModeChangedListener> cbs = mOpModeWatchers.valueAt(i); cbs.remove(changedListener); if (cbs.size() <= 0) { mOpModeWatchers.removeAt(i); } } for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) { ArraySet<OnOpModeChangedListener> cbs = mPackageModeWatchers.valueAt(i); cbs.remove(changedListener); if (cbs.size() <= 0) { mPackageModeWatchers.removeAt(i); } } } } @Override public ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op) { synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeChangedListenersSet = mOpModeWatchers.get(op); if (modeChangedListenersSet == null) { return new ArraySet<>(); } return new ArraySet<>(modeChangedListenersSet); } } @Override public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners( @NonNull String packageName) { Objects.requireNonNull(packageName); synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeChangedListenersSet = mPackageModeWatchers.get(packageName); if (modeChangedListenersSet == null) { return new ArraySet<>(); } return new ArraySet<>(modeChangedListenersSet); } } @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<OnOpModeChangedListener, ArraySet<String>> callbackSpecs = null; synchronized (mLock) { ArraySet<OnOpModeChangedListener> 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<String> 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<String> 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<String> reportedPackageNames = callbackSpecs.valueAt(i); if (reportedPackageNames == null) { mHandler.sendMessage(PooledLambda.obtainMessage( LegacyAppOpsServiceInterfaceImpl::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( LegacyAppOpsServiceInterfaceImpl::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) { synchronized (mLock) { return evalForegroundOps(mPackageModes.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<OnOpModeChangedListener> 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<OnOpModeChangedListener> 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()); } } } 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<OnOpModeChangedListener> 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; } } No newline at end of file services/core/java/com/android/server/appop/OnOpModeChangedListener.java 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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.os.RemoteException; /** * Listener for mode changes, encapsulates methods that should be triggered in the event of a mode * change. */ abstract class OnOpModeChangedListener { // Constant meaning that any UID should be matched when dispatching callbacks private static final int UID_ANY = -2; private int mWatchingUid; private int mFlags; private int mWatchedOpCode; private int mCallingUid; private int mCallingPid; OnOpModeChangedListener(int watchingUid, int flags, int watchedOpCode, int callingUid, int callingPid) { this.mWatchingUid = watchingUid; this.mFlags = flags; this.mWatchedOpCode = watchedOpCode; this.mCallingUid = callingUid; this.mCallingPid = callingPid; } /** * Returns the user id that is watching for the mode change. */ public int getWatchingUid() { return mWatchingUid; } /** * Returns the flags associated with the mode change listener. */ public int getFlags() { return mFlags; } /** * Get the app-op whose mode change should trigger the callback. */ public int getWatchedOpCode() { return mWatchedOpCode; } /** * Get the user-id that triggered the app-op mode change to be watched. */ public int getCallingUid() { return mCallingUid; } /** * Get the process-id that triggered the app-op mode change to be watched. */ public int getCallingPid() { return mCallingPid; } /** * returns true if the user id passed in the param is the one that is watching for op mode * changed. */ public boolean isWatchingUid(int uid) { return uid == UID_ANY || mWatchingUid < 0 || mWatchingUid == uid; } /** * Method that should be triggered when the app-op's mode is changed. * @param op app-op whose mode-change is being listened to. * @param uid user-is associated with the app-op. * @param packageName package name associated with the app-op. */ public abstract void onOpModeChanged(int op, int uid, String packageName) throws RemoteException; /** * Return human readable string representing the listener. */ public abstract String toString(); } Loading
services/core/java/com/android/server/appop/AppOpsService.java +102 −290 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/appop/AppOpsServiceInterface.java +98 −1 Original line number Diff line number Diff line Loading @@ -14,12 +14,20 @@ * limitations under the License. */ package com.android.server.appop; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager.Mode; import android.util.ArraySet; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import java.io.PrintWriter; /** * Interface for accessing and modifying modes for app-ops i.e. package and uid modes. * In the future this interface will also include mode callbacks and op restrictions. * This interface also includes functions for added and removing op mode watchers. * In the future this interface will also include op restrictions. */ public interface AppOpsServiceInterface { /** Loading Loading @@ -95,4 +103,93 @@ public interface AppOpsServiceInterface { * Stop tracking app-op modes for all uid and packages. */ void clearAllModes(); /** * Registers changedListener to listen to op's mode change. * @param changedListener the listener that must be trigger on the op's mode change. * @param op op representing the app-op whose mode change needs to be listened to. */ void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op); /** * Registers changedListener to listen to package's app-op's mode change. * @param changedListener the listener that must be trigger on the mode change. * @param packageName of the package whose app-op's mode change needs to be listened to. */ void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, @NonNull String packageName); /** * Stop the changedListener from triggering on any mode change. * @param changedListener the listener that needs to be removed. */ void removeListener(@NonNull OnOpModeChangedListener changedListener); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Returns a set of OnOpModeChangedListener that are listening for op's mode changes. * @param op app-op whose mode change is being listened to. */ ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes. * @param packageName of package whose app-op's mode change is being listened to. */ ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName); /** * Temporary API which will be removed once we can safely untangle the methods that use this. * Notify that the app-op's mode is changed by triggering the change listener. * @param 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. * @return foregroundOps. */ SparseBooleanArray evalForegroundPackageOps(String packageName, SparseBooleanArray foregroundOps); /** * 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); }
services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java +375 −4 Original line number Diff line number Diff line Loading @@ -16,15 +16,39 @@ package com.android.server.appop; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES; import static android.app.AppOpsManager.opRestrictsRead; import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.Mode; import android.content.Context; import android.content.pm.PackageManager; 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.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; import libcore.util.EmptyArray; import java.io.PrintWriter; import java.util.Collections; import java.util.Objects; /** Loading @@ -33,8 +57,13 @@ import com.android.internal.annotations.VisibleForTesting; */ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface { // Should be the same object that the AppOpsService is using for locking. static final String TAG = "LegacyAppOpsServiceInterfaceImpl"; // Must be the same object that the AppOpsService is using for locking. final Object mLock; final Handler mHandler; final Context mContext; final SparseArray<int[]> mSwitchedOps; @GuardedBy("mLock") @VisibleForTesting Loading @@ -43,13 +72,25 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface @GuardedBy("mLock") final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>(); final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers = new SparseArray<>(); final ArrayMap<String, ArraySet<OnOpModeChangedListener>> mPackageModeWatchers = new ArrayMap<>(); final PersistenceScheduler mPersistenceScheduler; // Constant meaning that any UID should be matched when dispatching callbacks private static final int UID_ANY = -2; LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler, @NonNull Object lock) { @NonNull Object lock, Handler handler, Context context, SparseArray<int[]> switchedOps) { this.mPersistenceScheduler = persistenceScheduler; this.mLock = lock; this.mHandler = handler; this.mContext = context; this.mSwitchedOps = switchedOps; } @Override Loading Loading @@ -158,7 +199,6 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface } } @Override public boolean areUidModesDefault(int uid) { synchronized (mLock) { Loading Loading @@ -195,4 +235,335 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface } } @Override public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op) { Objects.requireNonNull(changedListener); synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeWatcherSet = mOpModeWatchers.get(op); if (modeWatcherSet == null) { modeWatcherSet = new ArraySet<>(); mOpModeWatchers.put(op, modeWatcherSet); } modeWatcherSet.add(changedListener); } } @Override public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener, @NonNull String packageName) { Objects.requireNonNull(changedListener); Objects.requireNonNull(packageName); synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeWatcherSet = mPackageModeWatchers.get(packageName); if (modeWatcherSet == null) { modeWatcherSet = new ArraySet<>(); mPackageModeWatchers.put(packageName, modeWatcherSet); } 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<OnOpModeChangedListener> cbs = mOpModeWatchers.valueAt(i); cbs.remove(changedListener); if (cbs.size() <= 0) { mOpModeWatchers.removeAt(i); } } for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) { ArraySet<OnOpModeChangedListener> cbs = mPackageModeWatchers.valueAt(i); cbs.remove(changedListener); if (cbs.size() <= 0) { mPackageModeWatchers.removeAt(i); } } } } @Override public ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op) { synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeChangedListenersSet = mOpModeWatchers.get(op); if (modeChangedListenersSet == null) { return new ArraySet<>(); } return new ArraySet<>(modeChangedListenersSet); } } @Override public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners( @NonNull String packageName) { Objects.requireNonNull(packageName); synchronized (mLock) { ArraySet<OnOpModeChangedListener> modeChangedListenersSet = mPackageModeWatchers.get(packageName); if (modeChangedListenersSet == null) { return new ArraySet<>(); } return new ArraySet<>(modeChangedListenersSet); } } @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<OnOpModeChangedListener, ArraySet<String>> callbackSpecs = null; synchronized (mLock) { ArraySet<OnOpModeChangedListener> 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<String> 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<String> 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<String> reportedPackageNames = callbackSpecs.valueAt(i); if (reportedPackageNames == null) { mHandler.sendMessage(PooledLambda.obtainMessage( LegacyAppOpsServiceInterfaceImpl::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( LegacyAppOpsServiceInterfaceImpl::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) { synchronized (mLock) { return evalForegroundOps(mPackageModes.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<OnOpModeChangedListener> 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<OnOpModeChangedListener> 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()); } } } 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<OnOpModeChangedListener> 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; } } No newline at end of file
services/core/java/com/android/server/appop/OnOpModeChangedListener.java 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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.os.RemoteException; /** * Listener for mode changes, encapsulates methods that should be triggered in the event of a mode * change. */ abstract class OnOpModeChangedListener { // Constant meaning that any UID should be matched when dispatching callbacks private static final int UID_ANY = -2; private int mWatchingUid; private int mFlags; private int mWatchedOpCode; private int mCallingUid; private int mCallingPid; OnOpModeChangedListener(int watchingUid, int flags, int watchedOpCode, int callingUid, int callingPid) { this.mWatchingUid = watchingUid; this.mFlags = flags; this.mWatchedOpCode = watchedOpCode; this.mCallingUid = callingUid; this.mCallingPid = callingPid; } /** * Returns the user id that is watching for the mode change. */ public int getWatchingUid() { return mWatchingUid; } /** * Returns the flags associated with the mode change listener. */ public int getFlags() { return mFlags; } /** * Get the app-op whose mode change should trigger the callback. */ public int getWatchedOpCode() { return mWatchedOpCode; } /** * Get the user-id that triggered the app-op mode change to be watched. */ public int getCallingUid() { return mCallingUid; } /** * Get the process-id that triggered the app-op mode change to be watched. */ public int getCallingPid() { return mCallingPid; } /** * returns true if the user id passed in the param is the one that is watching for op mode * changed. */ public boolean isWatchingUid(int uid) { return uid == UID_ANY || mWatchingUid < 0 || mWatchingUid == uid; } /** * Method that should be triggered when the app-op's mode is changed. * @param op app-op whose mode-change is being listened to. * @param uid user-is associated with the app-op. * @param packageName package name associated with the app-op. */ public abstract void onOpModeChanged(int op, int uid, String packageName) throws RemoteException; /** * Return human readable string representing the listener. */ public abstract String toString(); }