Revert^2 "Move appop watcher logic into AppOpsService"

cb3312ab36

Change-Id: I1431518213db1efd7d8bef555fa882e82c4129ab
This commit is contained in:
Evan Severson
2023-03-27 21:48:56 +00:00
parent 445cac77cb
commit 553dcee1fb
10 changed files with 325 additions and 1161 deletions

View File

@@ -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<ArrayMap<String, SparseIntArray>> mUserPackageModes = new SparseArray<>();
final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers = new SparseArray<>();
final ArrayMap<String, ArraySet<OnOpModeChangedListener>> 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<int[]> 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<OnOpModeChangedListener> 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<OnOpModeChangedListener> modeWatcherSet =
mPackageModeWatchers.get(packageName);
if (modeWatcherSet == null) {
modeWatcherSet = new ArraySet<>();
mPackageModeWatchers.put(packageName, modeWatcherSet);
ArrayMap<String, SparseIntArray> 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<OnOpModeChangedListener> 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<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 notifyWatchersOfChange(int code, int uid) {
ArraySet<OnOpModeChangedListener> 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<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(
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<String, SparseIntArray> 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<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());
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<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;
return result;
}
private void scheduleWriteLocked() {

View File

@@ -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<OnOpModeChangedListener> getOpModeChangedListeners(int op);
/**
* Temporary API which will be removed once we can safely untangle the methods that use this.
* Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes.
* @param packageName of package whose app-op's mode change is being listened to.
*/
ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName);
/**
* Temporary API which will be removed once we can safely untangle the methods that use this.
* Notify that the app-op's mode is changed by triggering the change listener.
* @param op App-op whose mode has changed
* @param uid user id associated with the app-op (or, if UID_ANY, notifies all users)
*/
void notifyWatchersOfChange(int op, int uid);
/**
* Temporary API which will be removed once we can safely untangle the methods that use this.
* Notify that the app-op's mode is changed by triggering the change listener.
* @param changedListener the change listener.
* @param op App-op whose mode has changed
* @param uid user id associated with the app-op
* @param packageName package name that is associated with the app-op
*/
void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
@Nullable String packageName);
/**
* Temporary API which will be removed once we can safely untangle the methods that use this.
* Notify that the app-op's mode is changed to all packages associated with the uid by
* triggering the appropriate change listener.
* @param op App-op whose mode has changed
* @param uid user id associated with the app-op
* @param onlyForeground true if only watchers that
* @param callbackToIgnore callback that should be ignored.
*/
void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
@Nullable OnOpModeChangedListener callbackToIgnore);
/**
* TODO: Move hasForegroundWatchers and foregroundOps into this.
* Go over the list of app-ops for the uid and mark app-ops with MODE_FOREGROUND in
* foregroundOps.
* @param uid for which the app-op's mode needs to be marked.
* @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
* @return foregroundOps.
*/
SparseBooleanArray evalForegroundUidOps(int uid, 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);
}

View File

@@ -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<OnOpModeChangedListener> getOpModeChangedListeners(int op) {
Log.i(LOG_TAG, "getOpModeChangedListeners(op = " + op + ")");
return mService.getOpModeChangedListeners(op);
}
@Override
public ArraySet<OnOpModeChangedListener> 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);
}
}

View File

