Loading media/java/android/media/AudioManager.java +88 −7 Original line number Diff line number Diff line Loading @@ -2201,6 +2201,8 @@ public class AudioManager { listener = findFocusListener((String)msg.obj); } if (listener != null) { Log.d(TAG, "AudioManager dispatching onAudioFocusChange(" + msg.what + ") for " + msg.obj); listener.onAudioFocusChange(msg.what); } } Loading Loading @@ -2270,6 +2272,14 @@ public class AudioManager { * A successful focus change request. */ public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; /** * @hide * A focus change request whose granting is delayed: the request was successful, but the * requester will only be granted audio focus once the condition that prevented immediate * granting has ended. * See {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)} */ public static final int AUDIOFOCUS_REQUEST_DELAYED = 2; /** Loading @@ -2291,18 +2301,87 @@ public class AudioManager { */ public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) { int status = AUDIOFOCUS_REQUEST_FAILED; try { // status is guaranteed to be either AUDIOFOCUS_REQUEST_FAILED or // AUDIOFOCUS_REQUEST_GRANTED as focus is requested without the // AUDIOFOCUS_FLAG_DELAY_OK flag status = requestAudioFocus(l, new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, 0 /* flags, legacy behavior */); } catch (IllegalArgumentException e) { Log.e(TAG, "Audio focus request denied due to ", e); } return status; } // when adding new flags, add them to AUDIOFOCUS_FLAGS_ALL /** @hide */ public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; /** @hide */ public static final int AUDIOFOCUS_FLAGS_ALL = AUDIOFOCUS_FLAG_DELAY_OK; /** * @hide * @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}. * @param requestAttributes non null {@link AudioAttributes} describing the main reason for * requesting audio focus. * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request * is temporary, and focus will be abandonned shortly. Examples of transient requests are * for the playback of driving directions, or notifications sounds. * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for * the previous focus owner to keep playing if it ducks its audio output. * Alternatively use {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} for a temporary request * that benefits from the system not playing disruptive sounds like notifications, for * usecases such as voice memo recording, or speech recognition. * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such * as the playback of a song or a video. * @param flags 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). * 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. * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED} * or {@link #AUDIOFOCUS_REQUEST_DELAYED}. * The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag. * @throws IllegalArgumentException */ public int requestAudioFocus(OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags) throws IllegalArgumentException { // parameter checking if (requestAttributes == null) { throw new IllegalArgumentException("Illegal null AudioAttributes argument"); } if ((durationHint < AUDIOFOCUS_GAIN) || (durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) { Log.e(TAG, "Invalid duration hint, audio focus request denied"); return status; throw new IllegalArgumentException("Invalid duration hint"); } if (flags != (flags & AUDIOFOCUS_FLAGS_ALL)) { throw new IllegalArgumentException("Illegal flags 0x" + Integer.toHexString(flags).toUpperCase()); } if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) { throw new IllegalArgumentException( "Illegal null focus listener when flagged as accepting delayed focus grant"); } int status = AUDIOFOCUS_REQUEST_FAILED; registerAudioFocusListener(l); //TODO protect request by permission check? IAudioService service = getService(); try { status = service.requestAudioFocus(streamType, durationHint, mICallBack, status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), mContext.getOpPackageName() /* package name */); mContext.getOpPackageName() /* package name */, flags); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocus() on AudioService due to "+e); } Loading @@ -2322,9 +2401,11 @@ public class AudioManager { public void requestAudioFocusForCall(int streamType, int durationHint) { IAudioService service = getService(); try { service.requestAudioFocus(streamType, durationHint, mICallBack, null, service.requestAudioFocus(new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, mICallBack, null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID, mContext.getOpPackageName()); mContext.getOpPackageName(), 0 /* flags, legacy behavior*/ ); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e); } Loading media/java/android/media/AudioService.java +4 −4 Original line number Diff line number Diff line Loading @@ -5011,10 +5011,10 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== // Audio Focus //========================================================================================== public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName) { return mMediaFocusControl.requestAudioFocus(mainStreamType, durationHint, cb, fd, clientId, callingPackageName); public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags); } public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) { Loading media/java/android/media/FocusRequester.java +18 −7 Original line number Diff line number Diff line Loading @@ -44,20 +44,25 @@ class FocusRequester { * the audio focus gain request that caused the addition of this object in the focus stack. */ private final int mFocusGainRequest; /** * the flags associated with the gain request that qualify the type of grant (e.g. accepting * delay vs grant must be immediate) */ private final int mGrantFlags; /** * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if * it never lost focus. */ private int mFocusLossReceived; /** * the stream type associated with the focus request * the audio attributes associated with the focus request */ private final int mStreamType; private final AudioAttributes mAttributes; FocusRequester(int streamType, int focusRequest, FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid) { mStreamType = streamType; mAttributes = aa; mFocusDispatcher = afl; mSourceRef = source; mClientId = id; Loading @@ -65,6 +70,7 @@ class FocusRequester { mPackageName = pn; mCallingUid = uid; mFocusGainRequest = focusRequest; mGrantFlags = grantFlags; mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; } Loading Loading @@ -98,8 +104,12 @@ class FocusRequester { return mFocusGainRequest; } int getStreamType() { return mStreamType; int getGrantFlags() { return mGrantFlags; } AudioAttributes getAudioAttributes() { return mAttributes; } Loading Loading @@ -139,9 +149,10 @@ class FocusRequester { + " -- pack: " + mPackageName + " -- client: " + mClientId + " -- gain: " + focusGainToString() + " -- grant: " + mGrantFlags + " -- loss: " + focusLossToString() + " -- uid: " + mCallingUid + " -- stream: " + mStreamType); + " -- attr: " + mAttributes); } Loading media/java/android/media/IAudioService.aidl +3 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.AudioAttributes; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; Loading Loading @@ -116,8 +117,8 @@ interface IAudioService { boolean isBluetoothA2dpOn(); int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName); int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags); int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId); Loading media/java/android/media/MediaFocusControl.java +74 −16 Original line number Diff line number Diff line Loading @@ -538,16 +538,54 @@ public class MediaFocusControl implements OnFinished { /** * Helper function: * 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 * implies that an "exclusive" focus owner is at the top of the focus stack. * Modifications to the implementation that break this assumption will cause focus requests to * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. */ private boolean canReassignAudioFocus() { // 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 if (!mFocusStack.isEmpty() && mFocusStack.peek().hasSameClient(IN_VOICE_COMM_FOCUS_ID)) { if (!mFocusStack.isEmpty() && isExclusiveFocusOwner(mFocusStack.peek())) { return false; } return true; } private boolean isExclusiveFocusOwner(FocusRequester fr) { return fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID); } /** * Helper function * Pre-conditions: focus stack is not empty, there is one or more exclusive focus owner * at the top of the focus stack * Push the focus requester onto the audio focus stack at the first position immediately * following the exclusive focus owners. * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} */ private int pushBelowExclusiveFocusOwners(FocusRequester nfr) { int lastExclusiveFocusOwnerIndex = mFocusStack.size(); for (int index = mFocusStack.size()-1; index >= 0; index--) { if (isExclusiveFocusOwner(mFocusStack.elementAt(index))) { lastExclusiveFocusOwnerIndex = index; } } if (lastExclusiveFocusOwnerIndex == mFocusStack.size()) { // this should not happen, but handle it and log an error Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", new Exception()); // no exclusive owner, push at top of stack, focus is granted, propagate change propagateFocusLossFromGain_syncAf(nfr.getGainRequest()); mFocusStack.push(nfr); return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } else { mFocusStack.insertElementAt(nfr, lastExclusiveFocusOwnerIndex); return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; } } /** * Inner class to monitor audio focus client deaths, and remove them from the audio focus * stack if necessary. Loading Loading @@ -581,10 +619,11 @@ public class MediaFocusControl implements OnFinished { } } /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */ protected int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName) { Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */ protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId + " req=" + focusChangeHint + "flags=0x" + Integer.toHexString(flags)); // we need a valid binder callback for clients if (!cb.pingBinder()) { Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); Loading @@ -597,8 +636,16 @@ public class MediaFocusControl implements OnFinished { } synchronized(mAudioFocusLock) { boolean focusGrantDelayed = false; if (!canReassignAudioFocus()) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } else { // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be // granted right now, so the requester will be inserted in the focus stack // to receive focus later focusGrantDelayed = true; } } // handle the potential premature death of the new holder of the focus Loading @@ -616,7 +663,8 @@ public class MediaFocusControl implements OnFinished { if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { // if focus is already owned by this client and the reason for acquiring the focus // hasn't changed, don't do anything if (mFocusStack.peek().getGainRequest() == focusChangeHint) { final FocusRequester fr = mFocusStack.peek(); if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { // unlink death handler so it can be gc'ed. // linkToDeath() creates a JNI global reference preventing collection. cb.unlinkToDeath(afdh, 0); Loading @@ -624,21 +672,31 @@ public class MediaFocusControl implements OnFinished { } // the reason for the audio focus request has changed: remove the current top of // stack and respond as if we had a new focus owner FocusRequester fr = mFocusStack.pop(); if (!focusGrantDelayed) { mFocusStack.pop(); // the entry that was "popped" is the same that was "peeked" above fr.release(); } } // focus requester might already be somewhere below in the stack, remove it removeFocusStackEntry(clientId, false /* signal */); final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid()); if (focusGrantDelayed) { // focusGrantDelayed being true implies we can't reassign focus right now // which implies the focus stack is not empty. return pushBelowExclusiveFocusOwners(nfr); } else { // propagate the focus change through the stack if (!mFocusStack.empty()) { propagateFocusLossFromGain_syncAf(focusChangeHint); } // push focus requester at the top of the audio focus stack mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid())); mFocusStack.push(nfr); } }//synchronized(mAudioFocusLock) Loading Loading
media/java/android/media/AudioManager.java +88 −7 Original line number Diff line number Diff line Loading @@ -2201,6 +2201,8 @@ public class AudioManager { listener = findFocusListener((String)msg.obj); } if (listener != null) { Log.d(TAG, "AudioManager dispatching onAudioFocusChange(" + msg.what + ") for " + msg.obj); listener.onAudioFocusChange(msg.what); } } Loading Loading @@ -2270,6 +2272,14 @@ public class AudioManager { * A successful focus change request. */ public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; /** * @hide * A focus change request whose granting is delayed: the request was successful, but the * requester will only be granted audio focus once the condition that prevented immediate * granting has ended. * See {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)} */ public static final int AUDIOFOCUS_REQUEST_DELAYED = 2; /** Loading @@ -2291,18 +2301,87 @@ public class AudioManager { */ public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) { int status = AUDIOFOCUS_REQUEST_FAILED; try { // status is guaranteed to be either AUDIOFOCUS_REQUEST_FAILED or // AUDIOFOCUS_REQUEST_GRANTED as focus is requested without the // AUDIOFOCUS_FLAG_DELAY_OK flag status = requestAudioFocus(l, new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, 0 /* flags, legacy behavior */); } catch (IllegalArgumentException e) { Log.e(TAG, "Audio focus request denied due to ", e); } return status; } // when adding new flags, add them to AUDIOFOCUS_FLAGS_ALL /** @hide */ public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; /** @hide */ public static final int AUDIOFOCUS_FLAGS_ALL = AUDIOFOCUS_FLAG_DELAY_OK; /** * @hide * @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}. * @param requestAttributes non null {@link AudioAttributes} describing the main reason for * requesting audio focus. * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request * is temporary, and focus will be abandonned shortly. Examples of transient requests are * for the playback of driving directions, or notifications sounds. * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for * the previous focus owner to keep playing if it ducks its audio output. * Alternatively use {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} for a temporary request * that benefits from the system not playing disruptive sounds like notifications, for * usecases such as voice memo recording, or speech recognition. * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such * as the playback of a song or a video. * @param flags 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). * 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. * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED} * or {@link #AUDIOFOCUS_REQUEST_DELAYED}. * The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag. * @throws IllegalArgumentException */ public int requestAudioFocus(OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags) throws IllegalArgumentException { // parameter checking if (requestAttributes == null) { throw new IllegalArgumentException("Illegal null AudioAttributes argument"); } if ((durationHint < AUDIOFOCUS_GAIN) || (durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) { Log.e(TAG, "Invalid duration hint, audio focus request denied"); return status; throw new IllegalArgumentException("Invalid duration hint"); } if (flags != (flags & AUDIOFOCUS_FLAGS_ALL)) { throw new IllegalArgumentException("Illegal flags 0x" + Integer.toHexString(flags).toUpperCase()); } if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) { throw new IllegalArgumentException( "Illegal null focus listener when flagged as accepting delayed focus grant"); } int status = AUDIOFOCUS_REQUEST_FAILED; registerAudioFocusListener(l); //TODO protect request by permission check? IAudioService service = getService(); try { status = service.requestAudioFocus(streamType, durationHint, mICallBack, status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), mContext.getOpPackageName() /* package name */); mContext.getOpPackageName() /* package name */, flags); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocus() on AudioService due to "+e); } Loading @@ -2322,9 +2401,11 @@ public class AudioManager { public void requestAudioFocusForCall(int streamType, int durationHint) { IAudioService service = getService(); try { service.requestAudioFocus(streamType, durationHint, mICallBack, null, service.requestAudioFocus(new AudioAttributes.Builder() .setInternalLegacyStreamType(streamType).build(), durationHint, mICallBack, null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID, mContext.getOpPackageName()); mContext.getOpPackageName(), 0 /* flags, legacy behavior*/ ); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e); } Loading
media/java/android/media/AudioService.java +4 −4 Original line number Diff line number Diff line Loading @@ -5011,10 +5011,10 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== // Audio Focus //========================================================================================== public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName) { return mMediaFocusControl.requestAudioFocus(mainStreamType, durationHint, cb, fd, clientId, callingPackageName); public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags); } public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) { Loading
media/java/android/media/FocusRequester.java +18 −7 Original line number Diff line number Diff line Loading @@ -44,20 +44,25 @@ class FocusRequester { * the audio focus gain request that caused the addition of this object in the focus stack. */ private final int mFocusGainRequest; /** * the flags associated with the gain request that qualify the type of grant (e.g. accepting * delay vs grant must be immediate) */ private final int mGrantFlags; /** * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if * it never lost focus. */ private int mFocusLossReceived; /** * the stream type associated with the focus request * the audio attributes associated with the focus request */ private final int mStreamType; private final AudioAttributes mAttributes; FocusRequester(int streamType, int focusRequest, FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid) { mStreamType = streamType; mAttributes = aa; mFocusDispatcher = afl; mSourceRef = source; mClientId = id; Loading @@ -65,6 +70,7 @@ class FocusRequester { mPackageName = pn; mCallingUid = uid; mFocusGainRequest = focusRequest; mGrantFlags = grantFlags; mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; } Loading Loading @@ -98,8 +104,12 @@ class FocusRequester { return mFocusGainRequest; } int getStreamType() { return mStreamType; int getGrantFlags() { return mGrantFlags; } AudioAttributes getAudioAttributes() { return mAttributes; } Loading Loading @@ -139,9 +149,10 @@ class FocusRequester { + " -- pack: " + mPackageName + " -- client: " + mClientId + " -- gain: " + focusGainToString() + " -- grant: " + mGrantFlags + " -- loss: " + focusLossToString() + " -- uid: " + mCallingUid + " -- stream: " + mStreamType); + " -- attr: " + mAttributes); } Loading
media/java/android/media/IAudioService.aidl +3 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.media; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.AudioAttributes; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; Loading Loading @@ -116,8 +117,8 @@ interface IAudioService { boolean isBluetoothA2dpOn(); int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName); int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags); int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId); Loading
media/java/android/media/MediaFocusControl.java +74 −16 Original line number Diff line number Diff line Loading @@ -538,16 +538,54 @@ public class MediaFocusControl implements OnFinished { /** * Helper function: * 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 * implies that an "exclusive" focus owner is at the top of the focus stack. * Modifications to the implementation that break this assumption will cause focus requests to * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. */ private boolean canReassignAudioFocus() { // 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 if (!mFocusStack.isEmpty() && mFocusStack.peek().hasSameClient(IN_VOICE_COMM_FOCUS_ID)) { if (!mFocusStack.isEmpty() && isExclusiveFocusOwner(mFocusStack.peek())) { return false; } return true; } private boolean isExclusiveFocusOwner(FocusRequester fr) { return fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID); } /** * Helper function * Pre-conditions: focus stack is not empty, there is one or more exclusive focus owner * at the top of the focus stack * Push the focus requester onto the audio focus stack at the first position immediately * following the exclusive focus owners. * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} */ private int pushBelowExclusiveFocusOwners(FocusRequester nfr) { int lastExclusiveFocusOwnerIndex = mFocusStack.size(); for (int index = mFocusStack.size()-1; index >= 0; index--) { if (isExclusiveFocusOwner(mFocusStack.elementAt(index))) { lastExclusiveFocusOwnerIndex = index; } } if (lastExclusiveFocusOwnerIndex == mFocusStack.size()) { // this should not happen, but handle it and log an error Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", new Exception()); // no exclusive owner, push at top of stack, focus is granted, propagate change propagateFocusLossFromGain_syncAf(nfr.getGainRequest()); mFocusStack.push(nfr); return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } else { mFocusStack.insertElementAt(nfr, lastExclusiveFocusOwnerIndex); return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; } } /** * Inner class to monitor audio focus client deaths, and remove them from the audio focus * stack if necessary. Loading Loading @@ -581,10 +619,11 @@ public class MediaFocusControl implements OnFinished { } } /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */ protected int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName) { Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */ protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId + " req=" + focusChangeHint + "flags=0x" + Integer.toHexString(flags)); // we need a valid binder callback for clients if (!cb.pingBinder()) { Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); Loading @@ -597,8 +636,16 @@ public class MediaFocusControl implements OnFinished { } synchronized(mAudioFocusLock) { boolean focusGrantDelayed = false; if (!canReassignAudioFocus()) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } else { // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be // granted right now, so the requester will be inserted in the focus stack // to receive focus later focusGrantDelayed = true; } } // handle the potential premature death of the new holder of the focus Loading @@ -616,7 +663,8 @@ public class MediaFocusControl implements OnFinished { if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { // if focus is already owned by this client and the reason for acquiring the focus // hasn't changed, don't do anything if (mFocusStack.peek().getGainRequest() == focusChangeHint) { final FocusRequester fr = mFocusStack.peek(); if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { // unlink death handler so it can be gc'ed. // linkToDeath() creates a JNI global reference preventing collection. cb.unlinkToDeath(afdh, 0); Loading @@ -624,21 +672,31 @@ public class MediaFocusControl implements OnFinished { } // the reason for the audio focus request has changed: remove the current top of // stack and respond as if we had a new focus owner FocusRequester fr = mFocusStack.pop(); if (!focusGrantDelayed) { mFocusStack.pop(); // the entry that was "popped" is the same that was "peeked" above fr.release(); } } // focus requester might already be somewhere below in the stack, remove it removeFocusStackEntry(clientId, false /* signal */); final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid()); if (focusGrantDelayed) { // focusGrantDelayed being true implies we can't reassign focus right now // which implies the focus stack is not empty. return pushBelowExclusiveFocusOwners(nfr); } else { // propagate the focus change through the stack if (!mFocusStack.empty()) { propagateFocusLossFromGain_syncAf(focusChangeHint); } // push focus requester at the top of the audio focus stack mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid())); mFocusStack.push(nfr); } }//synchronized(mAudioFocusLock) Loading