Observe mic mute changes, consider recordingss paused when muted

Listen for the microphone mute state to change, and when it changes,
update the paused status of all audio recordings. If the mic is muted, then
every stream is considered paused.

Fixes: 168553482
Test: Get a call, then screen it. The "mic in use" icon should be
replaced by the mute icon

Change-Id: I5fad1f359c7009c924e8f7cce9c09f8ea299522a
Merged-In: I5fad1f359c7009c924e8f7cce9c09f8ea299522a
(cherry picked from commit f20a6afcc3a1578b6cc92237c4f85cfc11c39ee2)
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 64b35ca..2b9514f 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,13 @@
 
 package com.android.systemui.appops;
 
+import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
+
 import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.location.LocationManager;
 import android.media.AudioManager;
@@ -35,6 +40,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 
@@ -54,7 +60,7 @@
  * NotificationPresenter to be displayed to the user.
  */
 @Singleton
-public class AppOpsControllerImpl implements AppOpsController,
+public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
         AppOpsManager.OnOpActiveChangedInternalListener,
         AppOpsManager.OnOpNotedListener, Dumpable {
 
@@ -65,6 +71,7 @@
     private static final String TAG = "AppOpsControllerImpl";
     private static final boolean DEBUG = false;
 
+    private final BroadcastDispatcher mDispatcher;
     private final AppOpsManager mAppOps;
     private final AudioManager mAudioManager;
     private final LocationManager mLocationManager;
@@ -79,6 +86,7 @@
     private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
     private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
+    private boolean mMicMuted;
 
     @GuardedBy("mActiveItems")
     private final List<AppOpItem> mActiveItems = new ArrayList<>();
@@ -104,8 +112,10 @@
             @Background Looper bgLooper,
             DumpManager dumpManager,
             PermissionFlagsCache cache,
-            AudioManager audioManager
+            AudioManager audioManager,
+            BroadcastDispatcher dispatcher
     ) {
+        mDispatcher = dispatcher;
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mFlagsCache = cache;
         mBGHandler = new H(bgLooper);
@@ -114,6 +124,7 @@
             mCallbacksByCode.put(OPS[i], new ArraySet<>());
         }
         mAudioManager = audioManager;
+        mMicMuted = audioManager.isMicrophoneMute();
         mLocationManager = context.getSystemService(LocationManager.class);
         dumpManager.registerDumpable(TAG, this);
     }
@@ -132,6 +143,8 @@
             mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
             mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
                     mAudioManager.getActiveRecordingConfigurations()));
+            mDispatcher.registerReceiverWithHandler(this,
+                    new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
 
         } else {
             mAppOps.stopWatchingActive(this);
@@ -139,6 +152,7 @@
             mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
 
             mBGHandler.removeCallbacksAndMessages(null); // null removes all
+            mDispatcher.unregisterReceiver(this);
             synchronized (mActiveItems) {
                 mActiveItems.clear();
                 mRecordingsByUid.clear();
@@ -466,6 +480,9 @@
     }
 
     private boolean isAnyRecordingPausedLocked(int uid) {
+        if (mMicMuted) {
+            return true;
+        }
         List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
         if (configs == null) return false;
         int configsNum = configs.size();
@@ -520,6 +537,12 @@
         }
     };
 
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mMicMuted = mAudioManager.isMicrophoneMute();
+        updateRecordingPausedStatus();
+    }
+
     protected class H extends Handler {
         H(Looper looper) {
             super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 8f082c1..ade3290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -47,6 +47,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 
 import org.junit.Before;
@@ -82,6 +83,8 @@
     private PackageManager mPackageManager;
     @Mock(stubOnly = true)
     private AudioManager mAudioManager;
+    @Mock()
+    private BroadcastDispatcher mDispatcher;
     @Mock(stubOnly = true)
     private AudioManager.AudioRecordingCallback mRecordingCallback;
     @Mock(stubOnly = true)
@@ -120,7 +123,8 @@
                 mTestableLooper.getLooper(),
                 mDumpManager,
                 mFlagsCache,
-                mAudioManager
+                mAudioManager,
+                mDispatcher
         );
     }
 
@@ -128,12 +132,14 @@
     public void testOnlyListenForFewOps() {
         mController.setListening(true);
         verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
+        verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
     }
 
     @Test
     public void testStopListening() {
         mController.setListening(false);
         verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
+        verify(mDispatcher, times(1)).unregisterReceiver(mController);
     }
 
     @Test