@@ -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<OnOpModeChangedListener> getOpModeChangedListeners(int op) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#getOpModeChangedListeners");
try {
return mService.getOpModeChangedListeners(op);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(
@NonNull String packageName) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#getPackageModeChangedListeners");
try {
return mService.getPackageModeChangedListeners(packageName);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public void notifyWatchersOfChange(int op, int uid) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#notifyWatchersOfChange");
try {
mService.notifyWatchersOfChange(op, uid);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
@Nullable String packageName) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#notifyOpChanged");
try {
mService.notifyOpChanged(changedListener, op, uid, packageName);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
@Nullable OnOpModeChangedListener callbackToIgnore) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#notifyOpChangedForAllPkgsInUid");
try {
mService.notifyOpChangedForAllPkgsInUid(op, uid, onlyForeground, callbackToIgnore);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#evalForegroundUidOps");
try {
return mService.evalForegroundUidOps(uid, foregroundOps);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public SparseBooleanArray evalForegroundPackageOps(String packageName,
SparseBooleanArray foregroundOps, @UserIdInt int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#evalForegroundPackageOps");
try {
return mService.evalForegroundPackageOps(packageName, foregroundOps, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage,
PrintWriter printWriter) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#dumpListeners");
try {
return mService.dumpListeners(dumpOp, dumpUid, dumpPackage, printWriter);
return mService.getForegroundOps(packageName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}

View File

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

View File

@@ -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<Object, SparseBooleanArray> 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));
}
}

View File

@@ -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<Pair<String, Integer>, ArrayList<AsyncNotedAppOp>>
mUnforwardedAsyncNotedOps = new ArrayMap<>();
private final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers =
new SparseArray<>();
private final ArrayMap<String, ArraySet<OnOpModeChangedListener>> mPackageModeWatchers =
new ArrayMap<>();
boolean mWriteNoteOpsScheduled;
boolean mWriteScheduled;
@@ -316,6 +322,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@GuardedBy("this")
@VisibleForTesting
final SparseArray<UidState> 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<int[]> 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<String, Ops> 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<OnOpModeChangedListener> 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<OnOpModeChangedListener> 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,81 @@ 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<OnOpModeChangedListener, ArraySet<String>> callbackSpecs = null;
synchronized (this) {
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(mModeWatchers.get(callbackToIgnore.asBinder()));
}
}
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(
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 +2069,15 @@ public class AppOpsService extends IAppOpsService.Stub {
if (op.getMode() != mode) {
previousMode = op.getMode();
op.setMode(mode);
if (uidState != null) {
uidState.evalForegroundOps();
}
ArraySet<OnOpModeChangedListener> 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 +2116,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<ChangeRec> addChange(ArrayList<ChangeRec> reports,
@@ -2144,11 +2250,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 +2301,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 +2319,6 @@ public class AppOpsService extends IAppOpsService.Stub {
UserHandle.getUserId(uidState.uid));
}
}
if (uidChanged) {
uidState.evalForegroundOps();
}
}
if (changed) {
@@ -2296,15 +2395,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 +2438,21 @@ public class AppOpsService extends IAppOpsService.Stub {
mModeWatchers.put(callback.asBinder(), cb);
}
if (switchOp != AppOpsManager.OP_NONE) {
mAppOpsCheckingService.startWatchingOpModeChanged(cb, switchOp);
ArraySet<OnOpModeChangedListener> cbs = mOpModeWatchers.get(switchOp);
if (cbs == null) {
cbs = new ArraySet<>();
mOpModeWatchers.put(switchOp, cbs);
}
cbs.add(cb);
}
if (mayWatchPackageName) {
mAppOpsCheckingService.startWatchingPackageModeChanged(cb, packageName);
ArraySet<OnOpModeChangedListener> cbs = mPackageModeWatchers.get(packageName);
if (cbs == null) {
cbs = new ArraySet<>();
mPackageModeWatchers.put(packageName, cbs);
}
cbs.add(cb);
}
evalAllForegroundOpsLocked();
}
}
@@ -2366,10 +2465,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<OnOpModeChangedListener> cbs = mOpModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mOpModeWatchers.removeAt(i);
}
}
for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) {
ArraySet<OnOpModeChangedListener> cbs = mPackageModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mPackageModeWatchers.removeAt(i);
}
}
}
evalAllForegroundOpsLocked();
}
}
@@ -3722,7 +3832,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 +5262,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<OnOpModeChangedListener> 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<OnOpModeChangedListener> 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 +5510,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 +5517,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 +5718,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private void notifyWatchersOfChange(int code, int uid) {
final ArraySet<OnOpModeChangedListener> modeChangedListenerSet;
synchronized (this) {
modeChangedListenerSet = mAppOpsCheckingService.getOpModeChangedListeners(code);
modeChangedListenerSet = mOpModeWatchers.get(code);
if (modeChangedListenerSet == null) {
return;
}

View File

@@ -16,11 +16,9 @@
package com.android.server.appop;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import java.io.PrintWriter;
import java.util.Objects;
/**
@@ -181,109 +179,24 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
}
@Override
public void startWatchingOpModeChanged(OnOpModeChangedListener changedListener, int op) {
mOldImplementation.startWatchingOpModeChanged(changedListener, op);
mNewImplementation.startWatchingOpModeChanged(changedListener, op);
}
@Override
public void startWatchingPackageModeChanged(OnOpModeChangedListener changedListener,
String packageName) {
mOldImplementation.startWatchingPackageModeChanged(changedListener, packageName);
mNewImplementation.startWatchingPackageModeChanged(changedListener, packageName);
}
@Override
public void removeListener(OnOpModeChangedListener changedListener) {
mOldImplementation.removeListener(changedListener);
mNewImplementation.removeListener(changedListener);
}
@Override
public ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op) {
ArraySet<OnOpModeChangedListener> oldVal = mOldImplementation.getOpModeChangedListeners(op);
ArraySet<OnOpModeChangedListener> newVal = mNewImplementation.getOpModeChangedListeners(op);
public SparseBooleanArray getForegroundOps(int uid) {
SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(uid);
SparseBooleanArray newVal = mNewImplementation.getForegroundOps(uid);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getOpModeChangedListeners");
signalImplDifference("getForegroundOps");
}
return newVal;
}
@Override
public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(String packageName) {
ArraySet<OnOpModeChangedListener> oldVal = mOldImplementation
.getPackageModeChangedListeners(packageName);
ArraySet<OnOpModeChangedListener> newVal = mNewImplementation
.getPackageModeChangedListeners(packageName);
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("getPackageModeChangedListeners");
}
return newVal;
}
@Override
public void notifyWatchersOfChange(int op, int uid) {
mOldImplementation.notifyWatchersOfChange(op, uid);
mNewImplementation.notifyWatchersOfChange(op, uid);
}
@Override
public void notifyOpChanged(OnOpModeChangedListener changedListener, int op, int uid,
String packageName) {
mOldImplementation.notifyOpChanged(changedListener, op, uid, packageName);
mNewImplementation.notifyOpChanged(changedListener, op, uid, packageName);
}
@Override
public void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
OnOpModeChangedListener callbackToIgnore) {
mOldImplementation
.notifyOpChangedForAllPkgsInUid(op, uid, onlyForeground, callbackToIgnore);
mNewImplementation
.notifyOpChangedForAllPkgsInUid(op, uid, onlyForeground, callbackToIgnore);
}
@Override
public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) {
SparseBooleanArray oldVal = mOldImplementation.evalForegroundUidOps(uid, foregroundOps);
SparseBooleanArray newVal = mNewImplementation.evalForegroundUidOps(uid, foregroundOps);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("evalForegroundUidOps");
}
return newVal;
}
@Override
public SparseBooleanArray evalForegroundPackageOps(String packageName,
SparseBooleanArray foregroundOps, int userId) {
SparseBooleanArray oldVal = mOldImplementation
.evalForegroundPackageOps(packageName, foregroundOps, userId);
SparseBooleanArray newVal = mNewImplementation
.evalForegroundPackageOps(packageName, foregroundOps, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("evalForegroundPackageOps");
}
return newVal;
}
@Override
public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage,
PrintWriter printWriter) {
boolean oldVal = mOldImplementation
.dumpListeners(dumpOp, dumpUid, dumpPackage, printWriter);
boolean newVal = mNewImplementation
.dumpListeners(dumpOp, dumpUid, dumpPackage, printWriter);
if (oldVal != newVal) {
signalImplDifference("dumpListeners");
signalImplDifference("getForegroundOps");
}
return newVal;

View File

@@ -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<IndexedSet<OnOpModeChangedListener>>()
// packageName -> listeners
private val packageModeWatchers = IndexedMap<String, IndexedSet<OnOpModeChangedListener>>()
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<OnOpModeChangedListener> {
synchronized(lock) {
val listeners = opModeWatchers[op]
return if (listeners == null) {
IndexedSet()
} else {
IndexedSet(listeners)
}
}
}
override fun getPackageModeChangedListeners(
packageName: String
): IndexedSet<OnOpModeChangedListener> {
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<OnOpModeChangedListener, IndexedSet<String>>()
fun associateListenerWithPackageNames(
listener: OnOpModeChangedListener,
packageNames: Array<String>
) {
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<String> {
// 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<String, Int>?,
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
}
}

View File

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