diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 16ee6a745e1d9..4b93363b5b904 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2429,6 +2429,12 @@ android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid) return (jint)nativeToJavaStatus(status); } +static jint android_media_AudioSystem_setHotwordDetectionServiceUid(JNIEnv *env, jobject thiz, + jint uid) { + status_t status = AudioSystem::setHotwordDetectionServiceUid(uid); + return (jint)nativeToJavaStatus(status); +} + static jint android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) { std::vector nativeUidsVector; @@ -2799,6 +2805,8 @@ static const JNINativeMethod gMethods[] = {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled}, {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid}, + {"setHotwordDetectionServiceUid", "(I)I", + (void *)android_media_AudioSystem_setHotwordDetectionServiceUid}, {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported}, diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java index c827932194aef..cb887f2d523d2 100644 --- a/media/java/android/media/AudioManagerInternal.java +++ b/media/java/android/media/AudioManagerInternal.java @@ -38,6 +38,17 @@ public abstract class AudioManagerInternal { public abstract void updateRingerModeAffectedStreamsInternal(); + /** + * Notify the UID of the currently active {@link android.service.voice.HotwordDetectionService}. + * + *

The caller is expected to take care of any performance implications, e.g. by using a + * background thread to call this method.

+ * + * @param uid UID of the currently active service or {@link android.os.Process#INVALID_UID} if + * none. + */ + public abstract void setHotwordDetectionServiceUid(int uid); + public abstract void setAccessibilityServiceUids(IntArray uids); /** diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 8012f03d84b37..69d1889d57628 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1768,6 +1768,13 @@ public class AudioSystem */ public static native int setAssistantUid(int uid); + /** + * Communicate UID of the current {@link android.service.voice.HotwordDetectionService} to audio + * policy service. + * @hide + */ + public static native int setHotwordDetectionServiceUid(int uid); + /** * @hide * Communicate UIDs of active accessibility services to audio policy service. diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f7d091498456f..34e2578f7855f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -736,6 +736,11 @@ public class AudioService extends IAudioService.Stub private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT; private long mLoweredFromNormalToVibrateTime; + // Uid of the active hotword detection service to check if caller is the one or not. + @GuardedBy("mHotwordDetectionServiceUidLock") + private int mHotwordDetectionServiceUid = android.os.Process.INVALID_UID; + private final Object mHotwordDetectionServiceUidLock = new Object(); + // Array of Uids of valid accessibility services to check if caller is one of them private final Object mAccessibilityServiceUidsLock = new Object(); @GuardedBy("mAccessibilityServiceUidsLock") @@ -1337,6 +1342,9 @@ public class AudioService extends IAudioService.Stub updateAssistantUId(true); AudioSystem.setRttEnabled(mRttEnabled); } + synchronized (mHotwordDetectionServiceUidLock) { + AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid); + } synchronized (mAccessibilityServiceUidsLock) { AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); } @@ -9107,6 +9115,16 @@ public class AudioService extends IAudioService.Stub } } + @Override + public void setHotwordDetectionServiceUid(int uid) { + synchronized (mHotwordDetectionServiceUidLock) { + if (mHotwordDetectionServiceUid != uid) { + mHotwordDetectionServiceUid = uid; + AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid); + } + } + } + @Override public void setAccessibilityServiceUids(IntArray uids) { synchronized (mAccessibilityServiceUidsLock) { diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 973fbd2bba179..6d567807f357f 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -366,6 +366,14 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { return AudioSystem.muteMicrophone(on); } + /** + * Same as {@link AudioSystem#setHotwordDetectionServiceUid(int)} + * Communicate UID of current HotwordDetectionService to audio policy service. + */ + public int setHotwordDetectionServiceUid(int uid) { + return AudioSystem.setHotwordDetectionServiceUid(uid); + } + /** * Same as {@link AudioSystem#setCurrentImeUid(int)} * Communicate UID of current InputMethodService to audio policy service. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 965f126000fd8..734172fc1549b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -36,6 +36,7 @@ import android.content.PermissionChecker; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; +import android.media.AudioManagerInternal; import android.media.permission.Identity; import android.media.permission.PermissionUtil; import android.os.Binder; @@ -44,6 +45,7 @@ import android.os.IBinder; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SharedMemory; @@ -275,6 +277,7 @@ final class HotwordDetectionConnection { LocalServices.getService(PermissionManagerServiceInternal.class) .setHotwordDetectionServiceProvider(null); mIdentity = null; + updateServiceUidForAudioPolicy(Process.INVALID_UID); } mCancellationTaskFuture.cancel(/* may interrupt */ true); if (mAudioFlinger != null) { @@ -893,6 +896,8 @@ final class HotwordDetectionConnection { connection.run(service -> service.ping(new IRemoteCallback.Stub() { @Override public void sendResult(Bundle bundle) throws RemoteException { + // TODO: Exit if the service has been unbound already (though there's a very low + // chance this happens). if (DEBUG) { Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid()); } @@ -902,10 +907,21 @@ final class HotwordDetectionConnection { LocalServices.getService(PermissionManagerServiceInternal.class) .setHotwordDetectionServiceProvider(() -> uid); mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid); + updateServiceUidForAudioPolicy(uid); } })); } + private void updateServiceUidForAudioPolicy(int uid) { + mScheduledExecutorService.execute(() -> { + final AudioManagerInternal audioManager = + LocalServices.getService(AudioManagerInternal.class); + if (audioManager != null) { + audioManager.setHotwordDetectionServiceUid(uid); + } + }); + } + private static void bestEffortClose(Closeable closeable) { try { closeable.close();