Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47ce0353 authored by Faye Yan's avatar Faye Yan Committed by Android (Google) Code Review
Browse files

Merge "Ambient Activation p2.2" into main

parents 7eb15d04 6a57fba3
Loading
Loading
Loading
Loading
+57 −11
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;

import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -33,6 +34,7 @@ import android.os.UserHandle;
import android.permission.PermissionManager;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;

import androidx.annotation.WorkerThread;
@@ -96,19 +98,58 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
    private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid =
            new SparseArray<>();

    protected static final int[] OPS = new int[] {
            AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
            AppOpsManager.OP_CAMERA,
            AppOpsManager.OP_PHONE_CALL_CAMERA,
            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
    @VisibleForTesting
    protected static final int[] OPS_MIC = new int[] {
            AppOpsManager.OP_RECORD_AUDIO,
            AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
            AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
            AppOpsManager.OP_PHONE_CALL_MICROPHONE,
            AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
            AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
    };

    protected static final int[] OPS_CAMERA = new int[] {
            AppOpsManager.OP_CAMERA,
            AppOpsManager.OP_PHONE_CALL_CAMERA
    };

    protected static final int[] OPS_LOC = new int[] {
            AppOpsManager.OP_FINE_LOCATION,
            AppOpsManager.OP_COARSE_LOCATION,
            AppOpsManager.OP_FINE_LOCATION
            AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION
    };

    protected static final int[] OPS_OTHERS = new int[] {
            AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
            AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
    };


   protected static final int[] OPS = concatOps(OPS_MIC, OPS_CAMERA, OPS_LOC, OPS_OTHERS);

    /**
     * @param opArrays the given op arrays.
     * @return the concatenations of the given op arrays. Null arrays are treated as empty.
     */
    private static int[] concatOps(@Nullable int[]...opArrays) {
        if (opArrays == null) {
            return new int[0];
        }
        int totalLength = 0;
        for (int[] opArray : opArrays) {
            if (opArray == null || opArray.length == 0) {
                continue;
            }
            totalLength += opArray.length;
        }
        final int[] concatOps = new int[totalLength];
        int index = 0;
        for (int[] opArray : opArrays) {
            if (opArray == null || opArray.length == 0) continue;
            System.arraycopy(opArray, 0, concatOps, index, opArray.length);
            index += opArray.length;
        }
        return concatOps;
    }

    @Inject
    public AppOpsControllerImpl(
            Context context,
@@ -533,12 +574,17 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
    }

    private boolean isOpCamera(int op) {
        return op == AppOpsManager.OP_CAMERA || op == AppOpsManager.OP_PHONE_CALL_CAMERA;
        for (int i = 0; i < OPS_CAMERA.length; i++) {
            if (op == OPS_CAMERA[i]) return true;
        }
        return false;
    }

    private boolean isOpMicrophone(int op) {
        return op == AppOpsManager.OP_RECORD_AUDIO || op == AppOpsManager.OP_PHONE_CALL_MICROPHONE
                || op == AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
        for (int i = 0; i < OPS_MIC.length; i++) {
            if (op == OPS_MIC[i]) return true;
        }
        return false;
    }

    protected class H extends Handler {
+7 −3
Original line number Diff line number Diff line
@@ -53,11 +53,14 @@ class AppOpsPrivacyItemMonitor @Inject constructor(

    @VisibleForTesting
    companion object {
        val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO,
        val OPS_MIC_CAMERA = intArrayOf(
                AppOpsManager.OP_CAMERA,
                AppOpsManager.OP_PHONE_CALL_CAMERA,
                AppOpsManager.OP_RECORD_AUDIO,
                AppOpsManager.OP_PHONE_CALL_MICROPHONE,
                AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
                AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO)
                AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
                AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO)
        val OPS_LOCATION = intArrayOf(
                AppOpsManager.OP_COARSE_LOCATION,
                AppOpsManager.OP_FINE_LOCATION)
@@ -212,6 +215,7 @@ class AppOpsPrivacyItemMonitor @Inject constructor(
            AppOpsManager.OP_PHONE_CALL_MICROPHONE,
            AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
            AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
            AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
            else -> return null
        }
+66 −31
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.systemui.appops;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;

import static com.android.systemui.appops.AppOpsControllerImpl.OPS_MIC;

import static junit.framework.TestCase.assertFalse;

import static org.junit.Assert.assertEquals;
@@ -171,6 +173,28 @@ public class AppOpsControllerTest extends SysuiTestCase {
                TEST_UID, TEST_PACKAGE_NAME, true);
    }


    // Only the app ops in the {@link com.android.systemui.appops.AppOpsControllerImpl.OPS} will be
    // supported by the {@link AppOpsControllerImpl} to add callbacks. The state changes of active
    // app ops will be notified by the callback.
    @Test
    public void addCallback_partialIncludedCode() {
        mController.addCallback(new int[]{AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
                AppOpsManager.OP_FINE_LOCATION}, mCallback);
        mController.onOpActiveChanged(
                AppOpsManager.OPSTR_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
        mController.onOpActiveChanged(
                AppOpsManager.OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, TEST_UID, TEST_PACKAGE_NAME,
                true);
        assertEquals(2, mController.getActiveAppOps().size());

        mTestableLooper.processAllMessages();
        verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
                TEST_UID, TEST_PACKAGE_NAME, true);
        verify(mCallback, never()).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                TEST_UID, TEST_PACKAGE_NAME, true);
    }

    @Test
    public void addCallback_notIncludedCode() {
        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
@@ -504,41 +528,17 @@ public class AppOpsControllerTest extends SysuiTestCase {
    }

    @Test
    public void testUnpausedRecordingSentActive() {
        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
        mTestableLooper.processAllMessages();
        mController.onOpActiveChanged(
                AppOpsManager.OPSTR_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);
    public void testUnPausedRecordingSentActive() {
        for (int i = 0; i < OPS_MIC.length; i++) {
            verifyUnPausedSentActive(OPS_MIC[i]);
        }
    }

    @Test
    public void testAudioPausedSentInactive() {
        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
        mTestableLooper.processAllMessages();
        mController.onOpActiveChanged(
                AppOpsManager.OPSTR_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);
        for (int i = 0; i < OPS_MIC.length; i++) {
            verifyAudioPausedSentInactive(OPS_MIC[i]);
        }
    }

    @Test
@@ -673,6 +673,41 @@ public class AppOpsControllerTest extends SysuiTestCase {
        assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode());
    }

    private void verifyUnPausedSentActive(int micOpCode) {
        mController.addCallback(new int[]{micOpCode}, mCallback);
        mTestableLooper.processAllMessages();
        mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), TEST_UID,
                TEST_PACKAGE_NAME, true);

        mTestableLooper.processAllMessages();
        mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());

        mTestableLooper.processAllMessages();

        verify(mCallback).onActiveStateChanged(micOpCode, TEST_UID, TEST_PACKAGE_NAME, true);
    }

    private void verifyAudioPausedSentInactive(int micOpCode) {
        mController.addCallback(new int[]{micOpCode}, mCallback);
        mTestableLooper.processAllMessages();
        mController.onOpActiveChanged(AppOpsManager.opToPublicName(micOpCode), 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(
                micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
        inOrder.verify(mCallback).onActiveStateChanged(
                micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
    }

    private class TestHandler extends AppOpsControllerImpl.H {
        TestHandler(Looper looper) {
            mController.super(looper);
+10 −0
Original line number Diff line number Diff line
@@ -142,6 +142,16 @@ class AppOpsPrivacyItemMonitorTest : SysuiTestCase() {
        assertEquals(1, appOpsPrivacyItemMonitor.getActivePrivacyItems().size)
    }

    @Test
    fun testVoiceActivationPrivacyItems() {
        doReturn(listOf(AppOpItem(AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, TEST_UID,
                TEST_PACKAGE_NAME, 0)))
                .`when`(appOpsController).getActiveAppOps(anyBoolean())
        val privacyItems = appOpsPrivacyItemMonitor.getActivePrivacyItems()
        assertEquals(1, privacyItems.size)
        assertEquals(PrivacyType.TYPE_MICROPHONE, privacyItems[0].privacyType)
    }

    @Test
    fun testSimilarItemsDifferentTimeStamp() {
        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),