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

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

Merge "Audio focus: API for external audio focus policy" into oc-dev

parents c56defe9 126cf03f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -22842,6 +22842,7 @@ package android.media {
    method public void adjustStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustVolume(int, int);
    method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
    method public void dispatchMediaKeyEvent(android.view.KeyEvent);
    method public int generateAudioSessionId();
    method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
@@ -25844,8 +25845,10 @@ package android.media.audiopolicy {
  public static abstract class AudioPolicy.AudioPolicyFocusListener {
    ctor public AudioPolicy.AudioPolicyFocusListener();
    method public void onAudioFocusAbandon(android.media.AudioFocusInfo);
    method public void onAudioFocusGrant(android.media.AudioFocusInfo, int);
    method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean);
    method public void onAudioFocusRequest(android.media.AudioFocusInfo, int);
  }
  public static abstract class AudioPolicy.AudioPolicyStatusListener {
@@ -25860,6 +25863,7 @@ package android.media.audiopolicy {
    method public android.media.audiopolicy.AudioPolicy build();
    method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener);
    method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener);
    method public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean);
    method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException;
  }
+41 −3
Original line number Diff line number Diff line
@@ -2538,6 +2538,44 @@ public class AudioManager {
        }
    }

    /**
     * @hide
     * Notifies an application with a focus listener of gain or loss of audio focus.
     * 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.
     * @return {@link #AUDIOFOCUS_REQUEST_GRANTED} if the dispatch was successfully sent, or
     *     {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener, or
     *     if there was an error sending the request.
     * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
     */
    @SystemApi
    public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
            @NonNull AudioPolicy ap) {
        if (afi == null) {
            throw new NullPointerException("Illegal null AudioFocusInfo");
        }
        if (ap == null) {
            throw new NullPointerException("Illegal null AudioPolicy");
        }
        final IAudioService service = getService();
        try {
            return service.dispatchFocusChange(afi, focusChange, ap.cb());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Used internally by telephony package to abandon audio focus, typically after a call or
@@ -2548,7 +2586,7 @@ public class AudioManager {
        final IAudioService service = getService();
        try {
            service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID,
                    null /*AudioAttributes, legacy behavior*/);
                    null /*AudioAttributes, legacy behavior*/, getContext().getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -2579,7 +2617,7 @@ public class AudioManager {
        final IAudioService service = getService();
        try {
            status = service.abandonAudioFocus(mAudioFocusDispatcher,
                    getIdForAudioFocusListener(l), aa);
                    getIdForAudioFocusListener(l), aa, getContext().getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -2787,7 +2825,7 @@ public class AudioManager {
        final IAudioService service = getService();
        try {
            String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
                    policy.hasFocusListener());
                    policy.hasFocusListener(), policy.isFocusPolicy());
            if (regId == null) {
                return ERROR;
            } else {
+7 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
@@ -122,7 +123,8 @@ interface IAudioService {
            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
            IAudioPolicyCallback pcb);

    int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa);
    int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa,
            in String callingPackageName);

    void unregisterAudioFocusClient(String clientId);

@@ -164,7 +166,7 @@ interface IAudioService {
    boolean isHdmiSystemAudioSupported();

    String registerAudioPolicy(in AudioPolicyConfig policyConfig,
            in IAudioPolicyCallback pcb, boolean hasFocusListener);
            in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy);

    oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);

@@ -196,5 +198,8 @@ interface IAudioService {

    int getFocusRampTimeMs(in int focusGain, in AudioAttributes attr);

    int dispatchFocusChange(in AudioFocusInfo afi, in int focusChange,
            in IAudioPolicyCallback pcb);

    // WARNING: read warning at top of file, it is recommended to add new methods at the end
}
+86 −3
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ public class AudioPolicy {
    private int mStatus;
    private String mRegistrationId;
    private AudioPolicyStatusListener mStatusListener;
    private boolean mIsFocusPolicy;

    /**
     * The behavior of a policy with regards to audio focus where it relies on the application
@@ -96,12 +97,14 @@ public class AudioPolicy {
    public AudioPolicyConfig getConfig() { return mConfig; }
    /** @hide */
    public boolean hasFocusListener() { return mFocusListener != null; }
    /** @hide */
    public boolean isFocusPolicy() { return mIsFocusPolicy; }

    /**
     * The parameter is guaranteed non-null through the Builder
     */
    private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
            AudioPolicyFocusListener fl, AudioPolicyStatusListener sl) {
            AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) {
        mConfig = config;
        mStatus = POLICY_STATUS_UNREGISTERED;
        mContext = context;
@@ -116,10 +119,12 @@ public class AudioPolicy {
        }
        mFocusListener = fl;
        mStatusListener = sl;
        mIsFocusPolicy = isFocusPolicy;
    }

    /**
     * Builder class for {@link AudioPolicy} objects
     * Builder class for {@link AudioPolicy} objects.
     * By default the policy to be created doesn't govern audio focus decisions.
     */
    @SystemApi
    public static class Builder {
@@ -128,6 +133,7 @@ public class AudioPolicy {
        private Looper mLooper;
        private AudioPolicyFocusListener mFocusListener;
        private AudioPolicyStatusListener mStatusListener;
        private boolean mIsFocusPolicy = false;

        /**
         * Constructs a new Builder with no audio mixes.
@@ -178,6 +184,21 @@ public class AudioPolicy {
            mFocusListener = l;
        }

        /**
         * Declares whether this policy will grant and deny audio focus through
         * the {@link AudioPolicy.AudioPolicyStatusListener}.
         * If set to {@code true}, it is mandatory to set an
         * {@link AudioPolicy.AudioPolicyStatusListener} in order to successfully build
         * an {@code AudioPolicy} instance.
         * @param enforce true if the policy will govern audio focus decisions.
         * @return the same Builder instance.
         */
        @SystemApi
        public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) {
            mIsFocusPolicy = isFocusPolicy;
            return this;
        }

        /**
         * Sets the audio policy status listener.
         * @param l a {@link AudioPolicy.AudioPolicyStatusListener}
@@ -187,6 +208,14 @@ public class AudioPolicy {
            mStatusListener = l;
        }

        /**
         * Combines all of the attributes that have been set on this {@code Builder} and returns a
         * new {@link AudioPolicy} object.
         * @return a new {@code AudioPolicy} object.
         * @throws IllegalStateException if there is no
         *     {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured
         *     as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}.
         */
        @SystemApi
        public AudioPolicy build() {
            if (mStatusListener != null) {
@@ -195,8 +224,12 @@ public class AudioPolicy {
                    mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
                }
            }
            if (mIsFocusPolicy && mFocusListener == null) {
                throw new IllegalStateException("Cannot be a focus policy without "
                        + "an AudioPolicyFocusListener");
            }
            return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
                    mFocusListener, mStatusListener);
                    mFocusListener, mStatusListener, mIsFocusPolicy);
        }
    }

@@ -402,6 +435,24 @@ public class AudioPolicy {
    public static abstract class AudioPolicyFocusListener {
        public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
        public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
        /**
         * Called whenever an application requests audio focus.
         * Only ever called if the {@link AudioPolicy} was built with
         * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
         * @param afi information about the focus request and the requester
         * @param requestResult the result that was returned synchronously by the framework to the
         *     application, {@link #AUDIOFOCUS_REQUEST_FAILED},or
         *     {@link #AUDIOFOCUS_REQUEST_DELAYED}.
         */
        public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
        /**
         * Called whenever an application abandons audio focus.
         * Only ever called if the {@link AudioPolicy} was built with
         * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
         * @param afi information about the focus request being abandoned and the original
         *     requester.
         */
        public void onAudioFocusAbandon(AudioFocusInfo afi) {}
    }

    private void onPolicyStatusChange() {
@@ -439,6 +490,22 @@ public class AudioPolicy {
            }
        }

        public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
            sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
            if (DEBUG) {
                Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
                        + afi.getClientId() + "reqRes=" + requestResult);
            }
        }

        public void notifyAudioFocusAbandon(AudioFocusInfo afi) {
            sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */);
            if (DEBUG) {
                Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client="
                        + afi.getClientId());
            }
        }

        public void notifyMixStateUpdate(String regId, int state) {
            for (AudioMix mix : mConfig.getMixes()) {
                if (mix.getRegistration().equals(regId)) {
@@ -459,6 +526,8 @@ public class AudioPolicy {
    private final static int MSG_FOCUS_GRANT = 1;
    private final static int MSG_FOCUS_LOSS = 2;
    private final static int MSG_MIX_STATE_UPDATE = 3;
    private final static int MSG_FOCUS_REQUEST = 4;
    private final static int MSG_FOCUS_ABANDON = 5;

    private class EventHandler extends Handler {
        public EventHandler(AudioPolicy ap, Looper looper) {
@@ -488,6 +557,20 @@ public class AudioPolicy {
                        mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
                    }
                    break;
                case MSG_FOCUS_REQUEST:
                    if (mFocusListener != null) {
                        mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1);
                    } else { // should never be null, but don't crash
                        Log.e(TAG, "Invalid null focus listener for focus request event");
                    }
                    break;
                case MSG_FOCUS_ABANDON:
                    if (mFocusListener != null) { // should never be null
                        mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj);
                    } else { // should never be null, but don't crash
                        Log.e(TAG, "Invalid null focus listener for focus abandon event");
                    }
                    break;
                default:
                    Log.e(TAG, "Unknown event " + msg.what);
            }
+4 −1
Original line number Diff line number Diff line
@@ -22,9 +22,12 @@ import android.media.AudioFocusInfo;
 */
oneway interface IAudioPolicyCallback {

    // callbacks for audio focus
    // callbacks for audio focus listening
    void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult);
    void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified);
    // callback for audio focus policy
    void notifyAudioFocusRequest(in AudioFocusInfo afi, int requestResult);
    void notifyAudioFocusAbandon(in AudioFocusInfo afi);

    // callback for mix activity status update
    void notifyMixStateUpdate(in String regId, int state);
Loading