Merge "Ambient Activation p2.3" into main
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 340bf3f..eec16e6 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -34,7 +34,6 @@
 import android.permission.PermissionManager;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseArray;
 
 import androidx.annotation.WorkerThread;
@@ -122,8 +121,7 @@
             AppOpsManager.OP_SYSTEM_ALERT_WINDOW
     };
 
-
-   protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS);
+    protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS);
 
     /**
      * @param opArrays the given op arrays.
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 b23f7f2d..b100336 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -21,6 +21,8 @@
 
 import static com.android.systemui.appops.AppOpsControllerImpl.OPS_MIC;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertFalse;
 
 import static org.junit.Assert.assertEquals;
@@ -66,6 +68,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -543,69 +546,114 @@
 
     @Test
     public void testAudioFilteredWhenMicDisabled() {
-        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
-                mCallback);
+        int micOp = AppOpsManager.OP_RECORD_AUDIO;
+        int nonMicOp = AppOpsManager.OP_CAMERA;
+
+        // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
+        // verify the micOp is the only active op returned.
+        mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
-                AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+                AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
-        List<AppOpItem> list = mController.getActiveAppOps();
-        assertEquals(1, list.size());
-        assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
-        assertFalse(list.get(0).isDisabled());
+        verifySingleActiveOps(micOp);
 
-        // Add a camera op, and disable the microphone. The camera op should be the only op returned
+        // Add a non-mic op, and disable the microphone. The camera op should be the only active op
+        // returned.
         mController.onSensorBlockedChanged(MICROPHONE, true);
-        mController.onOpActiveChanged(
-                AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER,
+                TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
-        list = mController.getActiveAppOps();
-        assertEquals(1, list.size());
-        assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+        verifySingleActiveOps(nonMicOp);
 
-
-        // Re enable the microphone, and verify the op returns
+        // Re-enable the microphone, and verify the active op returns.
         mController.onSensorBlockedChanged(MICROPHONE, false);
         mTestableLooper.processAllMessages();
-
-        list = mController.getActiveAppOps();
-        assertEquals(2, list.size());
-        int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0;
-        assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode());
+        verifyActiveOps(micOp, nonMicOp);
     }
 
     @Test
     public void testPhoneCallMicrophoneFilteredWhenMicDisabled() {
-        mController.addCallback(
-                new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_CAMERA},
-                mCallback);
+        int micOp = AppOpsManager.OP_PHONE_CALL_MICROPHONE;
+        int nonMicOp = AppOpsManager.OP_CAMERA;
+
+        // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
+        // verify the micOp is the only active op returned.
+        mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
         mTestableLooper.processAllMessages();
         mController.onOpActiveChanged(
-                AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+                AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
-        List<AppOpItem> list = mController.getActiveAppOps();
-        assertEquals(1, list.size());
-        assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(0).getCode());
-        assertFalse(list.get(0).isDisabled());
+        verifySingleActiveOps(micOp);
 
-        // Add a camera op, and disable the microphone. The camera op should be the only op returned
+        // Add a non-mic op, and disable the microphone. The camera op should be the only active op
+        // returned.
         mController.onSensorBlockedChanged(MICROPHONE, true);
-        mController.onOpActiveChanged(
-                AppOpsManager.OPSTR_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER,
+                TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
-        list = mController.getActiveAppOps();
-        assertEquals(1, list.size());
-        assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+        verifySingleActiveOps(nonMicOp);
 
-
-        // Re enable the microphone, and verify the op returns
+        // Re-enable the microphone, and verify the active op returns.
         mController.onSensorBlockedChanged(MICROPHONE, false);
         mTestableLooper.processAllMessages();
+        verifyActiveOps(micOp, nonMicOp);
+    }
 
-        list = mController.getActiveAppOps();
-        assertEquals(2, list.size());
-        int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0;
-        assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(micIdx).getCode());
+    @Test
+    public void testAmbientTriggerMicrophoneFilteredWhenMicDisabled() {
+        int micOp = AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+        int nonMicOp = AppOpsManager.OP_CAMERA;
+
+        // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
+        // verify the micOp is the only active op returned.
+        mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        verifySingleActiveOps(micOp);
+
+        // Add a non-mic op, and disable the microphone. The camera op should be the only active op
+        // returned.
+        mController.onSensorBlockedChanged(MICROPHONE, true);
+        mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER,
+                TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        verifySingleActiveOps(nonMicOp);
+
+        // Re-enable the microphone, and verify the active op returns.
+        mController.onSensorBlockedChanged(MICROPHONE, false);
+        mTestableLooper.processAllMessages();
+        verifyActiveOps(micOp, nonMicOp);
+    }
+
+    @Test
+    public void testSandboxTriggerMicrophoneFilteredWhenMicDisabled() {
+        int micOp = AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
+        int nonMicOp = AppOpsManager.OP_CAMERA;
+
+        // Add callbacks for the micOp and nonMicOp, called for the micOp active state change,
+        // verify the micOp is the only active op returned.
+        mController.addCallback(new int[]{micOp, nonMicOp}, mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.opToPublicName(micOp), TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        verifySingleActiveOps(micOp);
+
+        // Add a non-mic op, and disable the microphone. The camera op should be the only active op
+        // returned.
+        mController.onSensorBlockedChanged(MICROPHONE, true);
+        mController.onOpActiveChanged(AppOpsManager.opToPublicName(nonMicOp), TEST_UID_OTHER,
+                TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        verifySingleActiveOps(nonMicOp);
+
+        // Re-enable the microphone, and verify the active op returns.
+        mController.onSensorBlockedChanged(MICROPHONE, false);
+        mTestableLooper.processAllMessages();
+        verifyActiveOps(micOp, nonMicOp);
     }
 
     @Test
@@ -708,6 +756,22 @@
                 micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
     }
 
+    private void verifySingleActiveOps(int op) {
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(op, list.get(0).getCode());
+        assertFalse(list.get(0).isDisabled());
+    }
+
+    private void verifyActiveOps(int micOp, int nonMicOp) {
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+        List<Integer> codes = Arrays.asList(list.get(0).getCode(), list.get(1).getCode());
+        assertThat(codes).containsExactly(micOp, nonMicOp);
+        assertFalse(list.get(0).isDisabled());
+        assertFalse(list.get(1).isDisabled());
+    }
+
     private class TestHandler extends AppOpsControllerImpl.H {
         TestHandler(Looper looper) {
             mController.super(looper);
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 76126df..8780991 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -27,6 +27,7 @@
 import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
 import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
 import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
+import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -1125,6 +1126,8 @@
                 case MICROPHONE:
                     mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled,
                             mAppOpsRestrictionToken);
+                    mAppOpsManagerInternal.setGlobalRestriction(
+                                OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, enabled, mAppOpsRestrictionToken);
                     mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled,
                             mAppOpsRestrictionToken);
                     // We don't show the dialog for RECEIVE_SOUNDTRIGGER_AUDIO, but still want to