Loading core/api/system-current.txt +2 −0 Original line number Original line Diff line number Diff line Loading @@ -5875,9 +5875,11 @@ package android.media.audiopolicy { method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); method public int getFocusDuckingBehavior(); method public int getFocusDuckingBehavior(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioFocusInfo> getFocusStack(); method public int getStatus(); method public int getStatus(); method public boolean removeUidDeviceAffinity(int); method public boolean removeUidDeviceAffinity(int); method public boolean removeUserIdDeviceAffinity(int); method public boolean removeUserIdDeviceAffinity(int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull android.media.AudioFocusInfo) throws java.lang.IllegalStateException; method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public void setRegistration(String); method public void setRegistration(String); method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); media/java/android/media/IAudioService.aidl +4 −0 Original line number Original line Diff line number Diff line Loading @@ -460,4 +460,8 @@ interface IAudioService { boolean register); boolean register); void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected); void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected); List<AudioFocusInfo> getFocusStack(); boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb); } } media/java/android/media/audiopolicy/AudioPolicy.java +41 −1 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media.audiopolicy; import android.annotation.IntDef; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.annotation.UserIdInt; Loading Loading @@ -230,7 +231,7 @@ public class AudioPolicy { * If set to {@code true}, it is mandatory to set an * If set to {@code true}, it is mandatory to set an * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build * an {@code AudioPolicy} instance. * an {@code AudioPolicy} instance. * @param enforce true if the policy will govern audio focus decisions. * @param isFocusPolicy true if the policy will govern audio focus decisions. * @return the same Builder instance. * @return the same Builder instance. */ */ @NonNull @NonNull Loading Loading @@ -722,6 +723,45 @@ public class AudioPolicy { } } } } /** * Returns the list of entries in the focus stack. * The list is ordered with increasing rank of focus ownership, where the last entry is at the * top of the focus stack and is the current focus owner. * @return the ordered list of focus owners * @see AudioManager#registerAudioPolicy(AudioPolicy) */ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public @NonNull List<AudioFocusInfo> getFocusStack() { try { return getService().getFocusStack(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus * loss, and for it to exit the focus stack (its focus listener will not be invoked after that). * This operation is only valid for a registered policy (with * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}. * @param focusLoser the stack entry that is exiting the stack through a focus loss * @return false if the focusLoser wasn't found in the stack, true otherwise * @throws IllegalStateException if used on an unregistered policy, or a registered policy * with no {@link AudioPolicyFocusListener} set * @see AudioManager#registerAudioPolicy(AudioPolicy) * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener) */ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException { Objects.requireNonNull(focusLoser); try { return getService().sendFocusLoss(focusLoser, cb()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** /** * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. * Audio buffers recorded through the created instance will contain the mix of the audio * Audio buffers recorded through the created instance will contain the mix of the audio Loading services/core/java/com/android/server/audio/AudioService.java +21 −0 Original line number Original line Diff line number Diff line Loading @@ -10142,6 +10142,27 @@ public class AudioService extends IAudioService.Stub return AudioManager.SUCCESS; return AudioManager.SUCCESS; } } /** @see AudioPolicy#getFocusStack() */ public List<AudioFocusInfo> getFocusStack() { enforceModifyAudioRoutingPermission(); return mMediaFocusControl.getFocusStack(); } /** @see AudioPolicy#sendFocusLoss */ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser, @NonNull IAudioPolicyCallback apcb) { Objects.requireNonNull(focusLoser); Objects.requireNonNull(apcb); enforceModifyAudioRoutingPermission(); if (!mAudioPolicies.containsKey(apcb.asBinder())) { throw new IllegalStateException("Only registered AudioPolicy can change focus"); } if (!mAudioPolicies.get(apcb.asBinder()).mHasFocusListener) { throw new IllegalStateException("AudioPolicy must have focus listener to change focus"); } return mMediaFocusControl.sendFocusLoss(focusLoser); } /** see AudioManager.hasRegisteredDynamicPolicy */ /** see AudioManager.hasRegisteredDynamicPolicy */ public boolean hasRegisteredDynamicPolicy() { public boolean hasRegisteredDynamicPolicy() { synchronized (mAudioPolicies) { synchronized (mAudioPolicies) { Loading services/core/java/com/android/server/audio/MediaFocusControl.java +46 −0 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; import android.media.IAudioFocusDispatcher; import android.media.MediaMetrics; import android.media.MediaMetrics; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.IAudioPolicyCallback; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Binder; import android.os.Build; import android.os.Build; Loading Loading @@ -221,6 +222,51 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } } } /** * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo * instead of FocusRequester instances) * @return a SystemApi-friendly version of the focus stack, in the same order (last entry * is top of focus stack, i.e. latest focus owner) * @see AudioPolicy#getFocusStack() */ @NonNull List<AudioFocusInfo> getFocusStack() { synchronized (mAudioFocusLock) { final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size()); for (FocusRequester fr : mFocusStack) { stack.add(fr.toAudioFocusInfo()); } return stack; } } /** * Send AUDIOFOCUS_LOSS to a specific stack entry. * Note this method is supporting an external API, and is restricted to LOSS in order to * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus) * @param focusLoser the stack entry that is exiting the stack through a focus loss * @return false if the focusLoser wasn't found in the stack, true otherwise * @see AudioPolicy#sendFocusLoss(AudioFocusInfo) */ boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) { synchronized (mAudioFocusLock) { FocusRequester loserToRemove = null; for (FocusRequester fr : mFocusStack) { if (fr.getClientId().equals(focusLoser.getClientId())) { fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false /*forceDuck*/); loserToRemove = fr; break; } } if (loserToRemove != null) { mFocusStack.remove(loserToRemove); loserToRemove.release(); return true; } } return false; } @GuardedBy("mAudioFocusLock") @GuardedBy("mAudioFocusLock") private void notifyTopOfAudioFocusStack() { private void notifyTopOfAudioFocusStack() { // notify the top of the stack it gained focus // notify the top of the stack it gained focus Loading Loading
core/api/system-current.txt +2 −0 Original line number Original line Diff line number Diff line Loading @@ -5875,9 +5875,11 @@ package android.media.audiopolicy { method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); method public int getFocusDuckingBehavior(); method public int getFocusDuckingBehavior(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioFocusInfo> getFocusStack(); method public int getStatus(); method public int getStatus(); method public boolean removeUidDeviceAffinity(int); method public boolean removeUidDeviceAffinity(int); method public boolean removeUserIdDeviceAffinity(int); method public boolean removeUserIdDeviceAffinity(int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull android.media.AudioFocusInfo) throws java.lang.IllegalStateException; method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public void setRegistration(String); method public void setRegistration(String); method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
media/java/android/media/IAudioService.aidl +4 −0 Original line number Original line Diff line number Diff line Loading @@ -460,4 +460,8 @@ interface IAudioService { boolean register); boolean register); void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected); void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected); List<AudioFocusInfo> getFocusStack(); boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb); } }
media/java/android/media/audiopolicy/AudioPolicy.java +41 −1 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media.audiopolicy; import android.annotation.IntDef; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.annotation.UserIdInt; Loading Loading @@ -230,7 +231,7 @@ public class AudioPolicy { * If set to {@code true}, it is mandatory to set an * If set to {@code true}, it is mandatory to set an * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build * an {@code AudioPolicy} instance. * an {@code AudioPolicy} instance. * @param enforce true if the policy will govern audio focus decisions. * @param isFocusPolicy true if the policy will govern audio focus decisions. * @return the same Builder instance. * @return the same Builder instance. */ */ @NonNull @NonNull Loading Loading @@ -722,6 +723,45 @@ public class AudioPolicy { } } } } /** * Returns the list of entries in the focus stack. * The list is ordered with increasing rank of focus ownership, where the last entry is at the * top of the focus stack and is the current focus owner. * @return the ordered list of focus owners * @see AudioManager#registerAudioPolicy(AudioPolicy) */ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public @NonNull List<AudioFocusInfo> getFocusStack() { try { return getService().getFocusStack(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus * loss, and for it to exit the focus stack (its focus listener will not be invoked after that). * This operation is only valid for a registered policy (with * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}. * @param focusLoser the stack entry that is exiting the stack through a focus loss * @return false if the focusLoser wasn't found in the stack, true otherwise * @throws IllegalStateException if used on an unregistered policy, or a registered policy * with no {@link AudioPolicyFocusListener} set * @see AudioManager#registerAudioPolicy(AudioPolicy) * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener) */ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException { Objects.requireNonNull(focusLoser); try { return getService().sendFocusLoss(focusLoser, cb()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** /** * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. * Audio buffers recorded through the created instance will contain the mix of the audio * Audio buffers recorded through the created instance will contain the mix of the audio Loading
services/core/java/com/android/server/audio/AudioService.java +21 −0 Original line number Original line Diff line number Diff line Loading @@ -10142,6 +10142,27 @@ public class AudioService extends IAudioService.Stub return AudioManager.SUCCESS; return AudioManager.SUCCESS; } } /** @see AudioPolicy#getFocusStack() */ public List<AudioFocusInfo> getFocusStack() { enforceModifyAudioRoutingPermission(); return mMediaFocusControl.getFocusStack(); } /** @see AudioPolicy#sendFocusLoss */ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser, @NonNull IAudioPolicyCallback apcb) { Objects.requireNonNull(focusLoser); Objects.requireNonNull(apcb); enforceModifyAudioRoutingPermission(); if (!mAudioPolicies.containsKey(apcb.asBinder())) { throw new IllegalStateException("Only registered AudioPolicy can change focus"); } if (!mAudioPolicies.get(apcb.asBinder()).mHasFocusListener) { throw new IllegalStateException("AudioPolicy must have focus listener to change focus"); } return mMediaFocusControl.sendFocusLoss(focusLoser); } /** see AudioManager.hasRegisteredDynamicPolicy */ /** see AudioManager.hasRegisteredDynamicPolicy */ public boolean hasRegisteredDynamicPolicy() { public boolean hasRegisteredDynamicPolicy() { synchronized (mAudioPolicies) { synchronized (mAudioPolicies) { Loading
services/core/java/com/android/server/audio/MediaFocusControl.java +46 −0 Original line number Original line Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; import android.media.IAudioFocusDispatcher; import android.media.MediaMetrics; import android.media.MediaMetrics; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.IAudioPolicyCallback; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Binder; import android.os.Build; import android.os.Build; Loading Loading @@ -221,6 +222,51 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } } } /** * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo * instead of FocusRequester instances) * @return a SystemApi-friendly version of the focus stack, in the same order (last entry * is top of focus stack, i.e. latest focus owner) * @see AudioPolicy#getFocusStack() */ @NonNull List<AudioFocusInfo> getFocusStack() { synchronized (mAudioFocusLock) { final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size()); for (FocusRequester fr : mFocusStack) { stack.add(fr.toAudioFocusInfo()); } return stack; } } /** * Send AUDIOFOCUS_LOSS to a specific stack entry. * Note this method is supporting an external API, and is restricted to LOSS in order to * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus) * @param focusLoser the stack entry that is exiting the stack through a focus loss * @return false if the focusLoser wasn't found in the stack, true otherwise * @see AudioPolicy#sendFocusLoss(AudioFocusInfo) */ boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) { synchronized (mAudioFocusLock) { FocusRequester loserToRemove = null; for (FocusRequester fr : mFocusStack) { if (fr.getClientId().equals(focusLoser.getClientId())) { fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false /*forceDuck*/); loserToRemove = fr; break; } } if (loserToRemove != null) { mFocusStack.remove(loserToRemove); loserToRemove.release(); return true; } } return false; } @GuardedBy("mAudioFocusLock") @GuardedBy("mAudioFocusLock") private void notifyTopOfAudioFocusStack() { private void notifyTopOfAudioFocusStack() { // notify the top of the stack it gained focus // notify the top of the stack it gained focus Loading