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

Commit 3aab2546 authored by Raj Goparaju's avatar Raj Goparaju
Browse files

Add API to set fade manager configuration

Fade manager configuration allows clients to set
custom fade out and in volume shaper configurations
for usages and audio attributes

Add api to allow clients set and update the fade
manager configuration with AudioPolicy.

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

Change-Id: Id1e2177bc4db9ec6bf499be539f26771f699dd7f
parent 428a66a7
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
@@ -6673,6 +6673,69 @@ package android.media {
    field public static final int CONTENT_ID_NONE = 0; // 0x0
  }
  @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs();
    method public long getFadeInDelayForOffenders();
    method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
    method public long getFadeInDurationForUsage(int);
    method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
    method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int);
    method public long getFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
    method public long getFadeOutDurationForUsage(int);
    method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
    method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int);
    method public int getFadeState();
    method @NonNull public java.util.List<java.lang.Integer> getFadeableUsages();
    method @NonNull public java.util.List<android.media.AudioAttributes> getUnfadeableAudioAttributes();
    method @NonNull public java.util.List<java.lang.Integer> getUnfadeableContentTypes();
    method @NonNull public java.util.List<java.lang.Integer> getUnfadeablePlayerTypes();
    method @NonNull public java.util.List<java.lang.Integer> getUnfadeableUids();
    method public boolean isAudioAttributesUnfadeable(@NonNull android.media.AudioAttributes);
    method public boolean isContentTypeUnfadeable(int);
    method public boolean isFadeEnabled();
    method public boolean isPlayerTypeUnfadeable(int);
    method public boolean isUidUnfadeable(int);
    method public boolean isUsageFadeable(int);
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.media.FadeManagerConfiguration> CREATOR;
    field public static final long DURATION_NOT_SET = 0L; // 0x0L
    field public static final int FADE_STATE_DISABLED = 0; // 0x0
    field public static final int FADE_STATE_ENABLED_AUTO = 2; // 0x2
    field public static final int FADE_STATE_ENABLED_DEFAULT = 1; // 0x1
    field public static final String TAG = "FadeManagerConfiguration";
    field public static final int VOLUME_SHAPER_SYSTEM_FADE_ID = 2; // 0x2
  }
  public static final class FadeManagerConfiguration.Builder {
    ctor public FadeManagerConfiguration.Builder();
    ctor public FadeManagerConfiguration.Builder(long, long);
    ctor public FadeManagerConfiguration.Builder(@NonNull android.media.FadeManagerConfiguration);
    method @NonNull public android.media.FadeManagerConfiguration.Builder addFadeableUsage(int);
    method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
    method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableContentType(int);
    method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableUid(int);
    method @NonNull public android.media.FadeManagerConfiguration build();
    method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsage(int);
    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentType(int);
    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUid(int);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForUsage(int, long);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeState(int);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeableUsages(@NonNull java.util.List<java.lang.Integer>);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setUnfadeableAudioAttributes(@NonNull java.util.List<android.media.AudioAttributes>);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setUnfadeableContentTypes(@NonNull java.util.List<java.lang.Integer>);
    method @NonNull public android.media.FadeManagerConfiguration.Builder setUnfadeableUids(@NonNull java.util.List<java.lang.Integer>);
  }
  public class HwAudioSource {
    method public boolean isPlaying();
    method public void start();
@@ -6891,15 +6954,18 @@ package android.media.audiopolicy {
  public class AudioPolicy {
    method public int attachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
    method @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public int clearFadeManagerConfigurationForFocusLoss();
    method public android.media.AudioRecord createAudioRecordSink(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 @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public android.media.FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss();
    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 boolean removeUidDeviceAffinity(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 @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public int setFadeManagerConfigurationForFocusLoss(@NonNull android.media.FadeManagerConfiguration);
    method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
    method public void setRegistration(String);
    method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
+7 −6
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -93,11 +94,9 @@ import java.util.Objects;
 *      Helps with recreating a new instance from another to simply change/add on top of the
 *      existing ones</li>
 * </ul>
 * TODO(b/304835727): Convert into system API so that it can be set through AudioPolicy
 *
 * @hide
 */

@SystemApi
@FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
public final class FadeManagerConfiguration implements Parcelable {

@@ -523,6 +522,7 @@ public final class FadeManagerConfiguration implements Parcelable {
     *
     * @param fadeState one of the fade state in {@link FadeStateEnum}
     * @return human-readable string
     * @hide
     */
    @NonNull
    public static String fadeStateToString(@FadeStateEnum int fadeState) {
@@ -712,7 +712,8 @@ public final class FadeManagerConfiguration implements Parcelable {
     *
     * <p><b>Notes:</b>
     * <ul>
     *     <li>When fade state is set to enabled, the builder expects at least one valid usage to be
     *     <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT} or
     *     {@link #FADE_STATE_ENABLED_AUTO}, the builder expects at least one valid usage to be
     *     set/added. Failure to do so will result in an exception during {@link #build()}</li>
     *     <li>Every usage added to the fadeable list should have corresponding volume shaper
     *     configs defined. This can be achieved by setting either the duration or volume shaper
@@ -720,8 +721,8 @@ public final class FadeManagerConfiguration implements Parcelable {
     *     {@link #setFadeOutVolumeShaperConfigForUsage(int, VolumeShaper.Configuration)}</li>
     *     <li> It is recommended to set volume shaper configurations individually for fade out and
     *     fade in</li>
     *     <li>For any incomplete volume shaper configs a volume shaper configuration will be
     *     created using either the default fade durations or the ones provided as part of the
     *     <li>For any incomplete volume shaper configurations, a volume shaper configuration will
     *     be created using either the default fade durations or the ones provided as part of the
     *     {@link #Builder(long, long)}</li>
     *     <li>Additional volume shaper configs can also configured for a given usage
     *     with additional attributes like content-type in order to achieve finer fade controls.
+13 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.BluetoothProfileConnectionInfo;
import android.media.FadeManagerConfiguration;
import android.media.IAudioDeviceVolumeDispatcher;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
@@ -754,4 +755,16 @@ interface IAudioService {
    oneway void removeLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);

    PersistableBundle getLoudnessParams(int piid, in LoudnessCodecInfo codecInfo);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
    int setFadeManagerConfigurationForFocusLoss(in FadeManagerConfiguration fmcForFocusLoss);

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
    int clearFadeManagerConfigurationForFocusLoss();

    @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
    FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss();
}
+108 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.media.audiopolicy;

import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -34,6 +36,7 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.FadeManagerConfiguration;
import android.media.IAudioService;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
@@ -49,6 +52,7 @@ import android.util.Pair;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -612,6 +616,110 @@ public class AudioPolicy {
        return mRegistrationId;
    }

    /**
     * Sets a custom {@link FadeManagerConfiguration} to handle fade cycle of players during
     * {@link android.media.AudioManager#AUDIOFOCUS_LOSS}
     *
     * @param fmcForFocusLoss custom {@link FadeManagerConfiguration}
     * @return {@link AudioManager#SUCCESS} if the update was successful,
     *     {@link AudioManager#ERROR} otherwise
     * @throws IllegalStateException if the audio policy is not registered
     * @hide
     */
    @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    @SystemApi
    public int setFadeManagerConfigurationForFocusLoss(
            @NonNull FadeManagerConfiguration fmcForFocusLoss) {
        Objects.requireNonNull(fmcForFocusLoss,
                "FadeManagerConfiguration for focus loss cannot be null");

        IAudioService service = getService();
        synchronized (mLock) {
            Preconditions.checkState(isAudioPolicyRegisteredLocked(),
                    "Cannot set FadeManagerConfiguration with unregistered AudioPolicy");

            try {
                return service.setFadeManagerConfigurationForFocusLoss(fmcForFocusLoss);
            } catch (RemoteException e) {
                Log.e(TAG, "Received remote exception for setFadeManagerConfigurationForFocusLoss:",
                        e);
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Clear the current {@link FadeManagerConfiguration} set to handle fade cycles of players
     * during {@link android.media.AudioManager#AUDIOFOCUS_LOSS}
     *
     * <p>In the absence of custom {@link FadeManagerConfiguration}, the default configurations will
     * be used to handle fade cycles during audio focus loss.
     *
     * @return {@link AudioManager#SUCCESS} if the update was successful,
     *     {@link AudioManager#ERROR} otherwise
     * @throws IllegalStateException if the audio policy is not registered
     * @see #setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)
     * @hide
     */
    @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    @SystemApi
    public int clearFadeManagerConfigurationForFocusLoss() {
        IAudioService service = getService();
        synchronized (mLock) {
            Preconditions.checkState(isAudioPolicyRegisteredLocked(),
                    "Cannot clear FadeManagerConfiguration from unregistered AudioPolicy");

            try {
                return service.clearFadeManagerConfigurationForFocusLoss();
            } catch (RemoteException e) {
                Log.e(TAG, "Received remote exception for "
                                + "clearFadeManagerConfigurationForFocusLoss:", e);
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Get the current fade manager configuration used for fade operations during
     * {@link android.media.AudioManager#AUDIOFOCUS_LOSS}
     *
     * <p>If no custom {@link FadeManagerConfiguration} is set, the default configuration currently
     * active will be returned.
     *
     * @return the active {@link FadeManagerConfiguration} used during audio focus loss
     * @throws IllegalStateException if the audio policy is not registered
     * @see #setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)
     * @see #clearFadeManagerConfigurationForFocusLoss()
     * @hide
     */
    @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    @SystemApi
    @NonNull
    public FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss() {
        IAudioService service = getService();
        synchronized (mLock) {
            Preconditions.checkState(isAudioPolicyRegisteredLocked(),
                    "Cannot get FadeManagerConfiguration from unregistered AudioPolicy");

            try {
                return service.getFadeManagerConfigurationForFocusLoss();
            } catch (RemoteException e) {
                Log.e(TAG, "Received remote exception for getFadeManagerConfigurationForFocusLoss:",
                        e);
                throw e.rethrowFromSystemServer();

            }
        }
    }

    @GuardedBy("mLock")
    private boolean isAudioPolicyRegisteredLocked() {
        return mStatus == POLICY_STATUS_REGISTERED;
    }

    private boolean policyReadyToUse() {
        synchronized (mLock) {
            if (mStatus != POLICY_STATUS_REGISTERED) {
+59 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import static android.media.AudioManager.STREAM_SYSTEM;
import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
@@ -116,6 +117,7 @@ import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.AudioTrack;
import android.media.BluetoothProfileConnectionInfo;
import android.media.FadeManagerConfiguration;
import android.media.IAudioDeviceVolumeDispatcher;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
@@ -4513,6 +4515,8 @@ public class AudioService extends IAudioService.Stub
                + bluetoothMacAddressAnonymization());
        pw.println("\tcom.android.media.audio.Flags.disablePrescaleAbsoluteVolume:"
                + disablePrescaleAbsoluteVolume());
        pw.println("\tandroid.media.audiopolicy.enableFadeManagerConfiguration:"
                + enableFadeManagerConfiguration());
    }
    private void dumpAudioMode(PrintWriter pw) {
@@ -12613,6 +12617,47 @@ public class AudioService extends IAudioService.Stub
        return mMediaFocusControl.sendFocusLoss(focusLoser);
    }
    /**
     * see {@link AudioPolicy#setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)}
     */
    @android.annotation.EnforcePermission(
            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public int setFadeManagerConfigurationForFocusLoss(
            @NonNull FadeManagerConfiguration fmcForFocusLoss) {
        super.setFadeManagerConfigurationForFocusLoss_enforcePermission();
        ensureFadeManagerConfigIsEnabled();
        Objects.requireNonNull(fmcForFocusLoss,
                "Fade manager config for focus loss cannot be null");
        validateFadeManagerConfiguration(fmcForFocusLoss);
        return mPlaybackMonitor.setFadeManagerConfiguration(AudioManager.AUDIOFOCUS_LOSS,
                fmcForFocusLoss);
    }
    /**
     * see {@link AudioPolicy#clearFadeManagerConfigurationForFocusLoss()}
     */
    @android.annotation.EnforcePermission(
            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public int clearFadeManagerConfigurationForFocusLoss() {
        super.clearFadeManagerConfigurationForFocusLoss_enforcePermission();
        ensureFadeManagerConfigIsEnabled();
        return mPlaybackMonitor.clearFadeManagerConfiguration(AudioManager.AUDIOFOCUS_LOSS);
    }
    /**
     * see {@link AudioPolicy#getFadeManagerConfigurationForFocusLoss()}
     */
    @android.annotation.EnforcePermission(
            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
    public FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss() {
        super.getFadeManagerConfigurationForFocusLoss_enforcePermission();
        ensureFadeManagerConfigIsEnabled();
        return mPlaybackMonitor.getFadeManagerConfiguration(AudioManager.AUDIOFOCUS_LOSS);
    }
    /**
     * @see AudioManager#getHalVersion
     */
@@ -12814,6 +12859,19 @@ public class AudioService extends IAudioService.Stub
        }
    }
    private void ensureFadeManagerConfigIsEnabled() {
        Preconditions.checkState(enableFadeManagerConfiguration(),
                "Fade manager configuration not supported");
    }
    private void validateFadeManagerConfiguration(FadeManagerConfiguration fmc) {
        // validate permission of audio attributes
        List<AudioAttributes> attrs = fmc.getAudioAttributesWithVolumeShaperConfigs();
        for (int index = 0; index < attrs.size(); index++) {
            validateAudioAttributesUsage(attrs.get(index));
        }
    }
    //======================
    // Audio policy callbacks from AudioSystem for dynamic policies
    //======================
@@ -13114,6 +13172,7 @@ public class AudioService extends IAudioService.Stub
                            + "could not link to " + projection + " binder death", e);
                }
            }
            int status = connectMixes();
            if (status != AudioSystem.SUCCESS) {
                release();
Loading