Loading packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java +14 −5 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ public class AppOpItem { private int mUid; private String mPackageName; 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) { this.mCode = code; Loading @@ -36,9 +38,8 @@ public class AppOpItem { .append("AppOpItem(") .append("Op code=").append(code).append(", ") .append("UID=").append(uid).append(", ") .append("Package name=").append(packageName) .append(")") .toString(); .append("Package name=").append(packageName).append(", ") .append("Paused="); } public int getCode() { Loading @@ -57,8 +58,16 @@ public class AppOpItem { return mTimeStarted; } public void setSilenced(boolean silenced) { mSilenced = silenced; } public boolean isSilenced() { return mSilenced; } @Override public String toString() { return mState; return mState.append(mSilenced).append(")").toString(); } } packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +85 −7 Original line number Diff line number Diff line Loading @@ -19,12 +19,15 @@ package com.android.systemui.appops; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.AudioRecordingConfiguration; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import androidx.annotation.WorkerThread; Loading Loading @@ -62,6 +65,7 @@ public class AppOpsControllerImpl implements AppOpsController, private static final boolean DEBUG = false; private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); Loading @@ -72,6 +76,9 @@ public class AppOpsControllerImpl implements AppOpsController, private final List<AppOpItem> mActiveItems = new ArrayList<>(); @GuardedBy("mNotedItems") private final List<AppOpItem> mNotedItems = new ArrayList<>(); @GuardedBy("mActiveItems") private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid = new SparseArray<>(); protected static final int[] OPS = new int[] { AppOpsManager.OP_CAMERA, Loading @@ -86,7 +93,8 @@ public class AppOpsControllerImpl implements AppOpsController, Context context, @Background Looper bgLooper, DumpManager dumpManager, PermissionFlagsCache cache PermissionFlagsCache cache, AudioManager audioManager ) { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mFlagsCache = cache; Loading @@ -95,6 +103,7 @@ public class AppOpsControllerImpl implements AppOpsController, for (int i = 0; i < numOps; i++) { mCallbacksByCode.put(OPS[i], new ArraySet<>()); } mAudioManager = audioManager; dumpManager.registerDumpable(TAG, this); } Loading @@ -109,12 +118,19 @@ public class AppOpsControllerImpl implements AppOpsController, if (listening) { mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); } else { mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); mBGHandler.removeCallbacksAndMessages(null); // null removes all synchronized (mActiveItems) { mActiveItems.clear(); mRecordingsByUid.clear(); } synchronized (mNotedItems) { mNotedItems.clear(); Loading Loading @@ -187,9 +203,12 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName); if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); if (code == AppOpsManager.OP_RECORD_AUDIO) { item.setSilenced(isAnyRecordingPausedLocked(uid)); } mActiveItems.add(item); if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); return true; return !item.isSilenced(); } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); Loading @@ -213,7 +232,7 @@ public class AppOpsControllerImpl implements AppOpsController, active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } if (!active) { notifySuscribers(code, uid, packageName, false); notifySuscribersWorker(code, uid, packageName, false); } } Loading Loading @@ -321,7 +340,7 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) && isUserVisible(item)) { && isUserVisible(item) && !item.isSilenced()) { list.add(item); } } Loading @@ -340,6 +359,10 @@ public class AppOpsControllerImpl implements AppOpsController, return list; } private void notifySuscribers(int code, int uid, String packageName, boolean active) { mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active)); } @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { if (DEBUG) { Loading @@ -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 // early removal) if (!alsoNoted) { mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active)); notifySuscribers(code, uid, packageName, active); } } Loading @@ -375,11 +398,11 @@ public class AppOpsControllerImpl implements AppOpsController, alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } 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 (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { Loading @@ -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 { H(Looper looper) { super(looper); Loading packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +106 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ 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.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.times; import static org.mockito.Mockito.verify; Loading @@ -34,6 +37,8 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.AudioRecordingConfiguration; import android.os.Looper; import android.os.UserHandle; import android.testing.AndroidTestingRunner; Loading @@ -47,9 +52,11 @@ import com.android.systemui.dump.DumpManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.List; @SmallTest Loading @@ -73,6 +80,12 @@ public class AppOpsControllerTest extends SysuiTestCase { private PermissionFlagsCache mFlagsCache; @Mock 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 TestableLooper mTestableLooper; Loading @@ -94,11 +107,20 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mFlagsCache.getPermissionFlags(anyString(), anyString(), 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( mContext, mTestableLooper.getLooper(), mDumpManager, mFlagsCache mFlagsCache, mAudioManager ); } Loading Loading @@ -363,6 +385,89 @@ public class AppOpsControllerTest extends SysuiTestCase { 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 { TestHandler(Looper looper) { mController.super(looper); Loading Loading
packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java +14 −5 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ public class AppOpItem { private int mUid; private String mPackageName; 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) { this.mCode = code; Loading @@ -36,9 +38,8 @@ public class AppOpItem { .append("AppOpItem(") .append("Op code=").append(code).append(", ") .append("UID=").append(uid).append(", ") .append("Package name=").append(packageName) .append(")") .toString(); .append("Package name=").append(packageName).append(", ") .append("Paused="); } public int getCode() { Loading @@ -57,8 +58,16 @@ public class AppOpItem { return mTimeStarted; } public void setSilenced(boolean silenced) { mSilenced = silenced; } public boolean isSilenced() { return mSilenced; } @Override public String toString() { return mState; return mState.append(mSilenced).append(")").toString(); } }
packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +85 −7 Original line number Diff line number Diff line Loading @@ -19,12 +19,15 @@ package com.android.systemui.appops; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.AudioRecordingConfiguration; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import androidx.annotation.WorkerThread; Loading Loading @@ -62,6 +65,7 @@ public class AppOpsControllerImpl implements AppOpsController, private static final boolean DEBUG = false; private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); Loading @@ -72,6 +76,9 @@ public class AppOpsControllerImpl implements AppOpsController, private final List<AppOpItem> mActiveItems = new ArrayList<>(); @GuardedBy("mNotedItems") private final List<AppOpItem> mNotedItems = new ArrayList<>(); @GuardedBy("mActiveItems") private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid = new SparseArray<>(); protected static final int[] OPS = new int[] { AppOpsManager.OP_CAMERA, Loading @@ -86,7 +93,8 @@ public class AppOpsControllerImpl implements AppOpsController, Context context, @Background Looper bgLooper, DumpManager dumpManager, PermissionFlagsCache cache PermissionFlagsCache cache, AudioManager audioManager ) { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mFlagsCache = cache; Loading @@ -95,6 +103,7 @@ public class AppOpsControllerImpl implements AppOpsController, for (int i = 0; i < numOps; i++) { mCallbacksByCode.put(OPS[i], new ArraySet<>()); } mAudioManager = audioManager; dumpManager.registerDumpable(TAG, this); } Loading @@ -109,12 +118,19 @@ public class AppOpsControllerImpl implements AppOpsController, if (listening) { mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); } else { mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); mBGHandler.removeCallbacksAndMessages(null); // null removes all synchronized (mActiveItems) { mActiveItems.clear(); mRecordingsByUid.clear(); } synchronized (mNotedItems) { mNotedItems.clear(); Loading Loading @@ -187,9 +203,12 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName); if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); if (code == AppOpsManager.OP_RECORD_AUDIO) { item.setSilenced(isAnyRecordingPausedLocked(uid)); } mActiveItems.add(item); if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); return true; return !item.isSilenced(); } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); Loading @@ -213,7 +232,7 @@ public class AppOpsControllerImpl implements AppOpsController, active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } if (!active) { notifySuscribers(code, uid, packageName, false); notifySuscribersWorker(code, uid, packageName, false); } } Loading Loading @@ -321,7 +340,7 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) && isUserVisible(item)) { && isUserVisible(item) && !item.isSilenced()) { list.add(item); } } Loading @@ -340,6 +359,10 @@ public class AppOpsControllerImpl implements AppOpsController, return list; } private void notifySuscribers(int code, int uid, String packageName, boolean active) { mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active)); } @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { if (DEBUG) { Loading @@ -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 // early removal) if (!alsoNoted) { mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active)); notifySuscribers(code, uid, packageName, active); } } Loading @@ -375,11 +398,11 @@ public class AppOpsControllerImpl implements AppOpsController, alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } 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 (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { Loading @@ -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 { H(Looper looper) { super(looper); Loading
packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +106 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ 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.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.times; import static org.mockito.Mockito.verify; Loading @@ -34,6 +37,8 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.AudioRecordingConfiguration; import android.os.Looper; import android.os.UserHandle; import android.testing.AndroidTestingRunner; Loading @@ -47,9 +52,11 @@ import com.android.systemui.dump.DumpManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.List; @SmallTest Loading @@ -73,6 +80,12 @@ public class AppOpsControllerTest extends SysuiTestCase { private PermissionFlagsCache mFlagsCache; @Mock 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 TestableLooper mTestableLooper; Loading @@ -94,11 +107,20 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mFlagsCache.getPermissionFlags(anyString(), anyString(), 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( mContext, mTestableLooper.getLooper(), mDumpManager, mFlagsCache mFlagsCache, mAudioManager ); } Loading Loading @@ -363,6 +385,89 @@ public class AppOpsControllerTest extends SysuiTestCase { 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 { TestHandler(Looper looper) { mController.super(looper); Loading