Move appop watcher logic into AppOpsService
The interface is intended to be for queries about the raw data state. We also remove the foregroundOps cache that is used for dispatching foreground mode changes; since we now have to do more computation on foreground changes to dispatch callbacks the performance impact has been briefly analyzed to show that onUidStateChanged is not a highly frequently called method and went from .05ms to 0.07ms to execute. This was calculated by running CtsAppOpsTestCAses and tracing. Test: atest CtsAppOpsTestCases Fixes: 268696066 Change-Id: I96700653496984aa3333b174abf2ceb3c1790b10
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -292,6 +293,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;
|
||||
@@ -315,6 +321,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);
|
||||
|
||||
@@ -341,8 +349,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;
|
||||
@@ -505,11 +511,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;
|
||||
}
|
||||
@@ -540,25 +541,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);
|
||||
@@ -940,7 +922,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");
|
||||
@@ -1108,8 +1091,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;
|
||||
}
|
||||
@@ -1149,8 +1131,6 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -1222,6 +1202,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
for (int uid : NON_PACKAGE_UIDS) {
|
||||
mUidStates.put(uid, new UidState(uid));
|
||||
}
|
||||
mUidStatesInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1265,8 +1246,6 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
ops.put(code, new Op(uidState, packageName, code, uid));
|
||||
}
|
||||
}
|
||||
|
||||
uidState.evalForegroundOps();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1342,12 +1321,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) {
|
||||
@@ -1356,7 +1363,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);
|
||||
@@ -1822,7 +1829,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);
|
||||
}
|
||||
@@ -1841,10 +1847,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) {
|
||||
@@ -1991,19 +2068,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<>();
|
||||
@@ -2042,9 +2115,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,
|
||||
@@ -2143,11 +2249,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);
|
||||
@@ -2196,11 +2300,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);
|
||||
@@ -2216,9 +2318,6 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
UserHandle.getUserId(uidState.uid));
|
||||
}
|
||||
}
|
||||
if (uidChanged) {
|
||||
uidState.evalForegroundOps();
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
@@ -2295,15 +2394,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);
|
||||
@@ -2347,12 +2437,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2365,10 +2464,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3721,7 +3831,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
|
||||
*/
|
||||
@@ -5151,8 +5261,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) {
|
||||
@@ -5352,11 +5509,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;
|
||||
}
|
||||
@@ -5364,21 +5516,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) {
|
||||
@@ -5580,7 +5717,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user