From 84e9721fd60e0deeeb08bc8f6f70bea1efeb5d00 Mon Sep 17 00:00:00 2001 From: Jan Sebechlebsky Date: Wed, 21 Dec 2022 13:40:24 +0100 Subject: [PATCH] Bypass audio focus handling for VDM When VirtualDevice is configured with custom device policy for audio, the audio tied to device specific session id is likely rerouted to loopback and streamed to different destination. In this situation it's not desired that the playback on VDM affects playback on the native device and vice-versa. This change modifies AudioManager so the requests for audio focus are ignored for VDM playback. This allows apps to write universal code for focus-aware audio playback which will behave correctly on native and virtual devices. Bug: 261698699 Bug: 249777386 Test: atest AudioFocusTest Change-Id: If48d841cb64b66d7d08e62bcbb9179fab5910aaa --- media/java/android/media/AudioManager.java | 43 ++++++++++++++++------ 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 24c5b41727320..2fb2f36ec9912 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -105,6 +105,7 @@ public class AudioManager { private Context mOriginalContext; private Context mApplicationContext; + private int mOriginalContextDeviceId = DEVICE_ID_DEFAULT; private @Nullable VirtualDeviceManager mVirtualDeviceManager; // Lazy initialized. private long mVolumeKeyUpTime; private static final String TAG = "AudioManager"; @@ -844,6 +845,7 @@ public class AudioManager { } private void setContext(Context context) { + mOriginalContextDeviceId = context.getDeviceId(); mApplicationContext = context.getApplicationContext(); if (mApplicationContext != null) { mOriginalContext = null; @@ -3794,7 +3796,7 @@ public class AudioManager { } /** - * Checks whether this {@link AudioManager} instance is asociated with {@link VirtualDevice} + * Checks whether this {@link AudioManager} instance is associated with {@link VirtualDevice} * configured with custom device policy for audio. If there is such device, request to play * sound effect is forwarded to {@link VirtualDeviceManager}. * @@ -3803,18 +3805,24 @@ public class AudioManager { * false otherwise. */ private boolean delegateSoundEffectToVdm(@SystemSoundEffect int effectType) { - int deviceId = getContext().getDeviceId(); - if (deviceId != DEVICE_ID_DEFAULT) { + if (hasCustomPolicyVirtualDeviceContext()) { VirtualDeviceManager vdm = getVirtualDeviceManager(); - if (vdm != null && vdm.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO) - != DEVICE_POLICY_DEFAULT) { - vdm.playSoundEffect(deviceId, effectType); - return true; - } + vdm.playSoundEffect(mOriginalContextDeviceId, effectType); + return true; } return false; } + private boolean hasCustomPolicyVirtualDeviceContext() { + if (mOriginalContextDeviceId == DEVICE_ID_DEFAULT) { + return false; + } + + VirtualDeviceManager vdm = getVirtualDeviceManager(); + return vdm != null && vdm.getDevicePolicy(mOriginalContextDeviceId, POLICY_TYPE_AUDIO) + != DEVICE_POLICY_DEFAULT; + } + /** * Load Sound effects. * This method must be called when sound effects are enabled. @@ -4513,6 +4521,16 @@ public class AudioManager { throw new IllegalArgumentException( "Illegal null audio policy when locking audio focus"); } + + if (hasCustomPolicyVirtualDeviceContext()) { + // If the focus request was made within context associated with VirtualDevice + // configured with custom device policy for audio, bypass audio service focus handling. + // The custom device policy for audio means that audio associated with this device + // is likely rerouted to VirtualAudioDevice and playback on the VirtualAudioDevice + // shouldn't affect non-virtual audio tracks (and vice versa). + return AUDIOFOCUS_REQUEST_GRANTED; + } + registerAudioFocusRequest(afr); final IAudioService service = getService(); final int status; @@ -4785,16 +4803,19 @@ public class AudioManager { @SuppressLint("RequiresPermission") // no permission enforcement, but only "undoes" what would // have been done by a matching requestAudioFocus public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) { - int status = AUDIOFOCUS_REQUEST_FAILED; + if (hasCustomPolicyVirtualDeviceContext()) { + // If this AudioManager instance is running within VirtualDevice context configured + // with custom device policy for audio, the audio focus handling is bypassed. + return AUDIOFOCUS_REQUEST_GRANTED; + } unregisterAudioFocusRequest(l); final IAudioService service = getService(); try { - status = service.abandonAudioFocus(mAudioFocusDispatcher, + return service.abandonAudioFocus(mAudioFocusDispatcher, getIdForAudioFocusListener(l), aa, getContext().getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return status; } //====================================================================