diff options
5 files changed, 159 insertions, 24 deletions
| diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java index c2632458435a..fd71f8694503 100644 --- a/media/java/android/media/AudioManagerInternal.java +++ b/media/java/android/media/AudioManagerInternal.java @@ -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 diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java index 14eae8da0417..c5180afcce7d 100644 --- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java +++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java @@ -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; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c89992d6b57c..74d218cba7ab 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -11963,8 +11963,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(() -> { @@ -12326,13 +12327,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);          } diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java index 0f3b0aa72b72..636cbeef27f7 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java @@ -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; @@ -353,6 +354,56 @@ public final class AudioServerPermissionProviderTest {      }      @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 =                  List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index cfcc04b10107..89b98509de34 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -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); -            if (audioManager != null) { -                audioManager.addAssistantServiceUid(uid); -            } -        }); +    private void addServiceUidForAudioPolicy(int isolatedUid, int owningUid) { +        AudioManagerInternal audioManager = LocalServices.getService(AudioManagerInternal.class); +        if (audioManager != null) { +            audioManager.addAssistantServiceUid(isolatedUid, owningUid); +        }      }      private void removeServiceUidForAudioPolicy(int uid) { -        mScheduledExecutorService.execute(() -> { -            AudioManagerInternal audioManager = -                    LocalServices.getService(AudioManagerInternal.class); -            if (audioManager != null) { -                audioManager.removeAssistantServiceUid(uid); -            } -        }); +        AudioManagerInternal audioManager = LocalServices.getService(AudioManagerInternal.class); +        if (audioManager != null) { +            audioManager.removeAssistantServiceUid(uid); +        }      }  } |