Merge "Add listeners to appops checking interface"
This commit is contained in:
committed by
Android (Google) Code Review
commit
7cea15d4ce
@@ -102,6 +102,8 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
|
||||
final SparseArray<ArrayMap<String, SparseIntArray>> mUserPackageModes = new SparseArray<>();
|
||||
|
||||
private final LegacyAppOpStateParser mAppOpsStateParser = new LegacyAppOpStateParser();
|
||||
@GuardedBy("mLock")
|
||||
private List<AppOpsModeChangedListener> mModeChangedListeners = new ArrayList<>();
|
||||
|
||||
final AtomicFile mFile;
|
||||
final Runnable mWriteRunner = new Runnable() {
|
||||
@@ -183,31 +185,39 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
|
||||
@Override
|
||||
public boolean setUidMode(int uid, int op, int mode) {
|
||||
final int defaultMode = AppOpsManager.opToDefaultMode(op);
|
||||
List<AppOpsModeChangedListener> listenersCopy;
|
||||
synchronized (mLock) {
|
||||
SparseIntArray opModes = mUidModes.get(uid, null);
|
||||
if (opModes == null) {
|
||||
if (mode != defaultMode) {
|
||||
opModes = new SparseIntArray();
|
||||
mUidModes.put(uid, opModes);
|
||||
opModes.put(op, mode);
|
||||
scheduleWriteLocked();
|
||||
|
||||
int previousMode = defaultMode;
|
||||
if (opModes != null) {
|
||||
previousMode = opModes.get(op, defaultMode);
|
||||
}
|
||||
if (mode == previousMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode == defaultMode) {
|
||||
opModes.delete(op);
|
||||
if (opModes.size() == 0) {
|
||||
mUidModes.remove(uid);
|
||||
}
|
||||
} else {
|
||||
if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
|
||||
return false;
|
||||
if (opModes == null) {
|
||||
opModes = new SparseIntArray();
|
||||
mUidModes.put(uid, opModes);
|
||||
}
|
||||
if (mode == defaultMode) {
|
||||
opModes.delete(op);
|
||||
if (opModes.size() <= 0) {
|
||||
opModes = null;
|
||||
mUidModes.delete(uid);
|
||||
}
|
||||
} else {
|
||||
opModes.put(op, mode);
|
||||
}
|
||||
scheduleWriteLocked();
|
||||
opModes.put(op, mode);
|
||||
}
|
||||
|
||||
scheduleWriteLocked();
|
||||
listenersCopy = new ArrayList<>(mModeChangedListeners);
|
||||
}
|
||||
|
||||
for (int i = 0; i < listenersCopy.size(); i++) {
|
||||
listenersCopy.get(i).onUidModeChanged(uid, op, mode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -229,35 +239,52 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
|
||||
@Override
|
||||
public void setPackageMode(String packageName, int op, @Mode int mode, @UserIdInt int userId) {
|
||||
final int defaultMode = AppOpsManager.opToDefaultMode(op);
|
||||
List<AppOpsModeChangedListener> listenersCopy;
|
||||
synchronized (mLock) {
|
||||
ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
|
||||
if (packageModes == null) {
|
||||
if (packageModes == null && mode != defaultMode) {
|
||||
packageModes = new ArrayMap<>();
|
||||
mUserPackageModes.put(userId, packageModes);
|
||||
}
|
||||
SparseIntArray opModes = packageModes.get(packageName);
|
||||
if (opModes == null) {
|
||||
if (mode != defaultMode) {
|
||||
opModes = new SparseIntArray();
|
||||
packageModes.put(packageName, opModes);
|
||||
opModes.put(op, mode);
|
||||
scheduleWriteLocked();
|
||||
SparseIntArray opModes = null;
|
||||
int previousMode = defaultMode;
|
||||
if (packageModes != null) {
|
||||
opModes = packageModes.get(packageName);
|
||||
if (opModes != null) {
|
||||
previousMode = opModes.get(op, defaultMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == previousMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == defaultMode) {
|
||||
opModes.delete(op);
|
||||
if (opModes.size() == 0) {
|
||||
packageModes.remove(packageName);
|
||||
if (packageModes.size() == 0) {
|
||||
mUserPackageModes.remove(userId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
|
||||
return;
|
||||
if (packageModes == null) {
|
||||
packageModes = new ArrayMap<>();
|
||||
mUserPackageModes.put(userId, packageModes);
|
||||
}
|
||||
if (mode == defaultMode) {
|
||||
opModes.delete(op);
|
||||
if (opModes.size() <= 0) {
|
||||
opModes = null;
|
||||
packageModes.remove(packageName);
|
||||
}
|
||||
} else {
|
||||
opModes.put(op, mode);
|
||||
if (opModes == null) {
|
||||
opModes = new SparseIntArray();
|
||||
packageModes.put(packageName, opModes);
|
||||
}
|
||||
scheduleWriteLocked();
|
||||
opModes.put(op, mode);
|
||||
}
|
||||
|
||||
scheduleFastWriteLocked();
|
||||
listenersCopy = new ArrayList<>(mModeChangedListeners);
|
||||
}
|
||||
|
||||
for (int i = 0; i < listenersCopy.size(); i++) {
|
||||
listenersCopy.get(i).onPackageModeChanged(packageName, userId, op, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,4 +660,18 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
synchronized (mLock) {
|
||||
return mModeChangedListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
synchronized (mLock) {
|
||||
return mModeChangedListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,4 +158,46 @@ public interface AppOpsCheckingServiceInterface {
|
||||
* MODE_FOREGROUND for the passed package name and user ID.
|
||||
*/
|
||||
SparseBooleanArray getForegroundOps(String packageName, int userId);
|
||||
|
||||
/**
|
||||
* Adds a listener for changes in appop modes. These callbacks should be dispatched
|
||||
* synchronously.
|
||||
*
|
||||
* @param listener The listener to be added.
|
||||
* @return true if the listener was added.
|
||||
*/
|
||||
boolean addAppOpsModeChangedListener(@NonNull AppOpsModeChangedListener listener);
|
||||
|
||||
/**
|
||||
* Removes a listener for changes in appop modes.
|
||||
*
|
||||
* @param listener The listener to be removed.
|
||||
* @return true if the listener was removed.
|
||||
*/
|
||||
boolean removeAppOpsModeChangedListener(@NonNull AppOpsModeChangedListener listener);
|
||||
|
||||
/**
|
||||
* A listener for changes to the AppOps mode.
|
||||
*/
|
||||
interface AppOpsModeChangedListener {
|
||||
|
||||
/**
|
||||
* Invoked when a UID's appop mode is changed.
|
||||
*
|
||||
* @param uid The UID whose appop mode was changed.
|
||||
* @param code The op code that was changed.
|
||||
* @param mode The new mode.
|
||||
*/
|
||||
void onUidModeChanged(int uid, int code, int mode);
|
||||
|
||||
/**
|
||||
* Invoked when a package's appop mode is changed.
|
||||
*
|
||||
* @param packageName The package name whose appop mode was changed.
|
||||
* @param userId The user ID for the package.
|
||||
* @param code The op code that was changed.
|
||||
* @param mode The new mode.
|
||||
*/
|
||||
void onPackageModeChanged(@NonNull String packageName, int userId, int code, int mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,4 +141,16 @@ public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServ
|
||||
+ ")");
|
||||
return mService.getForegroundOps(packageName, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
Log.i(LOG_TAG, "addAppOpsModeChangedListener(listener = " + listener + ")");
|
||||
return mService.addAppOpsModeChangedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
Log.i(LOG_TAG, "removeAppOpsModeChangedListener(listener = " + listener + ")");
|
||||
return mService.removeAppOpsModeChangedListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,4 +221,26 @@ public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServ
|
||||
Trace.traceEnd(TRACE_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
Trace.traceBegin(TRACE_TAG,
|
||||
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#addAppOpsModeChangedListener");
|
||||
try {
|
||||
return mService.addAppOpsModeChangedListener(listener);
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
Trace.traceBegin(TRACE_TAG,
|
||||
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#removeAppOpsModeChangedListener");
|
||||
try {
|
||||
return mService.removeAppOpsModeChangedListener(listener);
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +399,10 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
|
||||
private AppOpsUidStateTracker mUidStateTracker;
|
||||
|
||||
/** Callback to skip on next appop update.*/
|
||||
@GuardedBy("this")
|
||||
private IAppOpsCallback mIgnoredCallback = null;
|
||||
|
||||
/** Hands the definition of foreground and uid states */
|
||||
@GuardedBy("this")
|
||||
public AppOpsUidStateTracker getUidStateTracker() {
|
||||
@@ -923,6 +927,59 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
mAppOpsCheckingService = new AppOpsCheckingServiceTracingDecorator(
|
||||
new AppOpsCheckingServiceImpl(
|
||||
storageFile, this, handler, context, mSwitchedOps));
|
||||
mAppOpsCheckingService.addAppOpsModeChangedListener(
|
||||
new AppOpsCheckingServiceInterface.AppOpsModeChangedListener() {
|
||||
@Override
|
||||
public void onUidModeChanged(int uid, int code, int mode) {
|
||||
notifyOpChangedForAllPkgsInUid(code, uid, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageModeChanged(String packageName, int userId, int code,
|
||||
int mode) {
|
||||
ArraySet<OnOpModeChangedListener> repCbs = null;
|
||||
int uid = -1;
|
||||
synchronized (AppOpsService.this) {
|
||||
ArraySet<OnOpModeChangedListener> cbs =
|
||||
mOpModeWatchers.get(code);
|
||||
if (cbs != null) {
|
||||
if (repCbs == null) {
|
||||
repCbs = new ArraySet<>();
|
||||
}
|
||||
repCbs.addAll(cbs);
|
||||
}
|
||||
cbs = mPackageModeWatchers.get(packageName);
|
||||
if (cbs != null) {
|
||||
if (repCbs == null) {
|
||||
repCbs = new ArraySet<>();
|
||||
}
|
||||
repCbs.addAll(cbs);
|
||||
}
|
||||
if (repCbs != null && mIgnoredCallback != null) {
|
||||
repCbs.remove(mModeWatchers.get(mIgnoredCallback.asBinder()));
|
||||
}
|
||||
uid = getPackageManagerInternal().getPackageUid(packageName,
|
||||
PackageManager.MATCH_KNOWN_PACKAGES, userId);
|
||||
Op op = getOpLocked(code, uid, packageName, null, false, null,
|
||||
/* edit */ false);
|
||||
if (op != null && mode == AppOpsManager.opToDefaultMode(op.op)) {
|
||||
// If going into the default mode, prune this op
|
||||
// if there is nothing else interesting in it.
|
||||
pruneOpLocked(op, uid, packageName);
|
||||
}
|
||||
scheduleFastWriteLocked();
|
||||
if (mode != MODE_ERRORED) {
|
||||
updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
|
||||
}
|
||||
}
|
||||
|
||||
if (repCbs != null && uid != -1) {
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(
|
||||
AppOpsService::notifyOpChanged,
|
||||
AppOpsService.this, repCbs, code, uid, packageName));
|
||||
}
|
||||
}
|
||||
});
|
||||
//mAppOpsCheckingService = new AppOpsCheckingServiceLoggingDecorator(
|
||||
// LocalServices.getService(AppOpsCheckingServiceInterface.class));
|
||||
mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler,
|
||||
@@ -1363,7 +1420,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
&& uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(
|
||||
AppOpsService::notifyOpChangedForAllPkgsInUid,
|
||||
this, code, uidState.uid, true, null));
|
||||
this, code, uidState.uid, true));
|
||||
} else if (!uidState.pkgOps.isEmpty()) {
|
||||
final ArraySet<OnOpModeChangedListener> listenerSet =
|
||||
mOpModeWatchers.get(code);
|
||||
@@ -1830,6 +1887,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
previousMode = MODE_DEFAULT;
|
||||
}
|
||||
|
||||
mIgnoredCallback = permissionPolicyCallback;
|
||||
if (!uidState.setUidMode(code, mode)) {
|
||||
return;
|
||||
}
|
||||
@@ -1838,8 +1896,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
|
||||
notifyOpChangedSync(code, uid, null, mode, previousMode);
|
||||
notifyStorageManagerOpModeChangedSync(code, uid, null, mode, previousMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1849,8 +1906,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
* @param uid The uid the op was changed for
|
||||
* @param onlyForeground Only notify watchers that watch for foreground changes
|
||||
*/
|
||||
private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground,
|
||||
@Nullable IAppOpsCallback callbackToIgnore) {
|
||||
private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground) {
|
||||
String[] uidPackageNames = getPackagesForUid(uid);
|
||||
ArrayMap<OnOpModeChangedListener, ArraySet<String>> callbackSpecs = null;
|
||||
synchronized (this) {
|
||||
@@ -1903,8 +1959,8 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
if (callbackSpecs != null && callbackToIgnore != null) {
|
||||
callbackSpecs.remove(mModeWatchers.get(callbackToIgnore.asBinder()));
|
||||
if (callbackSpecs != null && mIgnoredCallback != null) {
|
||||
callbackSpecs.remove(mModeWatchers.get(mIgnoredCallback.asBinder()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2023,8 +2079,8 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode,
|
||||
int previousMode) {
|
||||
private void notifyStorageManagerOpModeChangedSync(int code, int uid,
|
||||
@NonNull String packageName, int mode, int previousMode) {
|
||||
final StorageManagerInternal storageManagerInternal =
|
||||
LocalServices.getService(StorageManagerInternal.class);
|
||||
if (storageManagerInternal != null) {
|
||||
@@ -2053,7 +2109,6 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
ArraySet<OnOpModeChangedListener> repCbs = null;
|
||||
code = AppOpsManager.opToSwitch(code);
|
||||
|
||||
PackageVerificationResult pvr;
|
||||
@@ -2070,49 +2125,17 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
|
||||
int previousMode = MODE_DEFAULT;
|
||||
synchronized (this) {
|
||||
UidState uidState = getUidStateLocked(uid, false);
|
||||
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
|
||||
if (op != null) {
|
||||
if (op.getMode() != mode) {
|
||||
previousMode = op.getMode();
|
||||
mIgnoredCallback = permissionPolicyCallback;
|
||||
op.setMode(mode);
|
||||
ArraySet<OnOpModeChangedListener> cbs =
|
||||
mOpModeWatchers.get(code);
|
||||
if (cbs != null) {
|
||||
if (repCbs == null) {
|
||||
repCbs = new ArraySet<>();
|
||||
}
|
||||
repCbs.addAll(cbs);
|
||||
}
|
||||
cbs = mPackageModeWatchers.get(packageName);
|
||||
if (cbs != null) {
|
||||
if (repCbs == null) {
|
||||
repCbs = new ArraySet<>();
|
||||
}
|
||||
repCbs.addAll(cbs);
|
||||
}
|
||||
if (repCbs != null && permissionPolicyCallback != null) {
|
||||
repCbs.remove(mModeWatchers.get(permissionPolicyCallback.asBinder()));
|
||||
}
|
||||
if (mode == AppOpsManager.opToDefaultMode(op.op)) {
|
||||
// If going into the default mode, prune this op
|
||||
// if there is nothing else interesting in it.
|
||||
pruneOpLocked(op, uid, packageName);
|
||||
}
|
||||
scheduleFastWriteLocked();
|
||||
if (mode != MODE_ERRORED) {
|
||||
updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (repCbs != null) {
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(
|
||||
AppOpsService::notifyOpChanged,
|
||||
this, repCbs, code, uid, packageName));
|
||||
}
|
||||
|
||||
notifyOpChangedSync(code, uid, packageName, mode, previousMode);
|
||||
notifyStorageManagerOpModeChangedSync(code, uid, packageName, mode, previousMode);
|
||||
}
|
||||
|
||||
private void notifyOpChanged(ArraySet<OnOpModeChangedListener> callbacks, int code,
|
||||
@@ -2349,7 +2372,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
int numChanges = allChanges.size();
|
||||
for (int i = 0; i < numChanges; i++) {
|
||||
ChangeRec change = allChanges.get(i);
|
||||
notifyOpChangedSync(change.op, change.uid, change.pkg,
|
||||
notifyStorageManagerOpModeChangedSync(change.op, change.uid, change.pkg,
|
||||
AppOpsManager.opToDefaultMode(change.op), change.previous_mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,4 +201,28 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
|
||||
|
||||
return newVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
boolean oldVal = mOldImplementation.addAppOpsModeChangedListener(listener);
|
||||
boolean newVal = mNewImplementation.addAppOpsModeChangedListener(listener);
|
||||
|
||||
if (oldVal != newVal) {
|
||||
signalImplDifference("addAppOpsModeChangedListener");
|
||||
}
|
||||
|
||||
return newVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAppOpsModeChangedListener(AppOpsModeChangedListener listener) {
|
||||
boolean oldVal = mOldImplementation.removeAppOpsModeChangedListener(listener);
|
||||
boolean newVal = mNewImplementation.removeAppOpsModeChangedListener(listener);
|
||||
|
||||
if (oldVal != newVal) {
|
||||
signalImplDifference("removeAppOpsModeChangedListener");
|
||||
}
|
||||
|
||||
return newVal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,43 +20,40 @@ import android.app.AppOpsManager
|
||||
import android.os.Handler
|
||||
import android.os.UserHandle
|
||||
import android.util.ArrayMap
|
||||
import android.util.SparseArray
|
||||
import android.util.ArraySet
|
||||
import android.util.SparseBooleanArray
|
||||
import android.util.SparseIntArray
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.internal.util.ArrayUtils
|
||||
import com.android.server.appop.AppOpsCheckingServiceInterface
|
||||
import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener
|
||||
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.collection.forEachIndexed
|
||||
import com.android.server.permission.access.collection.set
|
||||
|
||||
class AppOpService(
|
||||
private val service: AccessCheckingService
|
||||
) : AppOpsCheckingServiceInterface {
|
||||
private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME)
|
||||
as PackageAppOpPolicy
|
||||
private val uidPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME)
|
||||
private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME)
|
||||
as AppIdAppOpPolicy
|
||||
|
||||
private val context = service.context
|
||||
private lateinit var handler: Handler
|
||||
private lateinit var lock: Any
|
||||
private lateinit var switchedOps: SparseArray<IntArray>
|
||||
|
||||
@Volatile
|
||||
private var listeners = ArraySet<AppOpsModeChangedListener>()
|
||||
private val listenersLock = Any()
|
||||
|
||||
fun initialize() {
|
||||
// TODO(b/252883039): Wrong handler. Inject main thread handler here.
|
||||
handler = Handler(context.mainLooper)
|
||||
// TODO(b/252883039): Wrong lock object. Inject AppOpsService here.
|
||||
lock = Any()
|
||||
|
||||
switchedOps = SparseArray()
|
||||
for (switchedCode in 0 until AppOpsManager._NUM_OP) {
|
||||
val switchCode = AppOpsManager.opToSwitch(switchedCode)
|
||||
switchedOps.put(switchCode,
|
||||
ArrayUtils.appendInt(switchedOps.get(switchCode), switchedCode))
|
||||
}
|
||||
appIdPolicy.addOnAppOpModeChangedListener(OnAppIdAppOpModeChangedListener())
|
||||
packagePolicy.addOnAppOpModeChangedListener(OnPackageAppOpModeChangedListener())
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -90,16 +87,14 @@ class AppOpService(
|
||||
val userId = UserHandle.getUserId(uid)
|
||||
val opName = AppOpsManager.opToPublicName(op)
|
||||
return service.getState {
|
||||
with(uidPolicy) { getAppOpMode(appId, userId, opName) }
|
||||
with(appIdPolicy) { getAppOpMode(appId, userId, opName) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUidModes(uid: Int): ArrayMap<String, Int>? {
|
||||
val appId = UserHandle.getAppId(uid)
|
||||
val userId = UserHandle.getUserId(uid)
|
||||
return service.getState {
|
||||
with(uidPolicy) { getAppOpModes(appId, userId) }
|
||||
}?.map
|
||||
return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map
|
||||
}
|
||||
|
||||
override fun setUidMode(uid: Int, op: Int, mode: Int): Boolean {
|
||||
@@ -108,7 +103,7 @@ class AppOpService(
|
||||
val opName = AppOpsManager.opToPublicName(op)
|
||||
var wasChanged = false
|
||||
service.mutateState {
|
||||
wasChanged = with(uidPolicy) { setAppOpMode(appId, userId, opName, mode) }
|
||||
wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) }
|
||||
}
|
||||
return wasChanged
|
||||
}
|
||||
@@ -137,7 +132,7 @@ class AppOpService(
|
||||
val appId = UserHandle.getAppId(uid)
|
||||
val userId = UserHandle.getUserId(uid)
|
||||
service.mutateState {
|
||||
with(uidPolicy) { removeAppOpModes(appId, userId) }
|
||||
with(appIdPolicy) { removeAppOpModes(appId, userId) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,4 +189,89 @@ class AppOpService(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addAppOpsModeChangedListener(listener: AppOpsModeChangedListener): Boolean {
|
||||
synchronized(listenersLock) {
|
||||
val newListeners = ArraySet(listeners)
|
||||
val result = newListeners.add(listener)
|
||||
listeners = newListeners
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeAppOpsModeChangedListener(listener: AppOpsModeChangedListener): Boolean {
|
||||
synchronized(listenersLock) {
|
||||
val newListeners = ArraySet(listeners)
|
||||
val result = newListeners.remove(listener)
|
||||
listeners = newListeners
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() {
|
||||
// (uid, appOpCode) -> newMode
|
||||
val pendingChanges = ArrayMap<Pair<Int, Int>, Int>()
|
||||
|
||||
override fun onAppOpModeChanged(
|
||||
appId: Int,
|
||||
userId: Int,
|
||||
appOpName: String,
|
||||
oldMode: Int,
|
||||
newMode: Int
|
||||
) {
|
||||
val uid = UserHandle.getUid(userId, appId)
|
||||
val appOpCode = AppOpsManager.strOpToOp(appOpName)
|
||||
val key = Pair(uid, appOpCode)
|
||||
|
||||
pendingChanges[key] = newMode
|
||||
}
|
||||
|
||||
override fun onStateMutated() {
|
||||
val listenersLocal = listeners
|
||||
pendingChanges.forEachIndexed { _, key, mode ->
|
||||
listenersLocal.forEachIndexed { _, listener ->
|
||||
val uid = key.first
|
||||
val appOpCode = key.second
|
||||
|
||||
listener.onUidModeChanged(uid, appOpCode, mode)
|
||||
}
|
||||
}
|
||||
|
||||
pendingChanges.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnPackageAppOpModeChangedListener :
|
||||
PackageAppOpPolicy.OnAppOpModeChangedListener() {
|
||||
// (packageName, userId, appOpCode) -> newMode
|
||||
val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>()
|
||||
|
||||
override fun onAppOpModeChanged(
|
||||
packageName: String,
|
||||
userId: Int,
|
||||
appOpName: String,
|
||||
oldMode: Int,
|
||||
newMode: Int
|
||||
) {
|
||||
val appOpCode = AppOpsManager.strOpToOp(appOpName)
|
||||
val key = Triple(packageName, userId, appOpCode)
|
||||
|
||||
pendingChanges[key] = newMode
|
||||
}
|
||||
|
||||
override fun onStateMutated() {
|
||||
val listenersLocal = listeners
|
||||
pendingChanges.forEachIndexed { _, key, mode ->
|
||||
listenersLocal.forEachIndexed { _, listener ->
|
||||
val packageName = key.first
|
||||
val userId = key.second
|
||||
val appOpCode = key.third
|
||||
|
||||
listener.onPackageModeChanged(packageName, userId, appOpCode, mode)
|
||||
}
|
||||
}
|
||||
|
||||
pendingChanges.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
@@ -163,6 +164,8 @@ public class AppOpsServiceTest {
|
||||
when(mockPackageManagerInternal.getPackageStateInternal(sMyPackageName))
|
||||
.thenReturn(mockMyPSInternal);
|
||||
when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg);
|
||||
when(mockPackageManagerInternal.getPackageUid(eq(sMyPackageName), anyLong(),
|
||||
eq(getUserId(mMyUid)))).thenReturn(mMyUid);
|
||||
doReturn(mockPackageManagerInternal).when(
|
||||
() -> LocalServices.getService(PackageManagerInternal.class));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user