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

Commit 0428580b authored by Raj Goparaju's avatar Raj Goparaju
Browse files

Support dispatch audio focus with fade manager config

Dispatch of audio focus change from external focus policies
do not consider fade. This results in audio crossover
when transitioning between multiple apps of similar
usage types.

This commit adds new API to be used by external focus
policies to dispatch audio focus change with fade. The
API also supports transient fade manager configuration
that is prioritized over the default or the configuration
set through AudioPolicy.

Bug: 186905459
Bug: 314521818
API-Coverage-Bug: 308666800
Test: atest -c FadeManagerConfigurationUnitTest

Change-Id: Ifda86d4ca618d6926f85b0c9e29f70c436c9618c
parent 3aab2546
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6474,6 +6474,7 @@ package android.media {
    method public void clearAudioServerStateCallback();
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
    method @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public int dispatchAudioFocusChangeWithFade(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy, @NonNull java.util.List<android.media.AudioFocusInfo>, @Nullable android.media.FadeManagerConfiguration);
    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getActiveAssistantServicesUids();
    method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getAssistantServicesUids();
+66 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;

import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -5119,6 +5120,71 @@ public class AudioManager {
        }
    }

    /**
     * Notifies an application with a focus listener of gain or loss of audio focus
     *
     * <p>This is similar to {@link #dispatchAudioFocusChange(AudioFocusInfo, int, AudioPolicy)} but
     * with additional functionality  of fade. The players of the application with  audio focus
     * change, provided they meet the active {@link FadeManagerConfiguration} requirements, are
     * faded before dispatching the callback to the application. For example, players of the
     * application losing audio focus will be faded out, whereas players of the application gaining
     * audio focus will be faded in, if needed.
     *
     * <p>The applicability of fade is decided against the supplied active {@link AudioFocusInfo}.
     * This list cannot be {@code null}. The list can be empty if no other active
     * {@link AudioFocusInfo} available at the time of the dispatch.
     *
     * <p>The {@link FadeManagerConfiguration} supplied here is prioritized over existing fade
     * configurations. If none supplied, either the {@link FadeManagerConfiguration} set through
     * {@link AudioPolicy} or the default will be used to determine the fade properties.
     *
     * <p>This method can only be used by owners of an {@link AudioPolicy} configured with
     * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
     *
     * @param afi the recipient of the focus change, that has previously requested audio focus, and
     *     that was received by the {@code AudioPolicy} through
     *     {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}
     * @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN},
     *     {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or
     *     {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE})
     *     or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS},
     *     {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT},
     *     or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
     *     <br>For the focus gain, the change type should be the same as the app requested
     * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
     * @param otherActiveAfis active {@link AudioFocusInfo} that are granted audio focus at the time
     *     of dispatch
     * @param transientFadeMgrConfig {@link FadeManagerConfiguration} that will be used for fading
     *     players resulting from this dispatch. This is a transient configuration that is only
     *     valid for this focus change and shall be discarded after processing this request.
     * @return {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener or if
     *     there was an error sending the request, or {@link #AUDIOFOCUS_REQUEST_GRANTED} if the
     *     dispatch was successfully sent, or {@link #AUDIOFOCUS_REQUEST_DELAYED} if
     *     the request was successful but the dispatch of focus change was delayed due to a fade
     *     operation.
     * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} or list of
     *     other active {@link AudioFocusInfo} are {@code null}.
     * @hide
     */
    @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
    @SystemApi
    @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public int dispatchAudioFocusChangeWithFade(@NonNull AudioFocusInfo afi, int focusChange,
            @NonNull AudioPolicy ap, @NonNull List<AudioFocusInfo> otherActiveAfis,
            @Nullable FadeManagerConfiguration transientFadeMgrConfig) {
        Objects.requireNonNull(afi, "AudioFocusInfo cannot be null");
        Objects.requireNonNull(ap, "AudioPolicy cannot be null");
        Objects.requireNonNull(otherActiveAfis, "Other active AudioFocusInfo list cannot be null");

        IAudioService service = getService();
        try {
            return service.dispatchFocusChangeWithFade(afi, focusChange, ap.cb(), otherActiveAfis,
                    transientFadeMgrConfig);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Used internally by telephony package to abandon audio focus, typically after a call or
+8 −0
Original line number Diff line number Diff line
@@ -399,6 +399,14 @@ interface IAudioService {
    int dispatchFocusChange(in AudioFocusInfo afi, in int focusChange,
            in IAudioPolicyCallback pcb);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
    int dispatchFocusChangeWithFade(in AudioFocusInfo afi,
            in int focusChange,
            in IAudioPolicyCallback pcb,
            in List<AudioFocusInfo> otherActiveAfis,
            in FadeManagerConfiguration transientFadeMgrConfig);

    oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);

    @EnforcePermission("BLUETOOTH_STACK")
+37 −0
Original line number Diff line number Diff line
@@ -13530,6 +13530,43 @@ public class AudioService extends IAudioService.Stub
        }
    }
    /**
     * see {@link AudioManager#dispatchAudioFocusChangeWithFade(AudioFocusInfo, int, AudioPolicy,
     * List, FadeManagerConfiguration)}
     */
    @android.annotation.EnforcePermission(
            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
            IAudioPolicyCallback pcb, List<AudioFocusInfo> otherActiveAfis,
            FadeManagerConfiguration transientFadeMgrConfig) {
        super.dispatchFocusChangeWithFade_enforcePermission();
        ensureFadeManagerConfigIsEnabled();
        Objects.requireNonNull(afi, "AudioFocusInfo cannot be null");
        Objects.requireNonNull(pcb, "AudioPolicy callback cannot be null");
        Objects.requireNonNull(otherActiveAfis,
                "Other active AudioFocusInfo list cannot be null");
        if (transientFadeMgrConfig != null) {
            validateFadeManagerConfiguration(transientFadeMgrConfig);
        }
        synchronized (mAudioPolicies) {
            Preconditions.checkState(mAudioPolicies.containsKey(pcb.asBinder()),
                    "Unregistered AudioPolicy for focus dispatch with fade");
            // set the transient fade manager config to be used for handling this focus change
            if (transientFadeMgrConfig != null) {
                mPlaybackMonitor.setTransientFadeManagerConfiguration(focusChange,
                        transientFadeMgrConfig);
            }
            int status = mMediaFocusControl.dispatchFocusChangeWithFade(afi, focusChange,
                    otherActiveAfis);
            if (transientFadeMgrConfig != null) {
                mPlaybackMonitor.clearTransientFadeManagerConfiguration(focusChange);
            }
            return status;
        }
    }
    //======================
    // Audioserver state dispatch
