Loading media/java/android/media/AudioManager.java +98 −21 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.media; import android.Manifest; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; Loading Loading @@ -2318,14 +2319,25 @@ public class AudioManager { 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 */ @SystemApi public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; /** @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 * 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 * when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}. * @param requestAttributes non null {@link AudioAttributes} describing the main reason for Loading @@ -2340,11 +2352,12 @@ public class AudioManager { * 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 * @param flags 0 or {link #AUDIOFOCUS_FLAG_DELAY_OK}. * <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). * Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted * <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. Loading @@ -2354,10 +2367,55 @@ public class AudioManager { * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag. * @throws IllegalArgumentException */ @SystemApi public int requestAudioFocus(OnAudioFocusChangeListener l, AudioAttributes requestAttributes, @NonNull AudioAttributes requestAttributes, int durationHint, 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 if (requestAttributes == null) { throw new IllegalArgumentException("Illegal null AudioAttributes argument"); Loading @@ -2366,7 +2424,7 @@ public class AudioManager { (durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) { throw new IllegalArgumentException("Invalid duration hint"); } if (flags != (flags & AUDIOFOCUS_FLAGS_ALL)) { if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) { throw new IllegalArgumentException("Illegal flags 0x" + Integer.toHexString(flags).toUpperCase()); } Loading @@ -2374,6 +2432,10 @@ public class AudioManager { throw new IllegalArgumentException( "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; registerAudioFocusListener(l); Loading @@ -2381,9 +2443,10 @@ public class AudioManager { try { status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), mContext.getOpPackageName() /* package name */, flags); mContext.getOpPackageName() /* package name */, flags, ap != null ? ap.token() : null); } 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; } Loading @@ -2405,9 +2468,11 @@ public class AudioManager { .setInternalLegacyStreamType(streamType).build(), durationHint, mICallBack, null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID, mContext.getOpPackageName(), 0 /* flags, legacy behavior*/ ); mContext.getOpPackageName(), AUDIOFOCUS_FLAG_LOCK, null /* policy token */); } 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); } } Loading @@ -2420,9 +2485,10 @@ public class AudioManager { public void abandonAudioFocusForCall() { IAudioService service = getService(); 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) { Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e); Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService:", e); } } Loading @@ -2432,19 +2498,30 @@ public class AudioManager { * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED} */ 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; unregisterAudioFocusListener(l); IAudioService service = getService(); try { status = service.abandonAudioFocus(mAudioFocusDispatcher, getIdForAudioFocusListener(l)); getIdForAudioFocusListener(l), aa); } 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; } //==================================================================== // Remote Control /** Loading media/java/android/media/AudioService.java +29 −4 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import android.hardware.usb.UsbManager; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicyConfig; import android.os.Binder; import android.os.Build; Loading Loading @@ -5018,13 +5019,34 @@ public class AudioService extends IAudioService.Stub { // Audio Focus //========================================================================================== 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, clientId, callingPackageName, flags); } public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) { return mMediaFocusControl.abandonAudioFocus(fd, clientId); public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) { return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa); } public void unregisterAudioFocusClient(String clientId) { Loading Loading @@ -5725,6 +5747,9 @@ public class AudioService extends IAudioService.Stub { // 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 * their configurations. Loading @@ -5742,8 +5767,8 @@ public class AudioService extends IAudioService.Stub { public void binderDied() { synchronized (mAudioPolicies) { Log.i(TAG, "audio policy " + mToken + " died"); mAudioPolicies.remove(mToken); disconnectMixes(); mAudioPolicies.remove(mToken); } } Loading media/java/android/media/FocusRequester.java +16 −1 Original line number Diff line number Diff line Loading @@ -83,6 +83,10 @@ class FocusRequester { } } boolean isLockedFocusOwner() { return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0); } boolean hasSameBinder(IBinder ib) { return (mSourceRef != null) && mSourceRef.equals(ib); } Loading @@ -99,6 +103,9 @@ class FocusRequester { return mCallingUid == uid; } String getClientId() { return mClientId; } int getGainRequest() { return mFocusGainRequest; Loading Loading @@ -144,12 +151,20 @@ class FocusRequester { 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) { pw.println(" source:" + mSourceRef + " -- pack: " + mPackageName + " -- client: " + mClientId + " -- gain: " + focusGainToString() + " -- grant: " + mGrantFlags + " -- flags: " + flagsToString(mGrantFlags) + " -- loss: " + focusLossToString() + " -- uid: " + mCallingUid + " -- attr: " + mAttributes); Loading media/java/android/media/IAudioService.aidl +3 −2 Original line number Diff line number Diff line Loading @@ -118,9 +118,10 @@ interface IAudioService { boolean isBluetoothA2dpOn(); 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); Loading media/java/android/media/MediaFocusControl.java +20 −16 Original line number Diff line number Diff line Loading @@ -390,7 +390,8 @@ public class MediaFocusControl implements OnFinished { // 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 * entering and exiting calls. */ Loading Loading @@ -539,40 +540,40 @@ 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. * 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 * 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() && isExclusiveFocusOwner(mFocusStack.peek())) { if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { return false; } return true; } private boolean isExclusiveFocusOwner(FocusRequester fr) { return fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID); private boolean isLockedFocusOwner(FocusRequester fr) { return (fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner()); } /** * 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 * 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 * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} */ private int pushBelowExclusiveFocusOwners(FocusRequester nfr) { int lastExclusiveFocusOwnerIndex = mFocusStack.size(); private int pushBelowLockedFocusOwners(FocusRequester nfr) { int lastLockedFocusOwnerIndex = mFocusStack.size(); for (int index = mFocusStack.size()-1; index >= 0; index--) { if (isExclusiveFocusOwner(mFocusStack.elementAt(index))) { lastExclusiveFocusOwnerIndex = index; if (isLockedFocusOwner(mFocusStack.elementAt(index))) { lastLockedFocusOwnerIndex = index; } } if (lastExclusiveFocusOwnerIndex == mFocusStack.size()) { if (lastLockedFocusOwnerIndex == 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()); Loading @@ -581,7 +582,7 @@ public class MediaFocusControl implements OnFinished { mFocusStack.push(nfr); return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } else { mFocusStack.insertElementAt(nfr, lastExclusiveFocusOwnerIndex); mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex); return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; } } Loading Loading @@ -687,7 +688,7 @@ public class MediaFocusControl implements OnFinished { if (focusGrantDelayed) { // focusGrantDelayed being true implies we can't reassign focus right now // which implies the focus stack is not empty. return pushBelowExclusiveFocusOwners(nfr); return pushBelowLockedFocusOwners(nfr); } else { // propagate the focus change through the stack if (!mFocusStack.empty()) { Loading @@ -703,8 +704,11 @@ public class MediaFocusControl implements OnFinished { 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); try { // this will take care of notifying the new focus owner if needed Loading Loading
media/java/android/media/AudioManager.java +98 −21 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.media; import android.Manifest; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; Loading Loading @@ -2318,14 +2319,25 @@ public class AudioManager { 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 */ @SystemApi public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; /** @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 * 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 * when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}. * @param requestAttributes non null {@link AudioAttributes} describing the main reason for Loading @@ -2340,11 +2352,12 @@ public class AudioManager { * 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 * @param flags 0 or {link #AUDIOFOCUS_FLAG_DELAY_OK}. * <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). * Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted * <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. Loading @@ -2354,10 +2367,55 @@ public class AudioManager { * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag. * @throws IllegalArgumentException */ @SystemApi public int requestAudioFocus(OnAudioFocusChangeListener l, AudioAttributes requestAttributes, @NonNull AudioAttributes requestAttributes, int durationHint, 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 if (requestAttributes == null) { throw new IllegalArgumentException("Illegal null AudioAttributes argument"); Loading @@ -2366,7 +2424,7 @@ public class AudioManager { (durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) { throw new IllegalArgumentException("Invalid duration hint"); } if (flags != (flags & AUDIOFOCUS_FLAGS_ALL)) { if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) { throw new IllegalArgumentException("Illegal flags 0x" + Integer.toHexString(flags).toUpperCase()); } Loading @@ -2374,6 +2432,10 @@ public class AudioManager { throw new IllegalArgumentException( "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; registerAudioFocusListener(l); Loading @@ -2381,9 +2443,10 @@ public class AudioManager { try { status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), mContext.getOpPackageName() /* package name */, flags); mContext.getOpPackageName() /* package name */, flags, ap != null ? ap.token() : null); } 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; } Loading @@ -2405,9 +2468,11 @@ public class AudioManager { .setInternalLegacyStreamType(streamType).build(), durationHint, mICallBack, null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID, mContext.getOpPackageName(), 0 /* flags, legacy behavior*/ ); mContext.getOpPackageName(), AUDIOFOCUS_FLAG_LOCK, null /* policy token */); } 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); } } Loading @@ -2420,9 +2485,10 @@ public class AudioManager { public void abandonAudioFocusForCall() { IAudioService service = getService(); 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) { Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e); Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService:", e); } } Loading @@ -2432,19 +2498,30 @@ public class AudioManager { * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED} */ 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; unregisterAudioFocusListener(l); IAudioService service = getService(); try { status = service.abandonAudioFocus(mAudioFocusDispatcher, getIdForAudioFocusListener(l)); getIdForAudioFocusListener(l), aa); } 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; } //==================================================================== // Remote Control /** Loading
media/java/android/media/AudioService.java +29 −4 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import android.hardware.usb.UsbManager; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicyConfig; import android.os.Binder; import android.os.Build; Loading Loading @@ -5018,13 +5019,34 @@ public class AudioService extends IAudioService.Stub { // Audio Focus //========================================================================================== 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, clientId, callingPackageName, flags); } public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) { return mMediaFocusControl.abandonAudioFocus(fd, clientId); public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) { return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa); } public void unregisterAudioFocusClient(String clientId) { Loading Loading @@ -5725,6 +5747,9 @@ public class AudioService extends IAudioService.Stub { // 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 * their configurations. Loading @@ -5742,8 +5767,8 @@ public class AudioService extends IAudioService.Stub { public void binderDied() { synchronized (mAudioPolicies) { Log.i(TAG, "audio policy " + mToken + " died"); mAudioPolicies.remove(mToken); disconnectMixes(); mAudioPolicies.remove(mToken); } } Loading
media/java/android/media/FocusRequester.java +16 −1 Original line number Diff line number Diff line Loading @@ -83,6 +83,10 @@ class FocusRequester { } } boolean isLockedFocusOwner() { return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0); } boolean hasSameBinder(IBinder ib) { return (mSourceRef != null) && mSourceRef.equals(ib); } Loading @@ -99,6 +103,9 @@ class FocusRequester { return mCallingUid == uid; } String getClientId() { return mClientId; } int getGainRequest() { return mFocusGainRequest; Loading Loading @@ -144,12 +151,20 @@ class FocusRequester { 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) { pw.println(" source:" + mSourceRef + " -- pack: " + mPackageName + " -- client: " + mClientId + " -- gain: " + focusGainToString() + " -- grant: " + mGrantFlags + " -- flags: " + flagsToString(mGrantFlags) + " -- loss: " + focusLossToString() + " -- uid: " + mCallingUid + " -- attr: " + mAttributes); Loading
media/java/android/media/IAudioService.aidl +3 −2 Original line number Diff line number Diff line Loading @@ -118,9 +118,10 @@ interface IAudioService { boolean isBluetoothA2dpOn(); 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); Loading
media/java/android/media/MediaFocusControl.java +20 −16 Original line number Diff line number Diff line Loading @@ -390,7 +390,8 @@ public class MediaFocusControl implements OnFinished { // 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 * entering and exiting calls. */ Loading Loading @@ -539,40 +540,40 @@ 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. * 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 * 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() && isExclusiveFocusOwner(mFocusStack.peek())) { if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { return false; } return true; } private boolean isExclusiveFocusOwner(FocusRequester fr) { return fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID); private boolean isLockedFocusOwner(FocusRequester fr) { return (fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner()); } /** * 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 * 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 * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} */ private int pushBelowExclusiveFocusOwners(FocusRequester nfr) { int lastExclusiveFocusOwnerIndex = mFocusStack.size(); private int pushBelowLockedFocusOwners(FocusRequester nfr) { int lastLockedFocusOwnerIndex = mFocusStack.size(); for (int index = mFocusStack.size()-1; index >= 0; index--) { if (isExclusiveFocusOwner(mFocusStack.elementAt(index))) { lastExclusiveFocusOwnerIndex = index; if (isLockedFocusOwner(mFocusStack.elementAt(index))) { lastLockedFocusOwnerIndex = index; } } if (lastExclusiveFocusOwnerIndex == mFocusStack.size()) { if (lastLockedFocusOwnerIndex == 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()); Loading @@ -581,7 +582,7 @@ public class MediaFocusControl implements OnFinished { mFocusStack.push(nfr); return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } else { mFocusStack.insertElementAt(nfr, lastExclusiveFocusOwnerIndex); mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex); return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; } } Loading Loading @@ -687,7 +688,7 @@ public class MediaFocusControl implements OnFinished { if (focusGrantDelayed) { // focusGrantDelayed being true implies we can't reassign focus right now // which implies the focus stack is not empty. return pushBelowExclusiveFocusOwners(nfr); return pushBelowLockedFocusOwners(nfr); } else { // propagate the focus change through the stack if (!mFocusStack.empty()) { Loading @@ -703,8 +704,11 @@ public class MediaFocusControl implements OnFinished { 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); try { // this will take care of notifying the new focus owner if needed Loading