Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.annotation.SystemApi;
     22 import android.annotation.TestApi;
     23 import android.media.AudioManager.OnAudioFocusChangeListener;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.os.Looper;
     27 
     28 /**
     29  * A class to encapsulate information about an audio focus request.
     30  * An {@code AudioFocusRequest} instance is built by {@link Builder}, and is used to
     31  * request and abandon audio focus, respectively
     32  * with {@link AudioManager#requestAudioFocus(AudioFocusRequest)} and
     33  * {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
     34  *
     35  * <h3>What is audio focus?</h3>
     36  * <p>Audio focus is a concept introduced in API 8. It is used to convey the fact that a user can
     37  * only focus on a single audio stream at a time, e.g. listening to music or a podcast, but not
     38  * both at the same time. In some cases, multiple audio streams can be playing at the same time,
     39  * but there is only one the user would really listen to (focus on), while the other plays in
     40  * the background. An example of this is driving directions being spoken while music plays at
     41  * a reduced volume (a.k.a. ducking).
     42  * <p>When an application requests audio focus, it expresses its intention to own audio focus to
     43  * play audio. Lets review the different types of focus requests, the return value after a request,
     44  * and the responses to a loss.
     45  * <p class="note">Note: applications should not play anything until granted focus.</p>
     46  *
     47  * <h3>The different types of focus requests</h3>
     48  * <p>There are four focus request types. A successful focus request with each will yield different
     49  * behaviors by the system and the other application that previously held audio focus.
     50  * <ul>
     51  * <li>{@link AudioManager#AUDIOFOCUS_GAIN} expresses the fact that your application is now the
     52  * sole source of audio that the user is listening to. The duration of the audio playback is
     53  * unknown, and is possibly very long: after the user finishes interacting with your application,
     54  * (s)he doesnt expect another audio stream to resume. Examples of uses of this focus gain are
     55  * for music playback, for a game or a video player.</li>
     56  *
     57  * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT} is for a situation when you know your
     58  * application is temporarily grabbing focus from the current owner, but the user expects playback
     59  * to go back to where it was once your application no longer requires audio focus. An example is
     60  * for playing an alarm, or during a VoIP call. The playback is known to be finite: the alarm will
     61  * time-out or be dismissed, the VoIP call has a beginning and an end. When any of those events
     62  * ends, and if the user was listening to music when it started, the user expects music to resume,
     63  * but didnt wish to listen to both at the same time.</li>
     64  *
     65  * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}: this focus request type is similar
     66  * to {@code AUDIOFOCUS_GAIN_TRANSIENT} for the temporary aspect of the focus request, but it also
     67  * expresses the fact during the time you own focus, you allow another application to keep playing
     68  * at a reduced volume, ducked. Examples are when playing driving directions or notifications,
     69  * its ok for music to keep playing, but not loud enough that it would prevent the directions to
     70  * be hard to understand. A typical attenuation by the ducked application is a factor of 0.2f
     71  * (or -14dB), that can for instance be applied with {@code MediaPlayer.setVolume(0.2f)} when
     72  * using this class for playback.</li>
     73  *
     74  * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} is also for a temporary request,
     75  * but also expresses that your application expects the device to not play anything else. This is
     76  * typically used if you are doing audio recording or speech recognition, and dont want for
     77  * examples notifications to be played by the system during that time.</li>
     78  * </ul>
     79  *
     80  * <p>An {@code AudioFocusRequest} instance always contains one of the four types of requests
     81  * explained above. It is passed when building an {@code AudioFocusRequest} instance with its
     82  * builder in the {@link Builder} constructor
     83  * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or
     84  * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with
     85  * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}.
     86  *
     87  * <h3>Qualifying your focus request</h3>
     88  * <h4>Use case requiring a focus request</h4>
     89  * <p>Any focus request is qualified by the {@link AudioAttributes}
     90  * (see {@link Builder#setAudioAttributes(AudioAttributes)}) that describe the audio use case that
     91  * will follow the request (once it's successful or granted). It is recommended to use the
     92  * same {@code AudioAttributes} for the request as the attributes you are using for audio/media
     93  * playback.
     94  * <br>If no attributes are set, default attributes of {@link AudioAttributes#USAGE_MEDIA} are used.
     95  *
     96  * <h4>Delayed focus</h4>
     97  * <p>Audio focus can be "locked" by the system for a number of reasons: during a phone call, when
     98  * the car to which the device is connected plays an emergency message... To support these
     99  * situations, the application can request to be notified when its request is fulfilled, by flagging
    100  * its request as accepting delayed focus, with {@link Builder#setAcceptsDelayedFocusGain(boolean)}.
    101  * <br>If focus is requested while being locked by the system,
    102  * {@link AudioManager#requestAudioFocus(AudioFocusRequest)} will return
    103  * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}. When focus isn't locked anymore, the focus
    104  * listener set with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener)}
    105  * or with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)} will
    106  * be called to notify the application it now owns audio focus.
    107  *
    108  * <h4>Pausing vs ducking</h4>
    109  * <p>When an application requested audio focus with
    110  * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, the system will duck the current focus
    111  * owner.
    112  * <p class="note">Note: this behavior is <b>new for Android O</b>, whereas applications targeting
    113  * SDK level up to API 25 had to implement the ducking themselves when they received a focus
    114  * loss of {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
    115  * <p>But ducking is not always the behavior expected by the user. A typical example is when the
    116  * device plays driving directions while the user is listening to an audio book or podcast, and
    117  * expects the audio playback to pause, instead of duck, as it is hard to understand a navigation
    118  * prompt and spoken content at the same time. Therefore the system will not automatically duck
    119  * when it detects it would be ducking spoken content: such content is detected when the
    120  * {@code AudioAttributes} of the player are qualified by
    121  * {@link AudioAttributes#CONTENT_TYPE_SPEECH}. Refer for instance to
    122  * {@link AudioAttributes.Builder#setContentType(int)} and
    123  * {@link MediaPlayer#setAudioAttributes(AudioAttributes)} if you are writing a media playback
    124  * application for audio book, podcasts... Since the system will not automatically duck applications
    125  * that play speech, it calls their focus listener instead to notify them of
    126  * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, so they can pause instead. Note that
    127  * this behavior is independent of the use of {@code AudioFocusRequest}, but tied to the use
    128  * of {@code AudioAttributes}.
    129  * <p>If your application requires pausing instead of ducking for any other reason than playing
    130  * speech, you can also declare so with {@link Builder#setWillPauseWhenDucked(boolean)}, which will
    131  * cause the system to call your focus listener instead of automatically ducking.
    132  *
    133  * <h4>Example</h4>
    134  * <p>The example below covers the following steps to be found in any application that would play
    135  * audio, and use audio focus. Here we play an audio book, and our application is intended to pause
    136  * rather than duck when it loses focus. These steps consist in:
    137  * <ul>
    138  * <li>Creating {@code AudioAttributes} to be used for the playback and the focus request.</li>
    139  * <li>Configuring and creating the {@code AudioFocusRequest} instance that defines the intended
    140  *     focus behaviors.</li>
    141  * <li>Requesting audio focus and checking the return code to see if playback can happen right
    142  *     away, or is delayed.</li>
    143  * <li>Implementing a focus change listener to respond to focus gains and losses.</li>
    144  * </ul>
    145  * <p>
    146  * <pre class="prettyprint">
    147  * // initialization of the audio attributes and focus request
    148  * mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
    149  * mPlaybackAttributes = new AudioAttributes.Builder()
    150  *         .setUsage(AudioAttributes.USAGE_MEDIA)
    151  *         .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
    152  *         .build();
    153  * mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
    154  *         .setAudioAttributes(mPlaybackAttributes)
    155  *         .setAcceptsDelayedFocusGain(true)
    156  *         .setWillPauseWhenDucked(true)
    157  *         .setOnAudioFocusChangeListener(this, mMyHandler)
    158  *         .build();
    159  * mMediaPlayer = new MediaPlayer();
    160  * mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
    161  * final Object mFocusLock = new Object();
    162  *
    163  * boolean mPlaybackDelayed = false;
    164  *
    165  * // requesting audio focus
    166  * int res = mAudioManager.requestAudioFocus(mFocusRequest);
    167  * synchronized (mFocusLock) {
    168  *     if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
    169  *         mPlaybackDelayed = false;
    170  *     } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    171  *         mPlaybackDelayed = false;
    172  *         playbackNow();
    173  *     } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
    174  *        mPlaybackDelayed = true;
    175  *     }
    176  * }
    177  *
    178  * // implementation of the OnAudioFocusChangeListener
    179  * &#64;Override
    180  * public void onAudioFocusChange(int focusChange) {
    181  *     switch (focusChange) {
    182  *         case AudioManager.AUDIOFOCUS_GAIN:
    183  *             if (mPlaybackDelayed || mResumeOnFocusGain) {
    184  *                 synchronized (mFocusLock) {
    185  *                     mPlaybackDelayed = false;
    186  *                     mResumeOnFocusGain = false;
    187  *                 }
    188  *                 playbackNow();
    189  *             }
    190  *             break;
    191  *         case AudioManager.AUDIOFOCUS_LOSS:
    192  *             synchronized (mFocusLock) {
    193  *                 // this is not a transient loss, we shouldn't automatically resume for now
    194  *                 mResumeOnFocusGain = false;
    195  *                 mPlaybackDelayed = false;
    196  *             }
    197  *             pausePlayback();
    198  *             break;
    199  *         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    200  *         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    201  *             // we handle all transient losses the same way because we never duck audio books
    202  *             synchronized (mFocusLock) {
    203  *                 // we should only resume if playback was interrupted
    204  *                 mResumeOnFocusGain = mMediaPlayer.isPlaying();
    205  *                 mPlaybackDelayed = false;
    206  *             }
    207  *             pausePlayback();
    208  *             break;
    209  *     }
    210  * }
    211  *
    212  * // Important:
    213  * // Also set "mResumeOnFocusGain" to false when the user pauses or stops playback: this way your
    214  * // application doesn't automatically restart when it gains focus, even though the user had
    215  * // stopped it.
    216  * </pre>
    217  */
    218 
    219 public final class AudioFocusRequest {
    220 
    221     // default attributes for the request when not specified
    222     private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder()
    223             .setUsage(AudioAttributes.USAGE_MEDIA).build();
    224 
    225     /** @hide */
    226     public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking";
    227 
    228     private final OnAudioFocusChangeListener mFocusListener; // may be null
    229     private final Handler mListenerHandler;                  // may be null
    230     private final AudioAttributes mAttr;                     // never null
    231     private final int mFocusGain;
    232     private final int mFlags;
    233 
    234     private AudioFocusRequest(OnAudioFocusChangeListener listener, Handler handler,
    235             AudioAttributes attr, int focusGain, int flags) {
    236         mFocusListener = listener;
    237         mListenerHandler = handler;
    238         mFocusGain = focusGain;
    239         mAttr = attr;
    240         mFlags = flags;
    241     }
    242 
    243     /**
    244      * @hide
    245      * Checks whether a focus gain constant is a valid value for an audio focus request.
    246      * @param focusGain value to check
    247      * @return true if focusGain is a valid value for an audio focus request.
    248      */
    249     final static boolean isValidFocusGain(int focusGain) {
    250         switch (focusGain) {
    251             case AudioManager.AUDIOFOCUS_GAIN:
    252             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
    253             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
    254             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
    255                 return true;
    256             default:
    257                 return false;
    258         }
    259     }
    260 
    261     /**
    262      * @hide
    263      * Returns the focus change listener set for this {@code AudioFocusRequest}.
    264      * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set.
    265      */
    266     @TestApi
    267     public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
    268         return mFocusListener;
    269     }
    270 
    271     /**
    272      * @hide
    273      * Returns the {@link Handler} to be used for the focus change listener.
    274      * @return the same {@code Handler} set in.
    275      *   {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}, or null
    276      *   if no listener was set.
    277      */
    278     public @Nullable Handler getOnAudioFocusChangeListenerHandler() {
    279         return mListenerHandler;
    280     }
    281 
    282     /**
    283      * Returns the {@link AudioAttributes} set for this {@code AudioFocusRequest}, or the default
    284      * attributes if none were set.
    285      * @return non-null {@link AudioAttributes}.
    286      */
    287     public @NonNull AudioAttributes getAudioAttributes() {
    288         return mAttr;
    289     }
    290 
    291     /**
    292      * Returns the type of audio focus request configured for this {@code AudioFocusRequest}.
    293      * @return one of {@link AudioManager#AUDIOFOCUS_GAIN},
    294      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
    295      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
    296      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
    297      */
    298     public int getFocusGain() {
    299         return mFocusGain;
    300     }
    301 
    302     /**
    303      * Returns whether the application that would use this {@code AudioFocusRequest} would pause
    304      * when it is requested to duck.
    305      * @return the duck/pause behavior.
    306      */
    307     public boolean willPauseWhenDucked() {
    308         return (mFlags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
    309                 == AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS;
    310     }
    311 
    312     /**
    313      * Returns whether the application that would use this {@code AudioFocusRequest} supports
    314      * a focus gain granted after a temporary request failure.
    315      * @return whether delayed focus gain is supported.
    316      */
    317     public boolean acceptsDelayedFocusGain() {
    318         return (mFlags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK)
    319                 == AudioManager.AUDIOFOCUS_FLAG_DELAY_OK;
    320     }
    321 
    322     /**
    323      * @hide
    324      * Returns whether audio focus will be locked (i.e. focus cannot change) as a result of this
    325      * focus request being successful.
    326      * @return whether this request will lock focus.
    327      */
    328     @SystemApi
    329     public boolean locksFocus() {
    330         return (mFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK)
    331                 == AudioManager.AUDIOFOCUS_FLAG_LOCK;
    332     }
    333 
    334     int getFlags() {
    335         return mFlags;
    336     }
    337 
    338     /**
    339      * Builder class for {@link AudioFocusRequest} objects.
    340      * <p>See {@link AudioFocusRequest} for an example of building an instance with this builder.
    341      * <br>The default values for the instance to be built are:
    342      * <table>
    343      * <tr><td>focus listener and handler</td><td>none</td></tr>
    344      * <tr><td>{@code AudioAttributes}</td><td>attributes with usage set to
    345      *     {@link AudioAttributes#USAGE_MEDIA}</td></tr>
    346      * <tr><td>pauses on duck</td><td>false</td></tr>
    347      * <tr><td>supports delayed focus grant</td><td>false</td></tr>
    348      * </table>
    349      */
    350     public static final class Builder {
    351         private OnAudioFocusChangeListener mFocusListener;
    352         private Handler mListenerHandler;
    353         private AudioAttributes mAttr = FOCUS_DEFAULT_ATTR;
    354         private int mFocusGain;
    355         private boolean mPausesOnDuck = false;
    356         private boolean mDelayedFocus = false;
    357         private boolean mFocusLocked = false;
    358         private boolean mA11yForceDucking = false;
    359 
    360         /**
    361          * Constructs a new {@code Builder}, and specifies how audio focus
    362          * will be requested. Valid values for focus requests are
    363          * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
    364          * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
    365          * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
    366          * <p>By default there is no focus change listener, delayed focus is not supported, ducking
    367          * is suitable for the application, and the <code>AudioAttributes</code>
    368          * have a usage of {@link AudioAttributes#USAGE_MEDIA}.
    369          * @param focusGain the type of audio focus gain that will be requested
    370          * @throws IllegalArgumentException thrown when an invalid focus gain type is used
    371          */
    372         public Builder(int focusGain) {
    373             setFocusGain(focusGain);
    374         }
    375 
    376         /**
    377          * Constructs a new {@code Builder} with all the properties of the {@code AudioFocusRequest}
    378          * passed as parameter.
    379          * Use this method when you want a new request to differ only by some properties.
    380          * @param requestToCopy the non-null {@code AudioFocusRequest} to build a duplicate from.
    381          * @throws IllegalArgumentException thrown when a null {@code AudioFocusRequest} is used.
    382          */
    383         public Builder(@NonNull AudioFocusRequest requestToCopy) {
    384             if (requestToCopy == null) {
    385                 throw new IllegalArgumentException("Illegal null AudioFocusRequest");
    386             }
    387             mAttr = requestToCopy.mAttr;
    388             mFocusListener = requestToCopy.mFocusListener;
    389             mListenerHandler = requestToCopy.mListenerHandler;
    390             mFocusGain = requestToCopy.mFocusGain;
    391             mPausesOnDuck = requestToCopy.willPauseWhenDucked();
    392             mDelayedFocus = requestToCopy.acceptsDelayedFocusGain();
    393         }
    394 
    395         /**
    396          * Sets the type of focus gain that will be requested.
    397          * Use this method to replace the focus gain when building a request by modifying an
    398          * existing {@code AudioFocusRequest} instance.
    399          * @param focusGain the type of audio focus gain that will be requested.
    400          * @return this {@code Builder} instance
    401          * @throws IllegalArgumentException thrown when an invalid focus gain type is used
    402          */
    403         public @NonNull Builder setFocusGain(int focusGain) {
    404             if (!isValidFocusGain(focusGain)) {
    405                 throw new IllegalArgumentException("Illegal audio focus gain type " + focusGain);
    406             }
    407             mFocusGain = focusGain;
    408             return this;
    409         }
    410 
    411         /**
    412          * Sets the listener called when audio focus changes after being requested with
    413          *   {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned
    414          *   with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
    415          *   Note that only focus changes (gains and losses) affecting the focus owner are reported,
    416          *   not gains and losses of other focus requesters in the system.<br>
    417          *   Notifications are delivered on the {@link Looper} associated with the one of
    418          *   the creation of the {@link AudioManager} used to request focus
    419          *   (see {@link AudioManager#requestAudioFocus(AudioFocusRequest)}).
    420          * @param listener the listener receiving the focus change notifications.
    421          * @return this {@code Builder} instance.
    422          * @throws NullPointerException thrown when a null focus listener is used.
    423          */
    424         public @NonNull Builder setOnAudioFocusChangeListener(
    425                 @NonNull OnAudioFocusChangeListener listener) {
    426             if (listener == null) {
    427                 throw new NullPointerException("Illegal null focus listener");
    428             }
    429             mFocusListener = listener;
    430             mListenerHandler = null;
    431             return this;
    432         }
    433 
    434         /**
    435          * @hide
    436          * Internal listener setter, no null checks on listener nor handler
    437          * @param listener
    438          * @param handler
    439          * @return this {@code Builder} instance.
    440          */
    441         @NonNull Builder setOnAudioFocusChangeListenerInt(
    442                 OnAudioFocusChangeListener listener, Handler handler) {
    443             mFocusListener = listener;
    444             mListenerHandler = handler;
    445             return this;
    446         }
    447 
    448         /**
    449          * Sets the listener called when audio focus changes after being requested with
    450          *   {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned
    451          *   with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
    452          *   Note that only focus changes (gains and losses) affecting the focus owner are reported,
    453          *   not gains and losses of other focus requesters in the system.
    454          * @param listener the listener receiving the focus change notifications.
    455          * @param handler the {@link Handler} for the thread on which to execute
    456          *   the notifications.
    457          * @return this {@code Builder} instance.
    458          * @throws NullPointerException thrown when a null focus listener or handler is used.
    459          */
    460         public @NonNull Builder setOnAudioFocusChangeListener(
    461                 @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) {
    462             if (listener == null || handler == null) {
    463                 throw new NullPointerException("Illegal null focus listener or handler");
    464             }
    465             mFocusListener = listener;
    466             mListenerHandler = handler;
    467             return this;
    468         }
    469 
    470         /**
    471          * Sets the {@link AudioAttributes} to be associated with the focus request, and which
    472          * describe the use case for which focus is requested.
    473          * As the focus requests typically precede audio playback, this information is used on
    474          * certain platforms to declare the subsequent playback use case. It is therefore good
    475          * practice to use in this method the same {@code AudioAttributes} as used for
    476          * playback, see for example {@link MediaPlayer#setAudioAttributes(AudioAttributes)} in
    477          * {@code MediaPlayer} or {@link AudioTrack.Builder#setAudioAttributes(AudioAttributes)}
    478          * in {@code AudioTrack}.
    479          * @param attributes the {@link AudioAttributes} for the focus request.
    480          * @return this {@code Builder} instance.
    481          * @throws NullPointerException thrown when using null for the attributes.
    482          */
    483         public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) {
    484             if (attributes == null) {
    485                 throw new NullPointerException("Illegal null AudioAttributes");
    486             }
    487             mAttr = attributes;
    488             return this;
    489         }
    490 
    491         /**
    492          * Declare the intended behavior of the application with regards to audio ducking.
    493          * See more details in the {@link AudioFocusRequest} class documentation.
    494          * @param pauseOnDuck use {@code true} if the application intends to pause audio playback
    495          *    when losing focus with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
    496          *    If {@code true}, note that you must also set a focus listener to receive such an
    497          *    event, with
    498          *    {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}.
    499          * @return this {@code Builder} instance.
    500          */
    501         public @NonNull Builder setWillPauseWhenDucked(boolean pauseOnDuck) {
    502             mPausesOnDuck = pauseOnDuck;
    503             return this;
    504         }
    505 
    506         /**
    507          * Marks this focus request as compatible with delayed focus.
    508          * See more details about delayed focus in the {@link AudioFocusRequest} class
    509          * documentation.
    510          * @param acceptsDelayedFocusGain use {@code true} if the application supports delayed
    511          *    focus. If {@code true}, note that you must also set a focus listener to be notified
    512          *    of delayed focus gain, with
    513          *    {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}.
    514          * @return this {@code Builder} instance
    515          */
    516         public @NonNull Builder setAcceptsDelayedFocusGain(boolean acceptsDelayedFocusGain) {
    517             mDelayedFocus = acceptsDelayedFocusGain;
    518             return this;
    519         }
    520 
    521         /**
    522          * @hide
    523          * Marks this focus request as locking audio focus so granting is temporarily disabled.
    524          * This feature can only be used by owners of a registered
    525          * {@link android.media.audiopolicy.AudioPolicy} in
    526          * {@link AudioManager#requestAudioFocus(AudioFocusRequest, android.media.audiopolicy.AudioPolicy)}.
    527          * Setting to false is the same as the default behavior.
    528          * @param focusLocked true when locking focus
    529          * @return this {@code Builder} instance
    530          */
    531         @SystemApi
    532         public @NonNull Builder setLocksFocus(boolean focusLocked) {
    533             mFocusLocked = focusLocked;
    534             return this;
    535         }
    536 
    537         /**
    538          * Marks this focus request as forcing ducking, regardless of the conditions in which
    539          * the system would or would not enforce ducking.
    540          * Forcing ducking will only be honored when requesting AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
    541          * with an {@link AudioAttributes} usage of
    542          * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}, coming from an accessibility
    543          * service, and will be ignored otherwise.
    544          * @param forceDucking {@code true} to force ducking
    545          * @return this {@code Builder} instance
    546          */
    547         public @NonNull Builder setForceDucking(boolean forceDucking) {
    548             mA11yForceDucking = forceDucking;
    549             return this;
    550         }
    551 
    552         /**
    553          * Builds a new {@code AudioFocusRequest} instance combining all the information gathered
    554          * by this {@code Builder}'s configuration methods.
    555          * @return the {@code AudioFocusRequest} instance qualified by all the properties set
    556          *   on this {@code Builder}.
    557          * @throws IllegalStateException thrown when attempting to build a focus request that is set
    558          *    to accept delayed focus, or to pause on duck, but no focus change listener was set.
    559          */
    560         public AudioFocusRequest build() {
    561             if ((mDelayedFocus || mPausesOnDuck) && (mFocusListener == null)) {
    562                 throw new IllegalStateException(
    563                         "Can't use delayed focus or pause on duck without a listener");
    564             }
    565             if (mA11yForceDucking) {
    566                 final Bundle extraInfo;
    567                 if (mAttr.getBundle() == null) {
    568                     extraInfo = new Bundle();
    569                 } else {
    570                     extraInfo = mAttr.getBundle();
    571                 }
    572                 // checking of usage and focus request is done server side
    573                 extraInfo.putBoolean(KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING, true);
    574                 mAttr = new AudioAttributes.Builder(mAttr).addBundle(extraInfo).build();
    575             }
    576             final int flags = 0
    577                     | (mDelayedFocus ? AudioManager.AUDIOFOCUS_FLAG_DELAY_OK : 0)
    578                     | (mPausesOnDuck ? AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS : 0)
    579                     | (mFocusLocked  ? AudioManager.AUDIOFOCUS_FLAG_LOCK : 0);
    580             return new AudioFocusRequest(mFocusListener, mListenerHandler,
    581                     mAttr, mFocusGain, flags);
    582         }
    583     }
    584 }
    585