+65 −2
Original line number Diff line number Diff line
@@ -83,7 +83,9 @@ public final class FadeConfigurations {
    private FadeManagerConfiguration mDefaultFadeManagerConfig;
    @GuardedBy("mLock")
    private FadeManagerConfiguration mUpdatedFadeManagerConfig;
    /** active fade manager is one of updated > default */
    @GuardedBy("mLock")
    private FadeManagerConfiguration mTransientFadeManagerConfig;
    /** active fade manager is one of: transient > updated > default */
    @GuardedBy("mLock")
    private FadeManagerConfiguration mActiveFadeManagerConfig;

@@ -146,6 +148,63 @@ public final class FadeConfigurations {
        }
    }

    /**
     * Sets the transient fade manager configuration
     *
     * @param fadeManagerConfig custom fade manager configuration
     * @return {@link AudioManager#SUCCESS} if setting custom fade manager configuration succeeds
     *     or {@link AudioManager#ERROR} otherwise (example - when fade manager configuration is
     *     disabled)
     */
    public int setTransientFadeManagerConfiguration(
            @NonNull FadeManagerConfiguration fadeManagerConfig) {
        if (!enableFadeManagerConfiguration()) {
            return AudioManager.ERROR;
        }

        synchronized (mLock) {
            mTransientFadeManagerConfig = Objects.requireNonNull(fadeManagerConfig,
                    "Transient FadeManagerConfiguration cannot be null");
            mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
        }
        return AudioManager.SUCCESS;
    }

    /**
     * Clears the transient fade manager configuration that was previously set with
     * {@link #setTransientFadeManagerConfiguration(FadeManagerConfiguration)}
     *
     * @return {@link AudioManager#SUCCESS} if previously set transient fade manager configuration
     *     is cleared or {@link AudioManager#ERROR} otherwise (example - when fade manager
     *     configuration is disabled)
     */
    public int clearTransientFadeManagerConfiguration() {
        if (!enableFadeManagerConfiguration()) {
            return AudioManager.ERROR;
        }
        synchronized (mLock) {
            mTransientFadeManagerConfig = null;
            mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
        }
        return AudioManager.SUCCESS;
    }

    /**
     * Query if fade should be enforecd on players
     *
     * @return {@code true} if fade is enabled or using default configurations, {@code false}
     * otherwise.
     */
    public boolean isFadeEnabled() {
        if (!enableFadeManagerConfiguration()) {
            return true;
        }

        synchronized (mLock) {
            return getUpdatedFadeManagerConfigLocked().isFadeEnabled();
        }
    }

    /**
     * Query {@link android.media.AudioAttributes.AttributeUsage usages} that are allowed to
     * fade
@@ -469,11 +528,15 @@ public final class FadeConfigurations {
        return mActiveFadeManagerConfig;
    }

    /** Priority between fade manager configs: Updated > Default */
    /** Priority between fade manager configs: Transient > Updated > Default */
    @GuardedBy("mLock")
    private FadeManagerConfiguration getActiveFadeMgrConfigLocked() {
        // below configs are arranged in the order of priority
        // configs placed higher have higher priority
        if (mTransientFadeManagerConfig != null) {
            return mTransientFadeManagerConfig;
        }

        if (mUpdatedFadeManagerConfig != null) {
            return mUpdatedFadeManagerConfig;
        }
Loading