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

Commit 73a6d870 authored by Atneya Nair's avatar Atneya Nair Committed by Android (Google) Code Review
Browse files

Merge "Make AudioService perm provider HDS aware" into main

parents 102703d0 af33533f
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -44,8 +44,9 @@ public abstract class AudioManagerInternal {
     * Add the UID for a new assistant service
     *
     * @param uid UID of the newly available assistants
     * @param owningUid UID of the actual assistant app, if {@code uid} is a isolated proc
     */
    public abstract void addAssistantServiceUid(int uid);
    public abstract void addAssistantServiceUid(int uid, int owningUid);

    /**
     * Remove the UID for an existing assistant service
+86 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.media.permission.INativePermissionController;
@@ -61,6 +62,9 @@ public class AudioServerPermissionProvider {

    static final String[] MONITORED_PERMS = new String[PermissionEnum.ENUM_SIZE];

    static final byte[] HDS_PERMS = new byte[] {PermissionEnum.CAPTURE_AUDIO_HOTWORD,
            PermissionEnum.CAPTURE_AUDIO_OUTPUT, PermissionEnum.RECORD_AUDIO};

    static {
        MONITORED_PERMS[PermissionEnum.RECORD_AUDIO] = RECORD_AUDIO;
        MONITORED_PERMS[PermissionEnum.MODIFY_AUDIO_ROUTING] = MODIFY_AUDIO_ROUTING;
@@ -88,6 +92,7 @@ public class AudioServerPermissionProvider {

    @GuardedBy("mLock")
    private final Map<Integer, Set<String>> mPackageMap;

    // Values are sorted
    @GuardedBy("mLock")
    private final int[][] mPermMap = new int[PermissionEnum.ENUM_SIZE][];
@@ -95,6 +100,9 @@ public class AudioServerPermissionProvider {
    @GuardedBy("mLock")
    private boolean mIsUpdateDeferred = true;

    @GuardedBy("mLock")
    private int mHdsUid = -1;

    /**
     * @param appInfos - PackageState for all apps on the device, used to populate init state
     */
@@ -124,7 +132,7 @@ public class AudioServerPermissionProvider {
            try {
                for (byte i = 0; i < PermissionEnum.ENUM_SIZE; i++) {
                    if (mIsUpdateDeferred) {
                        mPermMap[i] = getUidsHoldingPerm(MONITORED_PERMS[i]);
                        mPermMap[i] = getUidsHoldingPerm(i);
                    }
                    mDest.populatePermissionState(i, mPermMap[i]);
                }
@@ -184,7 +192,7 @@ public class AudioServerPermissionProvider {
            }
            try {
                for (byte i = 0; i < PermissionEnum.ENUM_SIZE; i++) {
                    var newPerms = getUidsHoldingPerm(MONITORED_PERMS[i]);
                    var newPerms = getUidsHoldingPerm(i);
                    if (!Arrays.equals(newPerms, mPermMap[i])) {
                        mPermMap[i] = newPerms;
                        mDest.populatePermissionState(i, newPerms);
@@ -199,6 +207,77 @@ public class AudioServerPermissionProvider {
        }
    }

    public void setIsolatedServiceUid(int uid, int owningUid) {
        synchronized (mLock) {
            if (mHdsUid == uid) return;
            var packageNameSet = mPackageMap.get(owningUid);
            if (packageNameSet == null) return;
            var packageName = packageNameSet.iterator().next();
            onModifyPackageState(uid, packageName, /* isRemove= */ false);
            // permissions
            mHdsUid = uid;
            if (mDest == null) {
                mIsUpdateDeferred = true;
                return;
            }
            try {
                for (byte perm : HDS_PERMS) {
                    int[] newPerms = new int[mPermMap[perm].length + 1];
                    System.arraycopy(mPermMap[perm], 0, newPerms, 0, mPermMap[perm].length);
                    newPerms[newPerms.length - 1] = mHdsUid;
                    Arrays.sort(newPerms);
                    mPermMap[perm] = newPerms;
                    mDest.populatePermissionState(perm, newPerms);
                }
            } catch (RemoteException e) {
                // We will re-init the state when the service comes back up
                mDest = null;
                // We didn't necessarily finish
                mIsUpdateDeferred = true;
            }
        }
    }

    public void clearIsolatedServiceUid(int uid) {
        synchronized (mLock) {
            if (mHdsUid != uid) return;
            var packageNameSet = mPackageMap.get(uid);
            if (packageNameSet == null) return;
            var packageName = packageNameSet.iterator().next();
            onModifyPackageState(uid, packageName, /* isRemove= */ true);
            // permissions
            if (mDest == null) {
                mIsUpdateDeferred = true;
                return;
            }
            try {
                for (byte perm : HDS_PERMS) {
                    int[] newPerms = new int[mPermMap[perm].length - 1];
                    int ind = Arrays.binarySearch(mPermMap[perm], uid);
                    if (ind < 0) continue;
                    System.arraycopy(mPermMap[perm], 0, newPerms, 0, ind);
                    System.arraycopy(mPermMap[perm], ind + 1, newPerms, ind,
                            mPermMap[perm].length - ind - 1);
                    mPermMap[perm] = newPerms;
                    mDest.populatePermissionState(perm, newPerms);
                }
            } catch (RemoteException e) {
                // We will re-init the state when the service comes back up
                mDest = null;
                // We didn't necessarily finish
                mIsUpdateDeferred = true;
            }
            mHdsUid = -1;
        }
    }

    private boolean isSpecialHdsPermission(int perm) {
        for (var hdsPerm : HDS_PERMS) {
            if (perm == hdsPerm) return true;
        }
        return false;
    }

    /** Called when full syncing package state to audioserver. */
    @GuardedBy("mLock")
    private void resetNativePackageState() {
@@ -223,16 +302,19 @@ public class AudioServerPermissionProvider {

    @GuardedBy("mLock")
    /** Return all uids (not app-ids) which currently hold a given permission. Not app-op aware */
    private int[] getUidsHoldingPerm(String perm) {
    private int[] getUidsHoldingPerm(int perm) {
        IntArray acc = new IntArray();
        for (int userId : mUserIdSupplier.get()) {
            for (int appId : mPackageMap.keySet()) {
                int uid = UserHandle.getUid(userId, appId);
                if (mPermissionPredicate.test(uid, perm)) {
                if (mPermissionPredicate.test(uid, MONITORED_PERMS[perm])) {
                    acc.add(uid);
                }
            }
        }
        if (isSpecialHdsPermission(perm) && mHdsUid != -1) {
            acc.add(mHdsUid);
        }
        var unwrapped = acc.toArray();
        Arrays.sort(unwrapped);
        return unwrapped;
+10 −3
Original line number Diff line number Diff line
@@ -11967,8 +11967,9 @@ public class AudioService extends IAudioService.Stub
        var umi = LocalServices.getService(UserManagerInternal.class);
        var pmsi = LocalServices.getService(PermissionManagerServiceInternal.class);
        var provider = new AudioServerPermissionProvider(packageStates,
                (Integer uid, String perm) -> (pmsi.checkUidPermission(uid, perm,
                        Context.DEVICE_ID_DEFAULT) == PackageManager.PERMISSION_GRANTED),
                (Integer uid, String perm) -> ActivityManager.checkComponentPermission(perm, uid,
                        /* owningUid = */ -1, /* exported */true)
                    == PackageManager.PERMISSION_GRANTED,
                () -> umi.getUserIds()
                );
        audioPolicy.registerOnStartTask(() -> {
@@ -12330,13 +12331,19 @@ public class AudioService extends IAudioService.Stub
        }
        @Override
        public void addAssistantServiceUid(int uid) {
        public void addAssistantServiceUid(int uid, int owningUid) {
            if (audioserverPermissions()) {
                mPermissionProvider.setIsolatedServiceUid(uid, owningUid);
            }
            sendMsg(mAudioHandler, MSG_ADD_ASSISTANT_SERVICE_UID, SENDMSG_QUEUE,
                    uid, 0, null, 0);
        }
        @Override
        public void removeAssistantServiceUid(int uid) {
            if (audioserverPermissions()) {
                mPermissionProvider.clearIsolatedServiceUid(uid);
            }
            sendMsg(mAudioHandler, MSG_REMOVE_ASSISTANT_SERVICE_UID, SENDMSG_QUEUE,
                    uid, 0, null, 0);
        }
+51 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.android.media.permission.INativePermissionController;
import com.android.media.permission.PermissionEnum;
import com.android.media.permission.UidPackageState;
import com.android.server.pm.pkg.PackageState;

@@ -352,6 +353,56 @@ public final class AudioServerPermissionProviderTest {
        verify(mMockPc, times(MONITORED_PERMS.length)).populatePermissionState(anyByte(), any());
    }

    @Test
    public void testSpecialHotwordPermissions() throws Exception {
        BiPredicate<Integer, String> customPermPred = mock(BiPredicate.class);
        var initPackageListData =
                List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two);
        // expected state
        // PERM[CAPTURE_AUDIO_HOTWORD]: [10000]
        // PERM[CAPTURE_AUDIO_OUTPUT]: [10001]
        // PERM[RECORD_AUDIO]: [10001]
        // PERM[...]: []
        when(customPermPred.test(
                        eq(10000), eq(MONITORED_PERMS[PermissionEnum.CAPTURE_AUDIO_HOTWORD])))
                .thenReturn(true);
        when(customPermPred.test(
                        eq(10001), eq(MONITORED_PERMS[PermissionEnum.CAPTURE_AUDIO_OUTPUT])))
                .thenReturn(true);
        when(customPermPred.test(eq(10001), eq(MONITORED_PERMS[PermissionEnum.RECORD_AUDIO])))
                .thenReturn(true);
        mPermissionProvider =
                new AudioServerPermissionProvider(
                        initPackageListData, customPermPred, () -> new int[] {0});
        int HDS_UID = 99001;
        mPermissionProvider.onServiceStart(mMockPc);
        clearInvocations(mMockPc);
        mPermissionProvider.setIsolatedServiceUid(HDS_UID, 10000);
        verify(mMockPc)
                .populatePermissionState(
                        eq((byte) PermissionEnum.CAPTURE_AUDIO_HOTWORD),
                        aryEq(new int[] {10000, HDS_UID}));
        verify(mMockPc)
                .populatePermissionState(
                        eq((byte) PermissionEnum.CAPTURE_AUDIO_OUTPUT),
                        aryEq(new int[] {10001, HDS_UID}));
        verify(mMockPc)
                .populatePermissionState(
                        eq((byte) PermissionEnum.RECORD_AUDIO), aryEq(new int[] {10001, HDS_UID}));

        clearInvocations(mMockPc);
        mPermissionProvider.clearIsolatedServiceUid(HDS_UID);
        verify(mMockPc)
                .populatePermissionState(
                        eq((byte) PermissionEnum.CAPTURE_AUDIO_HOTWORD), aryEq(new int[] {10000}));
        verify(mMockPc)
                .populatePermissionState(
                        eq((byte) PermissionEnum.CAPTURE_AUDIO_OUTPUT), aryEq(new int[] {10001}));
        verify(mMockPc)
                .populatePermissionState(
                        eq((byte) PermissionEnum.RECORD_AUDIO), aryEq(new int[] {10001}));
    }

    @Test
    public void testPermissionsPopulated_onChange() throws Exception {
        var initPackageListData =
+10 −16
Original line number Diff line number Diff line
@@ -1165,7 +1165,7 @@ final class HotwordDetectionConnection {
                LocalServices.getService(PermissionManagerServiceInternal.class)
                        .setHotwordDetectionServiceProvider(() -> uid);
                mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid);
                addServiceUidForAudioPolicy(uid);
                addServiceUidForAudioPolicy(uid, mVoiceInteractionServiceUid);
            }
        }));
    }
@@ -1187,23 +1187,17 @@ final class HotwordDetectionConnection {
        });
    }

    private void addServiceUidForAudioPolicy(int uid) {
        mScheduledExecutorService.execute(() -> {
            AudioManagerInternal audioManager =
                    LocalServices.getService(AudioManagerInternal.class);
    private void addServiceUidForAudioPolicy(int isolatedUid, int owningUid) {
        AudioManagerInternal audioManager = LocalServices.getService(AudioManagerInternal.class);
        if (audioManager != null) {
                audioManager.addAssistantServiceUid(uid);
            audioManager.addAssistantServiceUid(isolatedUid, owningUid);
        }
        });
    }

    private void removeServiceUidForAudioPolicy(int uid) {
        mScheduledExecutorService.execute(() -> {
            AudioManagerInternal audioManager =
                    LocalServices.getService(AudioManagerInternal.class);
        AudioManagerInternal audioManager = LocalServices.getService(AudioManagerInternal.class);
        if (audioManager != null) {
            audioManager.removeAssistantServiceUid(uid);
        }
        });
    }
}