Files
frameworks_base/media/java/android/media/AudioFocusRequest.java
Jean-Michel Trivi 50750ff162 Audio focus: SDK/System API updates
Addressed API council notes: document some missing default values
  of AudioFocusRequest builder, remove getters on lister and
  handler.
Fix missing support of custom Handler for focus listener.
Integrate focus lock feature from AudioManager SystemAPI into
  new AudioFocusRequest class and builder. Deprecate system
  methods for focus requests that don't use AudioFocusRequest
Switch to a ConcurrentHashMap in AudioManager instead of a
  lock and HashMap to maintain the list of focus listeners
  for each AudioManager instance.
Clean up: all local IAudioService references can be made final.
  Add missing @Override for the Stub implementations.

Test: cts-tradefed run cts -m CtsMediaTestCases -t android.media.cts.AudioFocusTest
Bug: 30258418

Change-Id: I9e903379a607c723a2620821200547de679231d1
2017-03-22 15:12:05 -07:00

362 lines
16 KiB
Java

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.Handler;
import android.os.Looper;
/**
* A class to encapsulate information about an audio focus request.
* An {@code AudioFocusRequest} instance is built by {@link Builder}, and is used to
* request and abandon audio focus, respectively
* with {@link AudioManager#requestAudioFocus(AudioFocusRequest)} and
* {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
* <p>In the context of describing audio focus, the term "ducking" is used. It describes a temporary
* lowering of the audio level of an application in response to another application playing audio
* concurrently. An example is during the playback of driving directions,
* a user listening to music expects the music to "duck" during the playback of the message
* announcing directions.
*/
// TODO use this class to provide more documentation about audio focus and the new behaviors
// describe up to N, and after.
public final class AudioFocusRequest {
// default attributes for the request when not specified
private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA).build();
private final OnAudioFocusChangeListener mFocusListener; // may be null
private final Handler mListenerHandler; // may be null
private final AudioAttributes mAttr; // never null
private final int mFocusGain;
private final int mFlags;
private AudioFocusRequest(OnAudioFocusChangeListener listener, Handler handler,
AudioAttributes attr, int focusGain, int flags) {
mFocusListener = listener;
mListenerHandler = handler;
mFocusGain = focusGain;
mAttr = attr;
mFlags = flags;
}
/**
* @hide
* Checks whether a focus gain constant is a valid value for an audio focus request.
* @param focusGain value to check
* @return true if focusGain is a valid value for an audio focus request.
*/
final static boolean isValidFocusGain(int focusGain) {
switch (focusGain) {
case AudioManager.AUDIOFOCUS_GAIN:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
return true;
default:
return false;
}
}
/**
* @hide
* Returns the focus change listener set for this {@code AudioFocusRequest}.
* @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set.
*/
public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
return mFocusListener;
}
/**
* @hide
* Returns the {@link Handler} to be used for the focus change listener.
* @return the same {@code Handler} set in.
* {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}, or null
* if no listener was set.
*/
public @Nullable Handler getOnAudioFocusChangeListenerHandler() {
return mListenerHandler;
}
/**
* Returns the {@link AudioAttributes} set for this {@code AudioFocusRequest}, or the default
* attributes if none were set.
* @return non-null {@link AudioAttributes}.
*/
public @NonNull AudioAttributes getAudioAttributes() {
return mAttr;
}
/**
* Returns the type of audio focus request configured for this {@code AudioFocusRequest}.
* @return one of {@link AudioManager#AUDIOFOCUS_GAIN},
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
*/
public int getFocusGain() {
return mFocusGain;
}
/**
* Returns whether the application that would use this {@code AudioFocusRequest} would pause
* when it is requested to duck.
* @return the duck/pause behavior.
*/
public boolean willPauseWhenDucked() {
return (mFlags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
== AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS;
}
/**
* Returns whether the application that would use this {@code AudioFocusRequest} supports
* a focus gain granted after a temporary request failure.
* @return whether delayed focus gain is supported.
*/
public boolean acceptsDelayedFocusGain() {
return (mFlags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK)
== AudioManager.AUDIOFOCUS_FLAG_DELAY_OK;
}
/**
* @hide
* Returns whether audio focus will be locked (i.e. focus cannot change) as a result of this
* focus request being successful.
* @return whether this request will lock focus.
*/
@SystemApi
public boolean locksFocus() {
return (mFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK)
== AudioManager.AUDIOFOCUS_FLAG_LOCK;
}
int getFlags() {
return mFlags;
}
/**
* Builder class for {@link AudioFocusRequest} objects.
* <p> Here is an example where {@code Builder} is used to define the
* {@link AudioFocusRequest} for requesting audio focus:
*
* <pre class="prettyprint">
* mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
* mPlaybackAttributes = new AudioAttributes.Builder()
* .setUsage(AudioAttributes.USAGE_GAME)
* .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
* .build();
* mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
* .setAudioAttributes(mPlaybackAttributes)
* .setAcceptsDelayedFocusGain(true)
* .setOnAudioFocusChangeListener(mMyFocusListener, mMyHandler)
* .build();
* mMediaPlayer = new MediaPlayer();
* ...
* mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
* ...
* boolean mPlaybackAuthorized = true;;
* int res = mAudioManager.requestAudioFocus(mFocusRequest);
* if (res == AUDIOFOCUS_REQUEST_FAILED) {
* mPlaybackAuthorized = false;
* cancelPlayback();
* } else if (res == AUDIOFOCUS_REQUEST_DELAYED) {
* playbackDelayed();
* } else { // res == AUDIOFOCUS_REQUEST_GRANTED
* playbackNow();
* }
* </pre>
*
*/
public static final class Builder {
private OnAudioFocusChangeListener mFocusListener;
private Handler mListenerHandler;
private AudioAttributes mAttr = FOCUS_DEFAULT_ATTR;
private int mFocusGain;
private boolean mPausesOnDuck = false;
private boolean mDelayedFocus = false;
private boolean mFocusLocked = false;
/**
* Constructs a new {@code Builder}, and specifies how audio focus
* will be requested. Valid values for focus requests are
* {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
* <p>By default there is no focus change listener, delayed focus is not supported, ducking
* is suitable for the application, and the <code>AudioAttributes</code>
* have a usage of {@link AudioAttributes#USAGE_MEDIA}.
* @param focusGain the type of audio focus gain that will be requested
* @throws IllegalArgumentException thrown when an invalid focus gain type is used
*/
public Builder(int focusGain) {
setFocusGain(focusGain);
}
/**
* Constructs a new {@code Builder} with all the properties of the {@code AudioFocusRequest}
* passed as parameter.
* Use this method when you want a new request to differ only by some properties.
* @param requestToCopy the non-null {@code AudioFocusRequest} to build a duplicate from.
* @throws IllegalArgumentException thrown when a null {@code AudioFocusRequest} is used.
*/
public Builder(@NonNull AudioFocusRequest requestToCopy) {
if (requestToCopy == null) {
throw new IllegalArgumentException("Illegal null AudioFocusRequest");
}
mAttr = requestToCopy.mAttr;
mFocusListener = requestToCopy.mFocusListener;
mListenerHandler = requestToCopy.mListenerHandler;
mFocusGain = requestToCopy.mFocusGain;
mPausesOnDuck = requestToCopy.willPauseWhenDucked();
mDelayedFocus = requestToCopy.acceptsDelayedFocusGain();
}
/**
* Sets the type of focus gain that will be requested.
* Use this method to replace the focus gain when building a request by modifying an
* existing {@code AudioFocusRequest} instance.
* @param focusGain the type of audio focus gain that will be requested.
* @return this {@code Builder} instance
* @throws IllegalArgumentException thrown when an invalid focus gain type is used
*/
public @NonNull Builder setFocusGain(int focusGain) {
if (!isValidFocusGain(focusGain)) {
throw new IllegalArgumentException("Illegal audio focus gain type " + focusGain);
}
mFocusGain = focusGain;
return this;
}
/**
* Sets the listener called when audio focus changes after being requested with
* {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned
* with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
* Note that only focus changes (gains and losses) affecting the focus owner are reported,
* not gains and losses of other focus requesters in the system.
* @param listener the listener receiving the focus change notifications.
* @param handler the {@link Handler} for the thread on which to execute
* the notifications. If {@code null}, the {@code Handler} associated with the main
* {@link Looper} will be used.
* @return this {@code Builder} instance.
* @throws IllegalArgumentException thrown when a non-null handler is used with a null
* listener.
*/
public @NonNull Builder setOnAudioFocusChangeListener(
@Nullable OnAudioFocusChangeListener listener, @Nullable Handler handler) {
if (listener == null && handler != null) {
throw new IllegalArgumentException(
"Illegal non-null handler without a focus listener");
}
mFocusListener = listener;
mListenerHandler = handler;
return this;
}
/**
* Sets the {@link AudioAttributes} to be associated with the focus request, and which
* describe the use case describing why focus is requested.
* As the focus requests typically precede audio playback, this information is used on
* certain platforms to declare the subsequent playback use case. It is therefore good
* practice to use in this method the same {@code AudioAttributes} as used for
* playback, see for example {@link MediaPlayer#setAudioAttributes(AudioAttributes)} in
* {@code MediaPlayer} or {@link AudioTrack.Builder#setAudioAttributes(AudioAttributes)}
* in {@code AudioTrack}.
* @param attributes the {@link AudioAttributes} for the focus request.
* @return this {@code Builder} instance.
* @throws NullPointerException thrown when using null for the attributes.
*/
public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) {
if (attributes == null) {
throw new NullPointerException("Illegal null AudioAttributes");
}
mAttr = attributes;
return this;
}
/**
* Declare the intended behavior of the application with regards to audio ducking.
* See more details in the {@link AudioFocusRequest} class documentation.
* @param pauseOnDuck use {@code true} if the application intends to pause audio playback
* when losing focus with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
* If {@code true}, note that you must also set a focus listener to receive such an
* event, with
* {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}.
* @return this {@code Builder} instance.
*/
public @NonNull Builder setWillPauseWhenDucked(boolean pauseOnDuck) {
mPausesOnDuck = pauseOnDuck;
return this;
}
/**
* Marks this focus request as compatible with delayed focus.
* See more details about delayed focus in the {@link AudioFocusRequest} class
* documentation.
* @param acceptsDelayedFocusGain use {@code true} if the application supports delayed
* focus. If {@code true}, note that you must also set a focus listener to be notified
* of delayed focus gain, with
* {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}.
* @return this {@code Builder} instance
*/
public @NonNull Builder setAcceptsDelayedFocusGain(boolean acceptsDelayedFocusGain) {
mDelayedFocus = acceptsDelayedFocusGain;
return this;
}
/**
* @hide
* Marks this focus request as locking audio focus so granting is temporarily disabled.
* This feature can only be used by owners of a registered
* {@link android.media.audiopolicy.AudioPolicy} in
* {@link AudioManager#requestAudioFocus(AudioFocusRequest, android.media.audiopolicy.AudioPolicy)}.
* Setting to false is the same as the default behavior.
* @param focusLocked true when locking focus
* @return this {@code Builder} instance
*/
@SystemApi
public @NonNull Builder setLocksFocus(boolean focusLocked) {
mFocusLocked = focusLocked;
return this;
}
/**
* Builds a new {@code AudioFocusRequest} instance combining all the information gathered
* by this {@code Builder}'s configuration methods.
* @return the {@code AudioFocusRequest} instance qualified by all the properties set
* on this {@code Builder}.
* @throws IllegalStateException thrown when attempting to build a focus request that is set
* to accept delayed focus, or to pause on duck, but no focus change listener was set.
*/
public AudioFocusRequest build() {
if ((mDelayedFocus || mPausesOnDuck) && (mFocusListener == null)) {
throw new IllegalStateException(
"Can't use delayed focus or pause on duck without a listener");
}
final int flags = 0
| (mDelayedFocus ? AudioManager.AUDIOFOCUS_FLAG_DELAY_OK : 0)
| (mPausesOnDuck ? AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS : 0)
| (mFocusLocked ? AudioManager.AUDIOFOCUS_FLAG_LOCK : 0);
return new AudioFocusRequest(mFocusListener, mListenerHandler,
mAttr, mFocusGain, flags);
}
}
}