Revert^2 "Move appop watcher logic into AppOpsService"
cb3312ab36
Change-Id: I1431518213db1efd7d8bef555fa882e82c4129ab
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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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