[DO NOT MERGE] Keep track of audio recordings that are silenced
Sometimes, an app will have an active App Op for recording, but it is silenced (for example, after a while being in background). This means they cannot get the recording audio stream. This change keeps track of that and prevents them from being considered as actively using AppOpsManager.OP_RECORD_AUDIO from SystemUI's perspective. It does two things: - If a particular UID has any of its AudioRecordingConfigurations as silenced, it will be marked as paused and an inactive update will be sent to listeners of AppOpsController. When the recording stops being silent, an active update will be sent. - When getting the list of active items, paused ones will be filtered out. Test: manual with recording app Test: atest AppOpsControllerTest Bug: 162552566 Change-Id: I72fbebf4b37b799907855d57e6c615c244b63999
This commit is contained in:
@@ -25,7 +25,9 @@ public class AppOpItem {
|
|||||||
private int mUid;
|
private int mUid;
|
||||||
private String mPackageName;
|
private String mPackageName;
|
||||||
private long mTimeStarted;
|
private long mTimeStarted;
|
||||||
private String mState;
|
private StringBuilder mState;
|
||||||
|
// This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO
|
||||||
|
private boolean mSilenced;
|
||||||
|
|
||||||
public AppOpItem(int code, int uid, String packageName, long timeStarted) {
|
public AppOpItem(int code, int uid, String packageName, long timeStarted) {
|
||||||
this.mCode = code;
|
this.mCode = code;
|
||||||
@@ -36,9 +38,8 @@ public class AppOpItem {
|
|||||||
.append("AppOpItem(")
|
.append("AppOpItem(")
|
||||||
.append("Op code=").append(code).append(", ")
|
.append("Op code=").append(code).append(", ")
|
||||||
.append("UID=").append(uid).append(", ")
|
.append("UID=").append(uid).append(", ")
|
||||||
.append("Package name=").append(packageName)
|
.append("Package name=").append(packageName).append(", ")
|
||||||
.append(")")
|
.append("Paused=");
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCode() {
|
public int getCode() {
|
||||||
@@ -57,8 +58,16 @@ public class AppOpItem {
|
|||||||
return mTimeStarted;
|
return mTimeStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSilenced(boolean silenced) {
|
||||||
|
mSilenced = silenced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSilenced() {
|
||||||
|
return mSilenced;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mState;
|
return mState.append(mSilenced).append(")").toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,15 @@ package com.android.systemui.appops;
|
|||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.AudioRecordingConfiguration;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
@@ -62,6 +65,7 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
private final AppOpsManager mAppOps;
|
private final AppOpsManager mAppOps;
|
||||||
|
private final AudioManager mAudioManager;
|
||||||
private H mBGHandler;
|
private H mBGHandler;
|
||||||
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
|
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
|
||||||
private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
|
private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
|
||||||
@@ -72,6 +76,9 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
private final List<AppOpItem> mActiveItems = new ArrayList<>();
|
private final List<AppOpItem> mActiveItems = new ArrayList<>();
|
||||||
@GuardedBy("mNotedItems")
|
@GuardedBy("mNotedItems")
|
||||||
private final List<AppOpItem> mNotedItems = new ArrayList<>();
|
private final List<AppOpItem> mNotedItems = new ArrayList<>();
|
||||||
|
@GuardedBy("mActiveItems")
|
||||||
|
private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid =
|
||||||
|
new SparseArray<>();
|
||||||
|
|
||||||
protected static final int[] OPS = new int[] {
|
protected static final int[] OPS = new int[] {
|
||||||
AppOpsManager.OP_CAMERA,
|
AppOpsManager.OP_CAMERA,
|
||||||
@@ -86,7 +93,8 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
Context context,
|
Context context,
|
||||||
@Background Looper bgLooper,
|
@Background Looper bgLooper,
|
||||||
DumpManager dumpManager,
|
DumpManager dumpManager,
|
||||||
PermissionFlagsCache cache
|
PermissionFlagsCache cache,
|
||||||
|
AudioManager audioManager
|
||||||
) {
|
) {
|
||||||
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||||
mFlagsCache = cache;
|
mFlagsCache = cache;
|
||||||
@@ -95,6 +103,7 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
for (int i = 0; i < numOps; i++) {
|
for (int i = 0; i < numOps; i++) {
|
||||||
mCallbacksByCode.put(OPS[i], new ArraySet<>());
|
mCallbacksByCode.put(OPS[i], new ArraySet<>());
|
||||||
}
|
}
|
||||||
|
mAudioManager = audioManager;
|
||||||
dumpManager.registerDumpable(TAG, this);
|
dumpManager.registerDumpable(TAG, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +118,19 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
if (listening) {
|
if (listening) {
|
||||||
mAppOps.startWatchingActive(OPS, this);
|
mAppOps.startWatchingActive(OPS, this);
|
||||||
mAppOps.startWatchingNoted(OPS, this);
|
mAppOps.startWatchingNoted(OPS, this);
|
||||||
|
mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
|
||||||
|
mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
|
||||||
|
mAudioManager.getActiveRecordingConfigurations()));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
mAppOps.stopWatchingActive(this);
|
mAppOps.stopWatchingActive(this);
|
||||||
mAppOps.stopWatchingNoted(this);
|
mAppOps.stopWatchingNoted(this);
|
||||||
|
mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
|
||||||
|
|
||||||
mBGHandler.removeCallbacksAndMessages(null); // null removes all
|
mBGHandler.removeCallbacksAndMessages(null); // null removes all
|
||||||
synchronized (mActiveItems) {
|
synchronized (mActiveItems) {
|
||||||
mActiveItems.clear();
|
mActiveItems.clear();
|
||||||
|
mRecordingsByUid.clear();
|
||||||
}
|
}
|
||||||
synchronized (mNotedItems) {
|
synchronized (mNotedItems) {
|
||||||
mNotedItems.clear();
|
mNotedItems.clear();
|
||||||
@@ -187,9 +203,12 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
|
AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
|
||||||
if (item == null && active) {
|
if (item == null && active) {
|
||||||
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
|
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
|
||||||
|
if (code == AppOpsManager.OP_RECORD_AUDIO) {
|
||||||
|
item.setSilenced(isAnyRecordingPausedLocked(uid));
|
||||||
|
}
|
||||||
mActiveItems.add(item);
|
mActiveItems.add(item);
|
||||||
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
|
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
|
||||||
return true;
|
return !item.isSilenced();
|
||||||
} else if (item != null && !active) {
|
} else if (item != null && !active) {
|
||||||
mActiveItems.remove(item);
|
mActiveItems.remove(item);
|
||||||
if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
|
if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
|
||||||
@@ -213,7 +232,7 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
|
active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
|
||||||
}
|
}
|
||||||
if (!active) {
|
if (!active) {
|
||||||
notifySuscribers(code, uid, packageName, false);
|
notifySuscribersWorker(code, uid, packageName, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +340,7 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
AppOpItem item = mActiveItems.get(i);
|
AppOpItem item = mActiveItems.get(i);
|
||||||
if ((userId == UserHandle.USER_ALL
|
if ((userId == UserHandle.USER_ALL
|
||||||
|| UserHandle.getUserId(item.getUid()) == userId)
|
|| UserHandle.getUserId(item.getUid()) == userId)
|
||||||
&& isUserVisible(item)) {
|
&& isUserVisible(item) && !item.isSilenced()) {
|
||||||
list.add(item);
|
list.add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,6 +359,10 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
|
||||||
|
mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
|
public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -357,7 +380,7 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
// If active is false, we only send the update if the op is not actively noted (prevent
|
// If active is false, we only send the update if the op is not actively noted (prevent
|
||||||
// early removal)
|
// early removal)
|
||||||
if (!alsoNoted) {
|
if (!alsoNoted) {
|
||||||
mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active));
|
notifySuscribers(code, uid, packageName, active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,11 +398,11 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
|
alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
|
||||||
}
|
}
|
||||||
if (!alsoActive) {
|
if (!alsoActive) {
|
||||||
mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true));
|
notifySuscribers(code, uid, packageName, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
|
private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) {
|
||||||
if (mCallbacksByCode.containsKey(code) && isUserVisible(code, uid, packageName)) {
|
if (mCallbacksByCode.containsKey(code) && isUserVisible(code, uid, packageName)) {
|
||||||
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
|
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
|
||||||
for (Callback cb: mCallbacksByCode.get(code)) {
|
for (Callback cb: mCallbacksByCode.get(code)) {
|
||||||
@@ -405,6 +428,61 @@ public class AppOpsControllerImpl implements AppOpsController,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAnyRecordingPausedLocked(int uid) {
|
||||||
|
List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
|
||||||
|
if (configs == null) return false;
|
||||||
|
int configsNum = configs.size();
|
||||||
|
for (int i = 0; i < configsNum; i++) {
|
||||||
|
AudioRecordingConfiguration config = configs.get(i);
|
||||||
|
if (config.isClientSilenced()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateRecordingPausedStatus() {
|
||||||
|
synchronized (mActiveItems) {
|
||||||
|
int size = mActiveItems.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
AppOpItem item = mActiveItems.get(i);
|
||||||
|
if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) {
|
||||||
|
boolean paused = isAnyRecordingPausedLocked(item.getUid());
|
||||||
|
if (item.isSilenced() != paused) {
|
||||||
|
item.setSilenced(paused);
|
||||||
|
notifySuscribers(
|
||||||
|
item.getCode(),
|
||||||
|
item.getUid(),
|
||||||
|
item.getPackageName(),
|
||||||
|
!item.isSilenced()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AudioManager.AudioRecordingCallback mAudioRecordingCallback =
|
||||||
|
new AudioManager.AudioRecordingCallback() {
|
||||||
|
@Override
|
||||||
|
public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
|
||||||
|
synchronized (mActiveItems) {
|
||||||
|
mRecordingsByUid.clear();
|
||||||
|
final int recordingsCount = configs.size();
|
||||||
|
for (int i = 0; i < recordingsCount; i++) {
|
||||||
|
AudioRecordingConfiguration recording = configs.get(i);
|
||||||
|
|
||||||
|
ArrayList<AudioRecordingConfiguration> recordings = mRecordingsByUid.get(
|
||||||
|
recording.getClientUid());
|
||||||
|
if (recordings == null) {
|
||||||
|
recordings = new ArrayList<>();
|
||||||
|
mRecordingsByUid.put(recording.getClientUid(), recordings);
|
||||||
|
}
|
||||||
|
recordings.add(recording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateRecordingPausedStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected class H extends Handler {
|
protected class H extends Handler {
|
||||||
H(Looper looper) {
|
H(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ import static org.mockito.ArgumentMatchers.anyInt;
|
|||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -34,6 +37,8 @@ import static org.mockito.Mockito.when;
|
|||||||
|
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.AudioRecordingConfiguration;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
@@ -47,9 +52,11 @@ import com.android.systemui.dump.DumpManager;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -73,6 +80,12 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
|||||||
private PermissionFlagsCache mFlagsCache;
|
private PermissionFlagsCache mFlagsCache;
|
||||||
@Mock
|
@Mock
|
||||||
private PackageManager mPackageManager;
|
private PackageManager mPackageManager;
|
||||||
|
@Mock(stubOnly = true)
|
||||||
|
private AudioManager mAudioManager;
|
||||||
|
@Mock(stubOnly = true)
|
||||||
|
private AudioManager.AudioRecordingCallback mRecordingCallback;
|
||||||
|
@Mock(stubOnly = true)
|
||||||
|
private AudioRecordingConfiguration mPausedMockRecording;
|
||||||
|
|
||||||
private AppOpsControllerImpl mController;
|
private AppOpsControllerImpl mController;
|
||||||
private TestableLooper mTestableLooper;
|
private TestableLooper mTestableLooper;
|
||||||
@@ -94,11 +107,20 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
|||||||
when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
|
when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
|
||||||
eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
|
eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
|
||||||
|
|
||||||
|
doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0))
|
||||||
|
.when(mAudioManager).registerAudioRecordingCallback(any(), any());
|
||||||
|
when(mPausedMockRecording.getClientUid()).thenReturn(TEST_UID);
|
||||||
|
when(mPausedMockRecording.isClientSilenced()).thenReturn(true);
|
||||||
|
|
||||||
|
when(mAudioManager.getActiveRecordingConfigurations())
|
||||||
|
.thenReturn(List.of(mPausedMockRecording));
|
||||||
|
|
||||||
mController = new AppOpsControllerImpl(
|
mController = new AppOpsControllerImpl(
|
||||||
mContext,
|
mContext,
|
||||||
mTestableLooper.getLooper(),
|
mTestableLooper.getLooper(),
|
||||||
mDumpManager,
|
mDumpManager,
|
||||||
mFlagsCache
|
mFlagsCache,
|
||||||
|
mAudioManager
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +385,89 @@ public class AppOpsControllerTest extends SysuiTestCase {
|
|||||||
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
|
AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPausedRecordingIsRetrievedOnCreation() {
|
||||||
|
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
mController.onOpActiveChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
verify(mCallback, never())
|
||||||
|
.onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPausedRecordingFilteredOut() {
|
||||||
|
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
mController.onOpActiveChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
assertTrue(mController.getActiveAppOps().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnlyRecordAudioPaused() {
|
||||||
|
mController.addCallback(new int[]{
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO,
|
||||||
|
AppOpsManager.OP_CAMERA
|
||||||
|
}, mCallback);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
mController.onOpActiveChanged(
|
||||||
|
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
verify(mCallback).onActiveStateChanged(
|
||||||
|
AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
|
List<AppOpItem> list = mController.getActiveAppOps();
|
||||||
|
|
||||||
|
assertEquals(1, list.size());
|
||||||
|
assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnpausedRecordingSentActive() {
|
||||||
|
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
mController.onOpActiveChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
|
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());
|
||||||
|
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
verify(mCallback).onActiveStateChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAudioPausedSentInactive() {
|
||||||
|
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
mController.onOpActiveChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class);
|
||||||
|
when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER);
|
||||||
|
when(mockARC.isClientSilenced()).thenReturn(true);
|
||||||
|
|
||||||
|
mRecordingCallback.onRecordingConfigChanged(List.of(mockARC));
|
||||||
|
mTestableLooper.processAllMessages();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(mCallback);
|
||||||
|
inOrder.verify(mCallback).onActiveStateChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
|
||||||
|
inOrder.verify(mCallback).onActiveStateChanged(
|
||||||
|
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestHandler extends AppOpsControllerImpl.H {
|
private class TestHandler extends AppOpsControllerImpl.H {
|
||||||
TestHandler(Looper looper) {
|
TestHandler(Looper looper) {
|
||||||
mController.super(looper);
|
mController.super(looper);
|
||||||
|
|||||||
Reference in New Issue
Block a user