Merge "Listener to watch op starts" into rvc-dev am: 316bc6ff66 am: 2d3310ec59 am: 911b6104c8
Change-Id: I3a7c8242e1bde2a129cfc2bfe2dedb9d4f459b35
This commit is contained in:
@@ -66,6 +66,7 @@ import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsNotedCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.app.IAppOpsStartedCallback;
|
||||
import com.android.internal.app.MessageSamplingConfig;
|
||||
import com.android.internal.os.RuntimeInit;
|
||||
import com.android.internal.os.ZygoteInit;
|
||||
@@ -201,6 +202,10 @@ public class AppOpsManager {
|
||||
private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
|
||||
new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mStartedWatchers")
|
||||
private final ArrayMap<OnOpStartedListener, IAppOpsStartedCallback> mStartedWatchers =
|
||||
new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mNotedWatchers")
|
||||
private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
|
||||
new ArrayMap<>();
|
||||
@@ -6367,6 +6372,25 @@ public class AppOpsManager {
|
||||
default void onOpActiveChanged(int op, int uid, String packageName, boolean active) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for notification of an op being started.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface OnOpStartedListener {
|
||||
/**
|
||||
* Called when an op was started.
|
||||
*
|
||||
* Note: This is only for op starts. It is not called when an op is noted or stopped.
|
||||
*
|
||||
* @param op The op code.
|
||||
* @param uid The UID performing the operation.
|
||||
* @param packageName The package performing the operation.
|
||||
* @param result The result of the start.
|
||||
*/
|
||||
void onOpStarted(int op, int uid, String packageName, int result);
|
||||
}
|
||||
|
||||
AppOpsManager(Context context, IAppOpsService service) {
|
||||
mContext = context;
|
||||
mService = service;
|
||||
@@ -6921,6 +6945,73 @@ public class AppOpsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start watching for started app-ops.
|
||||
* An app-op may be long running and it has a clear start delimiter.
|
||||
* If an op start is attempted by any package, you will get a callback.
|
||||
* To change the watched ops for a registered callback you need to unregister and register it
|
||||
* again.
|
||||
*
|
||||
* <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
|
||||
* you can watch changes only for your UID.
|
||||
*
|
||||
* @param ops The operations to watch.
|
||||
* @param callback Where to report changes.
|
||||
*
|
||||
* @see #stopWatchingStarted(OnOpStartedListener)
|
||||
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
|
||||
* @see #startWatchingNoted(int[], OnOpNotedListener)
|
||||
* @see #startOp(int, int, String, boolean, String, String)
|
||||
* @see #finishOp(int, int, String, String)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
|
||||
public void startWatchingStarted(@NonNull int[] ops, @NonNull OnOpStartedListener callback) {
|
||||
IAppOpsStartedCallback cb;
|
||||
synchronized (mStartedWatchers) {
|
||||
if (mStartedWatchers.containsKey(callback)) {
|
||||
return;
|
||||
}
|
||||
cb = new IAppOpsStartedCallback.Stub() {
|
||||
@Override
|
||||
public void opStarted(int op, int uid, String packageName, int mode) {
|
||||
callback.onOpStarted(op, uid, packageName, mode);
|
||||
}
|
||||
};
|
||||
mStartedWatchers.put(callback, cb);
|
||||
}
|
||||
try {
|
||||
mService.startWatchingStarted(ops, cb);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop watching for started app-ops.
|
||||
* An app-op may be long running and it has a clear start delimiter.
|
||||
* Henceforth, if an op start is attempted by any package, you will not get a callback.
|
||||
* Unregistering a non-registered callback has no effect.
|
||||
*
|
||||
* @see #startWatchingStarted(int[], OnOpStartedListener)
|
||||
* @see #startOp(int, int, String, boolean, String, String)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void stopWatchingStarted(@NonNull OnOpStartedListener callback) {
|
||||
synchronized (mStartedWatchers) {
|
||||
final IAppOpsStartedCallback cb = mStartedWatchers.remove(callback);
|
||||
if (cb != null) {
|
||||
try {
|
||||
mService.stopWatchingStarted(cb);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start watching for noted app ops. An app op may be immediate or long running.
|
||||
* Immediate ops are noted while long running ones are started and stopped. This
|
||||
@@ -6935,6 +7026,7 @@ public class AppOpsManager {
|
||||
* @param callback Where to report changes.
|
||||
*
|
||||
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
|
||||
* @see #startWatchingStarted(int[], OnOpStartedListener)
|
||||
* @see #stopWatchingNoted(OnOpNotedListener)
|
||||
* @see #noteOp(String, int, String, String, String)
|
||||
*
|
||||
@@ -6974,7 +7066,7 @@ public class AppOpsManager {
|
||||
*/
|
||||
public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
|
||||
synchronized (mNotedWatchers) {
|
||||
final IAppOpsNotedCallback cb = mNotedWatchers.get(callback);
|
||||
final IAppOpsNotedCallback cb = mNotedWatchers.remove(callback);
|
||||
if (cb != null) {
|
||||
try {
|
||||
mService.stopWatchingNoted(cb);
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsActiveCallback;
|
||||
import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
||||
import com.android.internal.app.IAppOpsNotedCallback;
|
||||
import com.android.internal.app.IAppOpsStartedCallback;
|
||||
import com.android.internal.app.MessageSamplingConfig;
|
||||
|
||||
interface IAppOpsService {
|
||||
@@ -91,6 +92,9 @@ interface IAppOpsService {
|
||||
void stopWatchingActive(IAppOpsActiveCallback callback);
|
||||
boolean isOperationActive(int code, int uid, String packageName);
|
||||
|
||||
void startWatchingStarted(in int[] ops, IAppOpsStartedCallback callback);
|
||||
void stopWatchingStarted(IAppOpsStartedCallback callback);
|
||||
|
||||
void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);
|
||||
|
||||
void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
// Iterface to observe op starts
|
||||
oneway interface IAppOpsStartedCallback {
|
||||
void opStarted(int op, int uid, String packageName, int mode);
|
||||
}
|
||||
@@ -1634,22 +1634,24 @@ public final class ActiveServices {
|
||||
new AppOpsManager.OnOpNotedListener() {
|
||||
@Override
|
||||
public void onOpNoted(int op, int uid, String pkgName, int result) {
|
||||
if (uid == mProcessRecord.uid && isNotTop()) {
|
||||
incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
|
||||
}
|
||||
incrementOpCountIfNeeded(op, uid, result);
|
||||
}
|
||||
};
|
||||
|
||||
private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
|
||||
new AppOpsManager.OnOpActiveChangedInternalListener() {
|
||||
private final AppOpsManager.OnOpStartedListener mOpStartedCallback =
|
||||
new AppOpsManager.OnOpStartedListener() {
|
||||
@Override
|
||||
public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
|
||||
if (uid == mProcessRecord.uid && active && isNotTop()) {
|
||||
incrementOpCount(op, true);
|
||||
}
|
||||
public void onOpStarted(int op, int uid, String pkgName, int result) {
|
||||
incrementOpCountIfNeeded(op, uid, result);
|
||||
}
|
||||
};
|
||||
|
||||
private void incrementOpCountIfNeeded(int op, int uid, @AppOpsManager.Mode int result) {
|
||||
if (uid == mProcessRecord.uid && isNotTop()) {
|
||||
incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotTop() {
|
||||
return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
|
||||
}
|
||||
@@ -1674,7 +1676,7 @@ public final class ActiveServices {
|
||||
mNumFgs++;
|
||||
if (mNumFgs == 1) {
|
||||
mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
|
||||
mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
|
||||
mAppOpsManager.startWatchingStarted(LOGGED_AP_OPS, mOpStartedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1684,7 +1686,7 @@ public final class ActiveServices {
|
||||
mDestroyed = true;
|
||||
logFinalValues();
|
||||
mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
|
||||
mAppOpsManager.stopWatchingActive(mOpActiveCallback);
|
||||
mAppOpsManager.stopWatchingStarted(mOpStartedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,7 @@ import com.android.internal.app.IAppOpsAsyncNotedCallback;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsNotedCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.app.IAppOpsStartedCallback;
|
||||
import com.android.internal.app.MessageSamplingConfig;
|
||||
import com.android.internal.os.Zygote;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
@@ -1292,6 +1293,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
|
||||
final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
|
||||
final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
|
||||
final ArrayMap<IBinder, SparseArray<StartedCallback>> mStartedWatchers = new ArrayMap<>();
|
||||
final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
|
||||
final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager();
|
||||
|
||||
@@ -1407,6 +1409,50 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
final class StartedCallback implements DeathRecipient {
|
||||
final IAppOpsStartedCallback mCallback;
|
||||
final int mWatchingUid;
|
||||
final int mCallingUid;
|
||||
final int mCallingPid;
|
||||
|
||||
StartedCallback(IAppOpsStartedCallback callback, int watchingUid, int callingUid,
|
||||
int callingPid) {
|
||||
mCallback = callback;
|
||||
mWatchingUid = watchingUid;
|
||||
mCallingUid = callingUid;
|
||||
mCallingPid = callingPid;
|
||||
try {
|
||||
mCallback.asBinder().linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
/*ignored*/
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
sb.append("StartedCallback{");
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append(" watchinguid=");
|
||||
UserHandle.formatUid(sb, mWatchingUid);
|
||||
sb.append(" from uid=");
|
||||
UserHandle.formatUid(sb, mCallingUid);
|
||||
sb.append(" pid=");
|
||||
sb.append(mCallingPid);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
mCallback.asBinder().unlinkToDeath(this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
stopWatchingStarted(mCallback);
|
||||
}
|
||||
}
|
||||
|
||||
final class NotedCallback implements DeathRecipient {
|
||||
final IAppOpsNotedCallback mCallback;
|
||||
final int mWatchingUid;
|
||||
@@ -3031,13 +3077,12 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
return AppOpsManager.MODE_ERRORED;
|
||||
}
|
||||
final Op op = getOpLocked(ops, code, uid, true);
|
||||
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
|
||||
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
|
||||
scheduleOpNotedIfNeededLocked(code, uid, packageName,
|
||||
AppOpsManager.MODE_IGNORED);
|
||||
return AppOpsManager.MODE_IGNORED;
|
||||
}
|
||||
final UidState uidState = ops.uidState;
|
||||
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
|
||||
if (attributedOp.isRunning()) {
|
||||
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
|
||||
+ code + " startTime of in progress event="
|
||||
@@ -3045,6 +3090,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
|
||||
final int switchCode = AppOpsManager.opToSwitch(code);
|
||||
final UidState uidState = ops.uidState;
|
||||
// If there is a non-default per UID policy (we set UID op mode only if
|
||||
// non-default) it takes over, otherwise use the per package policy.
|
||||
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
|
||||
@@ -3076,10 +3122,9 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
+ packageName + (attributionTag == null ? ""
|
||||
: "." + attributionTag));
|
||||
}
|
||||
scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED);
|
||||
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state,
|
||||
flags);
|
||||
scheduleOpNotedIfNeededLocked(code, uid, packageName,
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
|
||||
if (shouldCollectAsyncNotedOp) {
|
||||
collectAsyncNotedOp(uid, packageName, code, attributionTag, message);
|
||||
@@ -3092,7 +3137,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
// TODO moltmann: Allow watching for attribution ops
|
||||
@Override
|
||||
public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
|
||||
int watchedUid = -1;
|
||||
int watchedUid = Process.INVALID_UID;
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
|
||||
@@ -3138,6 +3183,54 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startWatchingStarted(int[] ops, @NonNull IAppOpsStartedCallback callback) {
|
||||
int watchedUid = Process.INVALID_UID;
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
watchedUid = callingUid;
|
||||
}
|
||||
|
||||
Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
|
||||
Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
|
||||
"Invalid op code in: " + Arrays.toString(ops));
|
||||
Objects.requireNonNull(callback, "Callback cannot be null");
|
||||
|
||||
synchronized (this) {
|
||||
SparseArray<StartedCallback> callbacks = mStartedWatchers.get(callback.asBinder());
|
||||
if (callbacks == null) {
|
||||
callbacks = new SparseArray<>();
|
||||
mStartedWatchers.put(callback.asBinder(), callbacks);
|
||||
}
|
||||
|
||||
final StartedCallback startedCallback = new StartedCallback(callback, watchedUid,
|
||||
callingUid, callingPid);
|
||||
for (int op : ops) {
|
||||
callbacks.put(op, startedCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopWatchingStarted(IAppOpsStartedCallback callback) {
|
||||
Objects.requireNonNull(callback, "Callback cannot be null");
|
||||
|
||||
synchronized (this) {
|
||||
final SparseArray<StartedCallback> startedCallbacks =
|
||||
mStartedWatchers.remove(callback.asBinder());
|
||||
if (startedCallbacks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int callbackCount = startedCallbacks.size();
|
||||
for (int i = 0; i < callbackCount; i++) {
|
||||
startedCallbacks.valueAt(i).destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
|
||||
int watchedUid = Process.INVALID_UID;
|
||||
@@ -3340,12 +3433,14 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass,
|
||||
true /* edit */);
|
||||
if (ops == null) {
|
||||
scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED);
|
||||
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
|
||||
+ " package " + resolvedPackageName);
|
||||
return AppOpsManager.MODE_ERRORED;
|
||||
}
|
||||
final Op op = getOpLocked(ops, code, uid, true);
|
||||
if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) {
|
||||
scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED);
|
||||
return AppOpsManager.MODE_IGNORED;
|
||||
}
|
||||
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
|
||||
@@ -3353,7 +3448,6 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
final UidState uidState = ops.uidState;
|
||||
// If there is a non-default per UID policy (we set UID op mode only if
|
||||
// non-default) it takes over, otherwise use the per package policy.
|
||||
final int opCode = op.op;
|
||||
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
|
||||
final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
|
||||
if (uidMode != AppOpsManager.MODE_ALLOWED
|
||||
@@ -3362,6 +3456,7 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
+ switchCode + " (" + code + ") uid " + uid + " package "
|
||||
+ resolvedPackageName);
|
||||
attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
|
||||
scheduleOpStartedIfNeededLocked(code, uid, packageName, uidMode);
|
||||
return uidMode;
|
||||
}
|
||||
} else {
|
||||
@@ -3374,11 +3469,13 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
+ switchCode + " (" + code + ") uid " + uid + " package "
|
||||
+ resolvedPackageName);
|
||||
attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
|
||||
scheduleOpStartedIfNeededLocked(code, uid, packageName, mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
|
||||
+ " package " + resolvedPackageName);
|
||||
scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED);
|
||||
try {
|
||||
attributedOp.started(clientId, uidState.state);
|
||||
} catch (RemoteException e) {
|
||||
@@ -3480,6 +3577,52 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, int result) {
|
||||
ArraySet<StartedCallback> dispatchedCallbacks = null;
|
||||
final int callbackListCount = mStartedWatchers.size();
|
||||
for (int i = 0; i < callbackListCount; i++) {
|
||||
final SparseArray<StartedCallback> callbacks = mStartedWatchers.valueAt(i);
|
||||
|
||||
StartedCallback callback = callbacks.get(code);
|
||||
if (callback != null) {
|
||||
if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dispatchedCallbacks == null) {
|
||||
dispatchedCallbacks = new ArraySet<>();
|
||||
}
|
||||
dispatchedCallbacks.add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
if (dispatchedCallbacks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHandler.sendMessage(PooledLambda.obtainMessage(
|
||||
AppOpsService::notifyOpStarted,
|
||||
this, dispatchedCallbacks, code, uid, pkgName, result));
|
||||
}
|
||||
|
||||
private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
|
||||
int code, int uid, String packageName, int result) {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final int callbackCount = callbacks.size();
|
||||
for (int i = 0; i < callbackCount; i++) {
|
||||
final StartedCallback callback = callbacks.valueAt(i);
|
||||
try {
|
||||
callback.mCallback.opStarted(code, uid, packageName, result);
|
||||
} catch (RemoteException e) {
|
||||
/* do nothing */
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
|
||||
int result) {
|
||||
ArraySet<NotedCallback> dispatchedCallbacks = null;
|
||||
@@ -5185,6 +5328,56 @@ public class AppOpsService extends IAppOpsService.Stub {
|
||||
pw.println(cb);
|
||||
}
|
||||
}
|
||||
if (mStartedWatchers.size() > 0 && dumpMode < 0) {
|
||||
needSep = true;
|
||||
boolean printedHeader = false;
|
||||
|
||||
final int watchersSize = mStartedWatchers.size();
|
||||
for (int watcherNum = 0; watcherNum < watchersSize; watcherNum++) {
|
||||
final SparseArray<StartedCallback> startedWatchers =
|
||||
mStartedWatchers.valueAt(watcherNum);
|
||||
if (startedWatchers.size() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final StartedCallback cb = startedWatchers.valueAt(0);
|
||||
if (dumpOp >= 0 && startedWatchers.indexOfKey(dumpOp) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dumpPackage != null
|
||||
&& dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!printedHeader) {
|
||||
pw.println(" All op started watchers:");
|
||||
printedHeader = true;
|
||||
}
|
||||
|
||||
pw.print(" ");
|
||||
pw.print(Integer.toHexString(System.identityHashCode(
|
||||
mStartedWatchers.keyAt(watcherNum))));
|
||||
pw.println(" ->");
|
||||
|
||||
pw.print(" [");
|
||||
final int opCount = startedWatchers.size();
|
||||
for (int opNum = 0; opNum < opCount; opNum++) {
|
||||
if (opNum > 0) {
|
||||
pw.print(' ');
|
||||
}
|
||||
|
||||
pw.print(AppOpsManager.opToName(startedWatchers.keyAt(opNum)));
|
||||
if (opNum < opCount - 1) {
|
||||
pw.print(',');
|
||||
}
|
||||
}
|
||||
pw.println("]");
|
||||
|
||||
pw.print(" ");
|
||||
pw.println(cb);
|
||||
}
|
||||
}
|
||||
if (mNotedWatchers.size() > 0 && dumpMode < 0) {
|
||||
needSep = true;
|
||||
boolean printedHeader = false;
|
||||
|
||||
@@ -110,6 +110,22 @@ public class AppOpsActiveWatcherTest {
|
||||
|
||||
// We should not be getting any callbacks
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// Start watching op again
|
||||
appOpsManager.startWatchingActive(new String[] {AppOpsManager.OPSTR_CAMERA},
|
||||
getContext().getMainExecutor(), listener);
|
||||
|
||||
// Start the op
|
||||
appOpsManager.startOp(AppOpsManager.OP_CAMERA);
|
||||
|
||||
// We should get the callback again (and since we reset the listener, we therefore expect 1)
|
||||
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
|
||||
.times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
|
||||
eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
|
||||
|
||||
// Finish up
|
||||
appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
|
||||
appOpsManager.stopWatchingActive(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,26 +16,26 @@
|
||||
|
||||
package com.android.server.appops;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.OnOpNotedListener;
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.OnOpNotedListener;
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
/**
|
||||
* Tests watching noted ops.
|
||||
*/
|
||||
@@ -77,6 +77,27 @@ public class AppOpsNotedWatcherTest {
|
||||
|
||||
// This should be the only two callbacks we got
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// Note the op again and verify it isn't being watched
|
||||
appOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION);
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// Start watching again
|
||||
appOpsManager.startWatchingNoted(new int[]{AppOpsManager.OP_FINE_LOCATION,
|
||||
AppOpsManager.OP_CAMERA}, listener);
|
||||
|
||||
// Note the op again
|
||||
appOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION, Process.myUid(),
|
||||
getContext().getPackageName());
|
||||
|
||||
// Verify it's watched again
|
||||
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
|
||||
.times(2)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION),
|
||||
eq(Process.myUid()), eq(getContext().getPackageName()),
|
||||
eq(AppOpsManager.MODE_ALLOWED));
|
||||
|
||||
// Finish up
|
||||
appOpsManager.stopWatchingNoted(listener);
|
||||
}
|
||||
|
||||
private static Context getContext() {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.appop;
|
||||
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.OnOpStartedListener;
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
/** Tests watching started ops. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AppOpsStartedWatcherTest {
|
||||
|
||||
private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
@Test
|
||||
public void testWatchStartedOps() {
|
||||
// Create a mock listener
|
||||
final OnOpStartedListener listener = mock(OnOpStartedListener.class);
|
||||
|
||||
// Start watching started ops
|
||||
final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
|
||||
appOpsManager.startWatchingStarted(new int[]{AppOpsManager.OP_FINE_LOCATION,
|
||||
AppOpsManager.OP_CAMERA}, listener);
|
||||
|
||||
// Start some ops
|
||||
appOpsManager.startOp(AppOpsManager.OP_FINE_LOCATION);
|
||||
appOpsManager.startOp(AppOpsManager.OP_CAMERA);
|
||||
appOpsManager.startOp(AppOpsManager.OP_RECORD_AUDIO);
|
||||
|
||||
// Verify that we got called for the ops being started
|
||||
final InOrder inOrder = inOrder(listener);
|
||||
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
|
||||
.times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION),
|
||||
eq(Process.myUid()), eq(getContext().getPackageName()),
|
||||
eq(AppOpsManager.MODE_ALLOWED));
|
||||
inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
|
||||
.times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
|
||||
eq(Process.myUid()), eq(getContext().getPackageName()),
|
||||
eq(AppOpsManager.MODE_ALLOWED));
|
||||
|
||||
// Stop watching
|
||||
appOpsManager.stopWatchingStarted(listener);
|
||||
|
||||
// This should be the only two callbacks we got
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// Start the op again and verify it isn't being watched
|
||||
appOpsManager.startOp(AppOpsManager.OP_FINE_LOCATION);
|
||||
appOpsManager.finishOp(AppOpsManager.OP_FINE_LOCATION);
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// Start watching an op again (only CAMERA this time)
|
||||
appOpsManager.startWatchingStarted(new int[]{AppOpsManager.OP_CAMERA}, listener);
|
||||
|
||||
// Note the ops again
|
||||
appOpsManager.startOp(AppOpsManager.OP_CAMERA);
|
||||
appOpsManager.startOp(AppOpsManager.OP_FINE_LOCATION);
|
||||
|
||||
// Verify it's watched again
|
||||
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
|
||||
.times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
|
||||
eq(Process.myUid()), eq(getContext().getPackageName()),
|
||||
eq(AppOpsManager.MODE_ALLOWED));
|
||||
verifyNoMoreInteractions(listener);
|
||||
|
||||
// Finish up
|
||||
appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
|
||||
appOpsManager.finishOp(AppOpsManager.OP_FINE_LOCATION);
|
||||
appOpsManager.stopWatchingStarted(listener);
|
||||
}
|
||||
|
||||
private static Context getContext() {
|
||||
return InstrumentationRegistry.getContext();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user