am 213472d7: am 8c816096: Merge "Add support for audio focus locking" into lmp-mr1-dev

* commit '213472d7384c9d8096748f3cd9305ef49342dbcf':
  Add support for audio focus locking
This commit is contained in:
Jean-Michel Trivi
2014-11-25 17:49:46 +00:00
committed by Android Git Automerger
5 changed files with 166 additions and 44 deletions

View File

@ -17,6 +17,7 @@
package android.media; package android.media;
import android.Manifest; import android.Manifest;
import android.annotation.NonNull;
import android.annotation.SdkConstant; import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi; import android.annotation.SystemApi;
@ -2318,14 +2319,25 @@ public class AudioManager {
return status; return status;
} }
// when adding new flags, add them to AUDIOFOCUS_FLAGS_ALL // when adding new flags, add them to the relevant AUDIOFOCUS_FLAGS_APPS or SYSTEM masks
/** @hide */ /** @hide */
@SystemApi
public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0;
/** @hide */ /** @hide */
public static final int AUDIOFOCUS_FLAGS_ALL = AUDIOFOCUS_FLAG_DELAY_OK; @SystemApi
public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 1;
/** @hide */
public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK;
/** @hide */
public static final int AUDIOFOCUS_FLAGS_SYSTEM = AUDIOFOCUS_FLAG_DELAY_OK
| AUDIOFOCUS_FLAG_LOCK;
/** /**
* @hide * @hide
* Request audio focus.
* Send a request to obtain the audio focus. This method differs from
* {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)} in that it can express
* that the requester accepts delayed grants of audio focus.
* @param l the listener to be notified of audio focus changes. It is not allowed to be null * @param l the listener to be notified of audio focus changes. It is not allowed to be null
* when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}. * when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}.
* @param requestAttributes non null {@link AudioAttributes} describing the main reason for * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
@ -2340,24 +2352,70 @@ public class AudioManager {
* usecases such as voice memo recording, or speech recognition. * usecases such as voice memo recording, or speech recognition.
* Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
* as the playback of a song or a video. * as the playback of a song or a video.
* @param flags use 0 when not using any flags for the request, which behaves like * @param flags 0 or {link #AUDIOFOCUS_FLAG_DELAY_OK}.
* {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio * <br>Use 0 when not using any flags for the request, which behaves like
* focus is granted immediately, or the grant request fails because the system is in a * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
* state where focus cannot change (e.g. a phone call). * focus is granted immediately, or the grant request fails because the system is in a
* Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted * state where focus cannot change (e.g. a phone call).
* audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when * <br>Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted
* the system is in a state where focus cannot change, but be granted focus later when * audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
* this condition ends. * the system is in a state where focus cannot change, but be granted focus later when
* this condition ends.
* @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED} * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
* or {@link #AUDIOFOCUS_REQUEST_DELAYED}. * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
* The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested * The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested
* without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag. * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag.
* @throws IllegalArgumentException * @throws IllegalArgumentException
*/ */
@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l, public int requestAudioFocus(OnAudioFocusChangeListener l,
AudioAttributes requestAttributes, @NonNull AudioAttributes requestAttributes,
int durationHint, int durationHint,
int flags) throws IllegalArgumentException { int flags) throws IllegalArgumentException {
if (flags != (flags & AUDIOFOCUS_FLAGS_APPS)) {
throw new IllegalArgumentException("Invalid flags 0x"
+ Integer.toHexString(flags).toUpperCase());
}
return requestAudioFocus(l, requestAttributes, durationHint,
flags & AUDIOFOCUS_FLAGS_APPS,
null /* no AudioPolicy*/);
}
/**
* @hide
* Request or lock audio focus.
* This method is to be used by system components that have registered an
* {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
* so focus granting is temporarily disabled.
* @param l see the description of the same parameter in
* {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
* @param requestAttributes non null {@link AudioAttributes} describing the main reason for
* requesting audio focus.
* @param durationHint see the description of the same parameter in
* {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
* @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
* {@link #AUDIOFOCUS_FLAG_LOCK}
* <br>Use 0 when not using any flags for the request, which behaves like
* {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
* focus is granted immediately, or the grant request fails because the system is in a
* state where focus cannot change (e.g. a phone call).
* <br>Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted
* audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
* the system is in a state where focus cannot change, but be granted focus later when
* this condition ends.
* <br>Use {@link #AUDIOFOCUS_FLAG_LOCK} when locking audio focus so granting is
* temporarily disabled.
* @param ap a registered {@link android.media.audiopolicy.AudioPolicy} instance when locking
* focus, or null.
* @return see the description of the same return value in
* {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
* @throws IllegalArgumentException
*/
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
int flags,
AudioPolicy ap) throws IllegalArgumentException {
// parameter checking // parameter checking
if (requestAttributes == null) { if (requestAttributes == null) {
throw new IllegalArgumentException("Illegal null AudioAttributes argument"); throw new IllegalArgumentException("Illegal null AudioAttributes argument");
@ -2366,7 +2424,7 @@ public class AudioManager {
(durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) { (durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
throw new IllegalArgumentException("Invalid duration hint"); throw new IllegalArgumentException("Invalid duration hint");
} }
if (flags != (flags & AUDIOFOCUS_FLAGS_ALL)) { if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
throw new IllegalArgumentException("Illegal flags 0x" throw new IllegalArgumentException("Illegal flags 0x"
+ Integer.toHexString(flags).toUpperCase()); + Integer.toHexString(flags).toUpperCase());
} }
@ -2374,6 +2432,10 @@ public class AudioManager {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Illegal null focus listener when flagged as accepting delayed focus grant"); "Illegal null focus listener when flagged as accepting delayed focus grant");
} }
if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {
throw new IllegalArgumentException(
"Illegal null audio policy when locking audio focus");
}
int status = AUDIOFOCUS_REQUEST_FAILED; int status = AUDIOFOCUS_REQUEST_FAILED;
registerAudioFocusListener(l); registerAudioFocusListener(l);
@ -2381,9 +2443,10 @@ public class AudioManager {
try { try {
status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l), mAudioFocusDispatcher, getIdForAudioFocusListener(l),
mContext.getOpPackageName() /* package name */, flags); mContext.getOpPackageName() /* package name */, flags,
ap != null ? ap.token() : null);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocus() on AudioService due to "+e); Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);
} }
return status; return status;
} }
@ -2405,9 +2468,11 @@ public class AudioManager {
.setInternalLegacyStreamType(streamType).build(), .setInternalLegacyStreamType(streamType).build(),
durationHint, mICallBack, null, durationHint, mICallBack, null,
MediaFocusControl.IN_VOICE_COMM_FOCUS_ID, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID,
mContext.getOpPackageName(), 0 /* flags, legacy behavior*/ ); mContext.getOpPackageName(),
AUDIOFOCUS_FLAG_LOCK,
null /* policy token */);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e); Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService:", e);
} }
} }
@ -2420,9 +2485,10 @@ public class AudioManager {
public void abandonAudioFocusForCall() { public void abandonAudioFocusForCall() {
IAudioService service = getService(); IAudioService service = getService();
try { try {
service.abandonAudioFocus(null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID); service.abandonAudioFocus(null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID,
null /*AudioAttributes, legacy behavior*/);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e); Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService:", e);
} }
} }
@ -2432,19 +2498,30 @@ public class AudioManager {
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED} * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
*/ */
public int abandonAudioFocus(OnAudioFocusChangeListener l) { public int abandonAudioFocus(OnAudioFocusChangeListener l) {
return abandonAudioFocus(l, null /*AudioAttributes, legacy behavior*/);
}
/**
* @hide
* Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
* @param l the listener with which focus was requested.
* @param aa the {@link AudioAttributes} with which audio focus was requested
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
*/
@SystemApi
public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
int status = AUDIOFOCUS_REQUEST_FAILED; int status = AUDIOFOCUS_REQUEST_FAILED;
unregisterAudioFocusListener(l); unregisterAudioFocusListener(l);
IAudioService service = getService(); IAudioService service = getService();
try { try {
status = service.abandonAudioFocus(mAudioFocusDispatcher, status = service.abandonAudioFocus(mAudioFocusDispatcher,
getIdForAudioFocusListener(l)); getIdForAudioFocusListener(l), aa);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Can't call abandonAudioFocus() on AudioService due to "+e); Log.e(TAG, "Can't call abandonAudioFocus() on AudioService:", e);
} }
return status; return status;
} }
//==================================================================== //====================================================================
// Remote Control // Remote Control
/** /**

View File

@ -49,6 +49,7 @@ import android.hardware.usb.UsbManager;
import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnErrorListener;
import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicyConfig; import android.media.audiopolicy.AudioPolicyConfig;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
@ -5018,13 +5019,34 @@ public class AudioService extends IAudioService.Stub {
// Audio Focus // Audio Focus
//========================================================================================== //==========================================================================================
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
IBinder policyToken) {
// permission checks
if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
if (mMediaFocusControl.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)) {
Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
} else {
// only a registered audio policy can be used to lock focus
synchronized (mAudioPolicies) {
if (!mAudioPolicies.containsKey(policyToken)) {
Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus",
new Exception());
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
}
}
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, flags); clientId, callingPackageName, flags);
} }
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) { public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) {
return mMediaFocusControl.abandonAudioFocus(fd, clientId); return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa);
} }
public void unregisterAudioFocusClient(String clientId) { public void unregisterAudioFocusClient(String clientId) {
@ -5725,6 +5747,9 @@ public class AudioService extends IAudioService.Stub {
// TODO implement clearing mix attribute matching info in native audio policy // TODO implement clearing mix attribute matching info in native audio policy
} }
//======================
// Audio policy proxy
//======================
/** /**
* This internal class inherits from AudioPolicyConfig which contains all the mixes and * This internal class inherits from AudioPolicyConfig which contains all the mixes and
* their configurations. * their configurations.
@ -5742,8 +5767,8 @@ public class AudioService extends IAudioService.Stub {
public void binderDied() { public void binderDied() {
synchronized (mAudioPolicies) { synchronized (mAudioPolicies) {
Log.i(TAG, "audio policy " + mToken + " died"); Log.i(TAG, "audio policy " + mToken + " died");
mAudioPolicies.remove(mToken);
disconnectMixes(); disconnectMixes();
mAudioPolicies.remove(mToken);
} }
} }

View File

@ -83,6 +83,10 @@ class FocusRequester {
} }
} }
boolean isLockedFocusOwner() {
return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
}
boolean hasSameBinder(IBinder ib) { boolean hasSameBinder(IBinder ib) {
return (mSourceRef != null) && mSourceRef.equals(ib); return (mSourceRef != null) && mSourceRef.equals(ib);
} }
@ -99,6 +103,9 @@ class FocusRequester {
return mCallingUid == uid; return mCallingUid == uid;
} }
String getClientId() {
return mClientId;
}
int getGainRequest() { int getGainRequest() {
return mFocusGainRequest; return mFocusGainRequest;
@ -144,12 +151,20 @@ class FocusRequester {
return focusChangeToString(mFocusLossReceived); return focusChangeToString(mFocusLossReceived);
} }
private static String flagsToString(int flags) {
String msg = new String();
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) { msg += "DELAY_OK"; }
if (!msg.isEmpty()) { msg += "|"; }
if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0) { msg += "LOCK"; }
return msg;
}
void dump(PrintWriter pw) { void dump(PrintWriter pw) {
pw.println(" source:" + mSourceRef pw.println(" source:" + mSourceRef
+ " -- pack: " + mPackageName + " -- pack: " + mPackageName
+ " -- client: " + mClientId + " -- client: " + mClientId
+ " -- gain: " + focusGainToString() + " -- gain: " + focusGainToString()
+ " -- grant: " + mGrantFlags + " -- flags: " + flagsToString(mGrantFlags)
+ " -- loss: " + focusLossToString() + " -- loss: " + focusLossToString()
+ " -- uid: " + mCallingUid + " -- uid: " + mCallingUid
+ " -- attr: " + mAttributes); + " -- attr: " + mAttributes);

View File

@ -118,9 +118,10 @@ interface IAudioService {
boolean isBluetoothA2dpOn(); boolean isBluetoothA2dpOn();
int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb, int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags); IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
IBinder policyToken);
int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId); int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa);
void unregisterAudioFocusClient(String clientId); void unregisterAudioFocusClient(String clientId);

View File

@ -390,7 +390,8 @@ public class MediaFocusControl implements OnFinished {
// AudioFocus // AudioFocus
//========================================================================================== //==========================================================================================
/* constant to identify focus stack entry that is used to hold the focus while the phone /**
* Constant to identify a focus stack entry that is used to hold the focus while the phone
* is ringing or during a call. Used by com.android.internal.telephony.CallManager when * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
* entering and exiting calls. * entering and exiting calls.
*/ */
@ -539,40 +540,40 @@ public class MediaFocusControl implements OnFinished {
* Helper function: * Helper function:
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise. * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
* The implementation guarantees that a state where focus cannot be immediately reassigned * The implementation guarantees that a state where focus cannot be immediately reassigned
* implies that an "exclusive" focus owner is at the top of the focus stack. * implies that an "locked" focus owner is at the top of the focus stack.
* Modifications to the implementation that break this assumption will cause focus requests to * Modifications to the implementation that break this assumption will cause focus requests to
* misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
*/ */
private boolean canReassignAudioFocus() { private boolean canReassignAudioFocus() {
// focus requests are rejected during a phone call or when the phone is ringing // focus requests are rejected during a phone call or when the phone is ringing
// this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
if (!mFocusStack.isEmpty() && isExclusiveFocusOwner(mFocusStack.peek())) { if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
return false; return false;
} }
return true; return true;
} }
private boolean isExclusiveFocusOwner(FocusRequester fr) { private boolean isLockedFocusOwner(FocusRequester fr) {
return fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID); return (fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
} }
/** /**
* Helper function * Helper function
* Pre-conditions: focus stack is not empty, there is one or more exclusive focus owner * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
* at the top of the focus stack * at the top of the focus stack
* Push the focus requester onto the audio focus stack at the first position immediately * Push the focus requester onto the audio focus stack at the first position immediately
* following the exclusive focus owners. * following the locked focus owners.
* @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
* {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
*/ */
private int pushBelowExclusiveFocusOwners(FocusRequester nfr) { private int pushBelowLockedFocusOwners(FocusRequester nfr) {
int lastExclusiveFocusOwnerIndex = mFocusStack.size(); int lastLockedFocusOwnerIndex = mFocusStack.size();
for (int index = mFocusStack.size()-1; index >= 0; index--) { for (int index = mFocusStack.size()-1; index >= 0; index--) {
if (isExclusiveFocusOwner(mFocusStack.elementAt(index))) { if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
lastExclusiveFocusOwnerIndex = index; lastLockedFocusOwnerIndex = index;
} }
} }
if (lastExclusiveFocusOwnerIndex == mFocusStack.size()) { if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
// this should not happen, but handle it and log an error // this should not happen, but handle it and log an error
Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
new Exception()); new Exception());
@ -581,7 +582,7 @@ public class MediaFocusControl implements OnFinished {
mFocusStack.push(nfr); mFocusStack.push(nfr);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
} else { } else {
mFocusStack.insertElementAt(nfr, lastExclusiveFocusOwnerIndex); mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
} }
} }
@ -687,7 +688,7 @@ public class MediaFocusControl implements OnFinished {
if (focusGrantDelayed) { if (focusGrantDelayed) {
// focusGrantDelayed being true implies we can't reassign focus right now // focusGrantDelayed being true implies we can't reassign focus right now
// which implies the focus stack is not empty. // which implies the focus stack is not empty.
return pushBelowExclusiveFocusOwners(nfr); return pushBelowLockedFocusOwners(nfr);
} else { } else {
// propagate the focus change through the stack // propagate the focus change through the stack
if (!mFocusStack.empty()) { if (!mFocusStack.empty()) {
@ -703,8 +704,11 @@ public class MediaFocusControl implements OnFinished {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
} }
/** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */ /**
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
* */
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) {
// AudioAttributes are currently ignored, to be used for zones
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
try { try {
// this will take care of notifying the new focus owner if needed // this will take care of notifying the new focus owner if needed