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

Commit 8922f5db authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Audio focus enforcement: use VolumeShaper for ducking"

parents 6955665c dce82ab7
Loading
Loading
Loading
Loading
+28 −22
Original line number Diff line number Diff line
@@ -37,8 +37,9 @@ import java.util.Objects;
public final class VolumeShaper {
    /* member variables */
    private int mId;
    private final WeakReference<PlayerBase> mPlayerBase;
    private final WeakReference<PlayerProxy> mPlayerProxy;
    private final WeakReference<PlayerBase> mWeakPlayerBase;
    private final WeakReference<PlayerProxy> mWeakPlayerProxy;
    private PlayerProxy mPlayerProxy;

    /**
     * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and an
@@ -64,14 +65,14 @@ public final class VolumeShaper {

    /* package */ VolumeShaper(
            @NonNull Configuration configuration, @NonNull PlayerBase playerBase) {
        mPlayerBase = new WeakReference<PlayerBase>(playerBase);
        mWeakPlayerBase = new WeakReference<PlayerBase>(playerBase);
        mPlayerProxy = null;
        mWeakPlayerProxy = null;
        mId = applyPlayer(configuration, new Operation.Builder().defer().build());
    }

    /**
     * @hide
     * TODO SystemApi
     * Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
     * {@code PlayerProxy} object.  The PlayerProxy object requires that the configuration
     * be set with a system VolumeShaper id (this is a reserved value).
@@ -80,12 +81,20 @@ public final class VolumeShaper {
     * @param playerProxy
     */
    public VolumeShaper(
            @NonNull Configuration configuration, @NonNull PlayerProxy playerProxy) {
            @NonNull Configuration configuration,
            @NonNull PlayerProxy playerProxy,
            boolean keepReference) {
        if (configuration.getId() < 0) {
            throw new IllegalArgumentException("playerProxy configuration id must be specified");
        }
        mPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
        mPlayerBase = null;
        if (keepReference) {
            mPlayerProxy = playerProxy;
            mWeakPlayerProxy = null;
        } else {
            mWeakPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
            mPlayerProxy = null;
        }
        mWeakPlayerBase = null;
        mId = applyPlayer(configuration, new Operation.Builder().defer().build());
    }

@@ -143,12 +152,13 @@ public final class VolumeShaper {
        } catch (IllegalStateException ise) {
            ; // ok
        }
        if (mPlayerBase != null) {
            mPlayerBase.clear();
        if (mWeakPlayerBase != null) {
            mWeakPlayerBase.clear();
        }
        if (mPlayerProxy != null) {
            mPlayerProxy.clear();
        if (mWeakPlayerProxy != null) {
            mWeakPlayerProxy.clear();
        }
        mPlayerProxy = null;
    }

    @Override
@@ -167,11 +177,11 @@ public final class VolumeShaper {
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation) {
        final int id;
        if (mPlayerProxy != null) {
        if (mPlayerProxy != null || mWeakPlayerProxy != null) {
            // The PlayerProxy accepts only one way transactions so
            // the Configuration must have an id set to one of the system
            // ids (a positive value less than 16).
            PlayerProxy player = mPlayerProxy.get();
            PlayerProxy player = mWeakPlayerProxy != null ? mWeakPlayerProxy.get() : mPlayerProxy;
            if (player == null) {
                throw new IllegalStateException("player deallocated");
            }
@@ -180,8 +190,8 @@ public final class VolumeShaper {
                throw new IllegalArgumentException("proxy requires configuration with id");
            }
            player.applyVolumeShaper(configuration, operation);
        } else if (mPlayerBase != null) {
            PlayerBase player = mPlayerBase.get();
        } else if (mWeakPlayerBase != null) {
            PlayerBase player = mWeakPlayerBase.get();
            if (player == null) {
                throw new IllegalStateException("player deallocated");
            }
@@ -210,14 +220,10 @@ public final class VolumeShaper {
     */
    private @NonNull VolumeShaper.State getStatePlayer(int id) {
        final VolumeShaper.State state;
        if (mPlayerProxy != null) {
            PlayerProxy player = mPlayerProxy.get();
            if (player == null) {
                throw new IllegalStateException("player deallocated");
            }
        if (mPlayerProxy != null || mWeakPlayerProxy != null) {
            throw new IllegalStateException("getStatePlayer not permitted through proxy");
        } else if (mPlayerBase != null) {
            PlayerBase player = mPlayerBase.get();
        } else if (mWeakPlayerBase != null) {
            PlayerBase player = mWeakPlayerBase.get();
            if (player == null) {
                throw new IllegalStateException("player deallocated");
            }
+1 −1
Original line number Diff line number Diff line
@@ -437,7 +437,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
     * @param attr attributes of the sound about to start playing
     * @return time in ms
     */
    protected int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
    protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
        switch (attr.getUsage()) {
            case AudioAttributes.USAGE_MEDIA:
            case AudioAttributes.USAGE_GAME:
+40 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.media.AudioSystem;
import android.media.IPlaybackConfigDispatcher;
import android.media.MediaRecorder;
import android.media.PlayerBase;
import android.media.VolumeShaper;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -47,6 +48,7 @@ public final class PlaybackActivityMonitor

    public final static String TAG = "AudioService.PlaybackActivityMonitor";
    private final static boolean DEBUG = false;
    private final static int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;

    private ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
    // a public client is one that needs an anonymized version of the playback configurations, we
@@ -126,6 +128,11 @@ public final class PlaybackActivityMonitor
            final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
            if (checkConfigurationCaller(piid, apc, binderUid)) {
                mPlayers.remove(new Integer(piid));
                final VolumeShaper vs = mDuckVolumeShapers.get(new Integer(piid));
                if (vs != null) {
                    vs.release();
                    mDuckVolumeShapers.remove(new Integer(piid));
                }
            } else {
                Log.e(TAG, "Error releasing player " + piid);
            }
@@ -242,6 +249,20 @@ public final class PlaybackActivityMonitor
    private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
    private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();

    private final VolumeShaper.Configuration DUCK_VSHAPE =
            new VolumeShaper.Configuration.Builder()
                .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
                .setCurve(new float[] { 0.f, 1.f } /* times */,
                    new float[] { 1.f, 0.2f } /* volumes */)
                .setDurationMs(MediaFocusControl.getFocusRampTimeMs(
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
                    new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
                            .build()))
                .build();

    private final HashMap<Integer, VolumeShaper> mDuckVolumeShapers =
            new HashMap<Integer, VolumeShaper>();

    @Override
    public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
        if (DEBUG) {
@@ -268,11 +289,24 @@ public final class PlaybackActivityMonitor
                        // the player is speaking, ducking will make the speech unintelligible
                        // so let the app handle it instead
                        return false;
                    } else if (apc.getPlayerType()
                            == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
                        // TODO support ducking of SoundPool players
                        return false;
                    } else {
                        try {
                            if (DEBUG) { Log.v(TAG, "ducking player " + piid); }
                            //FIXME just a test before we have VolumeShape
                            apc.getPlayerProxy().setPan(-1.0f);
                            final VolumeShaper ducker;
                            if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
                                ducker = mDuckVolumeShapers.get(new Integer(piid));
                            } else {
                                ducker = new VolumeShaper(
                                        DUCK_VSHAPE,
                                        apc.getPlayerProxy(),
                                        true /* keepReference */);
                                mDuckVolumeShapers.put(new Integer(piid), ducker);
                            }
                            ducker.apply(VolumeShaper.Operation.PLAY); // duck
                            mDuckedPlayers.add(piid);
                        } catch (Exception e) {
                            Log.e(TAG, "Error ducking player " + piid, e);
@@ -301,8 +335,10 @@ public final class PlaybackActivityMonitor
                    try {
                        if (DEBUG) { Log.v(TAG, "unducking player" + piid); }
                        mDuckedPlayers.remove(new Integer(piid));
                        //FIXME just a test before we have VolumeShape
                        apc.getPlayerProxy().setPan(0.0f);
                        if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
                            final VolumeShaper ducker = mDuckVolumeShapers.get(new Integer(piid));
                            ducker.apply(VolumeShaper.Operation.REVERSE); // unduck
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "Error unducking player " + piid, e);
                    }