Loading Android.mk +2 −0 Original line number Diff line number Diff line Loading @@ -330,6 +330,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRemoteVolumeObserver.aidl \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ media/java/android/media/projection/IMediaProjection.aidl \ media/java/android/media/projection/IMediaProjectionCallback.aidl \ media/java/android/media/projection/IMediaProjectionManager.aidl \ Loading Loading @@ -437,6 +438,7 @@ aidl_files := \ frameworks/base/media/java/android/media/MediaDescription.aidl \ frameworks/base/media/java/android/media/Rating.aidl \ frameworks/base/media/java/android/media/AudioAttributes.aidl \ frameworks/base/media/java/android/media/AudioFocusInfo.aidl \ frameworks/base/media/java/android/media/session/PlaybackState.aidl \ frameworks/base/media/java/android/media/session/MediaSession.aidl \ frameworks/base/media/java/android/media/tv/TvInputInfo.aidl \ Loading media/java/android/media/AudioFocusInfo.aidl 0 → 100644 +18 −0 Original line number Diff line number Diff line /* Copyright 2014, 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; parcelable AudioFocusInfo; media/java/android/media/AudioFocusInfo.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 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.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; /** * @hide * A class to encapsulate information about an audio focus owner or request. */ @SystemApi public final class AudioFocusInfo implements Parcelable { private AudioAttributes mAttributes; private String mClientId; private String mPackageName; private int mGainRequest; private int mLossReceived; private int mFlags; /** * Class constructor * @param aa * @param clientId * @param packageName * @param gainRequest * @param lossReceived * @param flags */ AudioFocusInfo(AudioAttributes aa, String clientId, String packageName, int gainRequest, int lossReceived, int flags) { mAttributes = aa == null ? new AudioAttributes.Builder().build() : aa; mClientId = clientId == null ? "" : clientId; mPackageName = packageName == null ? "" : packageName; mGainRequest = gainRequest; mLossReceived = lossReceived; mFlags = flags; } /** * The audio attributes for the audio focus request. * @return non-null {@link AudioAttributes}. */ @SystemApi public AudioAttributes getAttributes() { return mAttributes; } @SystemApi public String getClientId() { return mClientId; } @SystemApi public String getPackageName() { return mPackageName; } /** * The type of audio focus gain request. * @return one of {@link AudioManager#AUDIOFOCUS_GAIN}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. */ @SystemApi public int getGainRequest() { return mGainRequest; } /** * The type of audio focus loss that was received by the * {@link AudioManager.OnAudioFocusChangeListener} if one was set. * @return 0 if focus wasn't lost, or one of {@link AudioManager#AUDIOFOCUS_LOSS}, * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT} or * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. */ @SystemApi public int getLossReceived() { return mLossReceived; } /** @hide */ void clearLossReceived() { mLossReceived = 0; } /** * The flags set in the audio focus request. * @return 0 or a combination of {link AudioManager#AUDIOFOCUS_FLAG_DELAY_OK}, * {@link AudioManager#AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and * {@link AudioManager#AUDIOFOCUS_FLAG_LOCK}. */ @SystemApi public int getFlags() { return mFlags; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { mAttributes.writeToParcel(dest, flags); dest.writeString(mClientId); dest.writeString(mPackageName); dest.writeInt(mGainRequest); dest.writeInt(mLossReceived); dest.writeInt(mFlags); } @SystemApi @Override public int hashCode() { return Objects.hash(mAttributes, mClientId, mPackageName, mGainRequest, mFlags); } @SystemApi @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AudioFocusInfo other = (AudioFocusInfo) obj; if (!mAttributes.equals(other.mAttributes)) { return false; } if (!mClientId.equals(other.mClientId)) { return false; } if (!mPackageName.equals(other.mPackageName)) { return false; } if (mGainRequest != other.mGainRequest) { return false; } if (mLossReceived != other.mLossReceived) { return false; } if (mFlags != other.mFlags) { return false; } return true; } public static final Parcelable.Creator<AudioFocusInfo> CREATOR = new Parcelable.Creator<AudioFocusInfo>() { public AudioFocusInfo createFromParcel(Parcel in) { return new AudioFocusInfo( AudioAttributes.CREATOR.createFromParcel(in), //AudioAttributes aa in.readString(), //String clientId in.readString(), //String packageName in.readInt(), //int gainRequest in.readInt(), //int lossReceived in.readInt() //int flags ); } public AudioFocusInfo[] newArray(int size) { return new AudioFocusInfo[size]; } }; } media/java/android/media/AudioManager.java +37 −20 Original line number Diff line number Diff line Loading @@ -2385,17 +2385,42 @@ public class AudioManager { } // when adding new flags, add them to the relevant AUDIOFOCUS_FLAGS_APPS or SYSTEM masks /** @hide */ /** * @hide * Use this flag when requesting audio focus to indicate 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. */ @SystemApi public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; /** @hide */ /** * @hide * Use this flag when requesting audio focus to indicate that the requester * will pause its media playback (if applicable) when losing audio focus with * {@link #AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, rather than ducking. * <br>On some platforms, the ducking may be handled without the application being aware of it * (i.e. it will not transiently lose focus). For applications that for instance play spoken * content, such as audio book or podcast players, ducking may never be acceptable, and will * thus always pause. This flag enables them to be declared as such whenever they request focus. */ @SystemApi public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 0x1 << 1; /** * @hide * Use this flag to lock audio focus so granting is temporarily disabled. * <br>This flag can only be used by owners of a registered * {@link android.media.audiopolicy.AudioPolicy} in * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int, AudioPolicy)} */ @SystemApi public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 1; public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 2; /** @hide */ public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK; public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS; /** @hide */ public static final int AUDIOFOCUS_FLAGS_SYSTEM = AUDIOFOCUS_FLAG_DELAY_OK | AUDIOFOCUS_FLAG_LOCK; | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS | AUDIOFOCUS_FLAG_LOCK; /** * @hide Loading @@ -2417,15 +2442,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 0 or {link #AUDIOFOCUS_FLAG_DELAY_OK}. * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK} * and {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}. * <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. * @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 Loading Loading @@ -2459,17 +2481,11 @@ public class AudioManager { * @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} * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and {@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 Loading Loading @@ -2510,7 +2526,7 @@ public class AudioManager { status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), mContext.getOpPackageName() /* package name */, flags, ap != null ? ap.token() : null); ap != null ? ap.cb() : null); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e); } Loading Loading @@ -2887,7 +2903,8 @@ public class AudioManager { } IAudioService service = getService(); try { String regId = service.registerAudioPolicy(policy.getConfig(), policy.token()); String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), policy.hasFocusListener()); if (regId == null) { return ERROR; } else { Loading @@ -2912,7 +2929,7 @@ public class AudioManager { } IAudioService service = getService(); try { service.unregisterAudioPolicyAsync(policy.token()); service.unregisterAudioPolicyAsync(policy.cb()); policy.setRegistration(null); } catch (RemoteException e) { Log.e(TAG, "Dead object in unregisterAudioPolicyAsync()", e); Loading media/java/android/media/AudioService.java +88 −26 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import android.media.MediaPlayer.OnErrorListener; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicyConfig; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; import android.os.Environment; Loading Loading @@ -5098,7 +5099,7 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IBinder policyToken) { IAudioPolicyCallback pcb) { // permission checks if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) { if (mMediaFocusControl.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { Loading @@ -5110,9 +5111,8 @@ public class AudioService extends IAudioService.Stub { } 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()); if (!mAudioPolicies.containsKey(pcb.asBinder())) { Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus"); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } Loading Loading @@ -5812,30 +5812,34 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== // Audio policy management //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IBinder cb) { //Log.v(TAG, "registerAudioPolicy for " + cb + " got policy:" + policyConfig); public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, boolean hasFocusListener) { if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder() + " with config:" + policyConfig); String regId = null; // error handling boolean hasPermissionForPolicy = (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission( android.Manifest.permission.MODIFY_AUDIO_ROUTING)); if (!hasPermissionForPolicy) { Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid " + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING"); return null; } synchronized (mAudioPolicies) { try { if (mAudioPolicies.containsKey(cb)) { if (mAudioPolicies.containsKey(pcb.asBinder())) { Slog.e(TAG, "Cannot re-register policy"); return null; } AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb); cb.linkToDeath(app, 0/*flags*/); regId = app.connectMixes(); mAudioPolicies.put(cb, app); AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener); pcb.asBinder().linkToDeath(app, 0/*flags*/); regId = app.getRegistrationId(); mAudioPolicies.put(pcb.asBinder(), app); } catch (RemoteException e) { // audio policy owner has already died! Slog.w(TAG, "Audio policy registration failed, could not link to " + cb + Slog.w(TAG, "Audio policy registration failed, could not link to " + pcb + " binder death", e); return null; } Loading @@ -5843,21 +5847,58 @@ public class AudioService extends IAudioService.Stub { return regId; } public void unregisterAudioPolicyAsync(IBinder cb) { public void unregisterAudioPolicyAsync(IAudioPolicyCallback pcb) { if (DEBUG_AP) Log.d(TAG, "unregisterAudioPolicyAsync for " + pcb.asBinder()); synchronized (mAudioPolicies) { AudioPolicyProxy app = mAudioPolicies.remove(cb); AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder()); if (app == null) { Slog.w(TAG, "Trying to unregister unknown audio policy for pid " + Binder.getCallingPid() + " / uid " + Binder.getCallingUid()); return; } else { cb.unlinkToDeath(app, 0/*flags*/); pcb.asBinder().unlinkToDeath(app, 0/*flags*/); } app.disconnectMixes(); app.release(); } // TODO implement clearing mix attribute matching info in native audio policy } public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) { if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior + " policy " + pcb.asBinder()); // error handling boolean hasPermissionForPolicy = (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission( android.Manifest.permission.MODIFY_AUDIO_ROUTING)); if (!hasPermissionForPolicy) { Slog.w(TAG, "Cannot change audio policy ducking handling for pid " + + Binder.getCallingPid() + " / uid " + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING"); return AudioManager.ERROR; } synchronized (mAudioPolicies) { if (!mAudioPolicies.containsKey(pcb.asBinder())) { Slog.e(TAG, "Cannot change audio policy focus properties, unregistered policy"); return AudioManager.ERROR; } final AudioPolicyProxy app = mAudioPolicies.get(pcb.asBinder()); if (duckingBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { // is there already one policy managing ducking? for(AudioPolicyProxy policy : mAudioPolicies.values()) { if (policy.mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { Slog.e(TAG, "Cannot change audio policy ducking behavior, already handled"); return AudioManager.ERROR; } } } app.mFocusDuckBehavior = duckingBehavior; mMediaFocusControl.setDuckingInExtPolicyAvailable( duckingBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY); } return AudioManager.SUCCESS; } private void dumpAudioPolicies(PrintWriter pw) { pw.println("\nAudio policies:"); synchronized (mAudioPolicies) { Loading @@ -5877,27 +5918,48 @@ public class AudioService extends IAudioService.Stub { public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient { private static final String TAG = "AudioPolicyProxy"; AudioPolicyConfig mConfig; IBinder mToken; AudioPolicyProxy(AudioPolicyConfig config, IBinder token) { IAudioPolicyCallback mPolicyToken; boolean mHasFocusListener; /** * Audio focus ducking behavior for an audio policy. * This variable reflects the value that was successfully set in * {@link AudioService#setFocusPropertiesForPolicy(int, IAudioPolicyCallback)}. This * implies that a value of FOCUS_POLICY_DUCKING_IN_POLICY means the corresponding policy * is handling ducking for audio focus. */ int mFocusDuckBehavior = AudioPolicy.FOCUS_POLICY_DUCKING_DEFAULT; AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token, boolean hasFocusListener) { super(config); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); mToken = token; mPolicyToken = token; mHasFocusListener = hasFocusListener; if (mHasFocusListener) { mMediaFocusControl.addFocusFollower(mPolicyToken); } updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE); } public void binderDied() { synchronized (mAudioPolicies) { Log.i(TAG, "audio policy " + mToken + " died"); disconnectMixes(); mAudioPolicies.remove(mToken); Log.i(TAG, "audio policy " + mPolicyToken + " died"); release(); mAudioPolicies.remove(mPolicyToken.asBinder()); } } String connectMixes() { updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE); String getRegistrationId() { return getRegistration(); } void disconnectMixes() { void release() { if (mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { mMediaFocusControl.setDuckingInExtPolicyAvailable(false); } if (mHasFocusListener) { mMediaFocusControl.removeFocusFollower(mPolicyToken); } updateMixes(AudioSystem.DEVICE_STATE_UNAVAILABLE); } Loading Loading
Android.mk +2 −0 Original line number Diff line number Diff line Loading @@ -330,6 +330,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRemoteVolumeObserver.aidl \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ media/java/android/media/projection/IMediaProjection.aidl \ media/java/android/media/projection/IMediaProjectionCallback.aidl \ media/java/android/media/projection/IMediaProjectionManager.aidl \ Loading Loading @@ -437,6 +438,7 @@ aidl_files := \ frameworks/base/media/java/android/media/MediaDescription.aidl \ frameworks/base/media/java/android/media/Rating.aidl \ frameworks/base/media/java/android/media/AudioAttributes.aidl \ frameworks/base/media/java/android/media/AudioFocusInfo.aidl \ frameworks/base/media/java/android/media/session/PlaybackState.aidl \ frameworks/base/media/java/android/media/session/MediaSession.aidl \ frameworks/base/media/java/android/media/tv/TvInputInfo.aidl \ Loading
media/java/android/media/AudioFocusInfo.aidl 0 → 100644 +18 −0 Original line number Diff line number Diff line /* Copyright 2014, 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; parcelable AudioFocusInfo;
media/java/android/media/AudioFocusInfo.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 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.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; /** * @hide * A class to encapsulate information about an audio focus owner or request. */ @SystemApi public final class AudioFocusInfo implements Parcelable { private AudioAttributes mAttributes; private String mClientId; private String mPackageName; private int mGainRequest; private int mLossReceived; private int mFlags; /** * Class constructor * @param aa * @param clientId * @param packageName * @param gainRequest * @param lossReceived * @param flags */ AudioFocusInfo(AudioAttributes aa, String clientId, String packageName, int gainRequest, int lossReceived, int flags) { mAttributes = aa == null ? new AudioAttributes.Builder().build() : aa; mClientId = clientId == null ? "" : clientId; mPackageName = packageName == null ? "" : packageName; mGainRequest = gainRequest; mLossReceived = lossReceived; mFlags = flags; } /** * The audio attributes for the audio focus request. * @return non-null {@link AudioAttributes}. */ @SystemApi public AudioAttributes getAttributes() { return mAttributes; } @SystemApi public String getClientId() { return mClientId; } @SystemApi public String getPackageName() { return mPackageName; } /** * The type of audio focus gain request. * @return one of {@link AudioManager#AUDIOFOCUS_GAIN}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. */ @SystemApi public int getGainRequest() { return mGainRequest; } /** * The type of audio focus loss that was received by the * {@link AudioManager.OnAudioFocusChangeListener} if one was set. * @return 0 if focus wasn't lost, or one of {@link AudioManager#AUDIOFOCUS_LOSS}, * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT} or * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. */ @SystemApi public int getLossReceived() { return mLossReceived; } /** @hide */ void clearLossReceived() { mLossReceived = 0; } /** * The flags set in the audio focus request. * @return 0 or a combination of {link AudioManager#AUDIOFOCUS_FLAG_DELAY_OK}, * {@link AudioManager#AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and * {@link AudioManager#AUDIOFOCUS_FLAG_LOCK}. */ @SystemApi public int getFlags() { return mFlags; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { mAttributes.writeToParcel(dest, flags); dest.writeString(mClientId); dest.writeString(mPackageName); dest.writeInt(mGainRequest); dest.writeInt(mLossReceived); dest.writeInt(mFlags); } @SystemApi @Override public int hashCode() { return Objects.hash(mAttributes, mClientId, mPackageName, mGainRequest, mFlags); } @SystemApi @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AudioFocusInfo other = (AudioFocusInfo) obj; if (!mAttributes.equals(other.mAttributes)) { return false; } if (!mClientId.equals(other.mClientId)) { return false; } if (!mPackageName.equals(other.mPackageName)) { return false; } if (mGainRequest != other.mGainRequest) { return false; } if (mLossReceived != other.mLossReceived) { return false; } if (mFlags != other.mFlags) { return false; } return true; } public static final Parcelable.Creator<AudioFocusInfo> CREATOR = new Parcelable.Creator<AudioFocusInfo>() { public AudioFocusInfo createFromParcel(Parcel in) { return new AudioFocusInfo( AudioAttributes.CREATOR.createFromParcel(in), //AudioAttributes aa in.readString(), //String clientId in.readString(), //String packageName in.readInt(), //int gainRequest in.readInt(), //int lossReceived in.readInt() //int flags ); } public AudioFocusInfo[] newArray(int size) { return new AudioFocusInfo[size]; } }; }
media/java/android/media/AudioManager.java +37 −20 Original line number Diff line number Diff line Loading @@ -2385,17 +2385,42 @@ public class AudioManager { } // when adding new flags, add them to the relevant AUDIOFOCUS_FLAGS_APPS or SYSTEM masks /** @hide */ /** * @hide * Use this flag when requesting audio focus to indicate 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. */ @SystemApi public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0; /** @hide */ /** * @hide * Use this flag when requesting audio focus to indicate that the requester * will pause its media playback (if applicable) when losing audio focus with * {@link #AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, rather than ducking. * <br>On some platforms, the ducking may be handled without the application being aware of it * (i.e. it will not transiently lose focus). For applications that for instance play spoken * content, such as audio book or podcast players, ducking may never be acceptable, and will * thus always pause. This flag enables them to be declared as such whenever they request focus. */ @SystemApi public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 0x1 << 1; /** * @hide * Use this flag to lock audio focus so granting is temporarily disabled. * <br>This flag can only be used by owners of a registered * {@link android.media.audiopolicy.AudioPolicy} in * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int, AudioPolicy)} */ @SystemApi public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 1; public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 2; /** @hide */ public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK; public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS; /** @hide */ public static final int AUDIOFOCUS_FLAGS_SYSTEM = AUDIOFOCUS_FLAG_DELAY_OK | AUDIOFOCUS_FLAG_LOCK; | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS | AUDIOFOCUS_FLAG_LOCK; /** * @hide Loading @@ -2417,15 +2442,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 0 or {link #AUDIOFOCUS_FLAG_DELAY_OK}. * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK} * and {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}. * <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. * @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 Loading Loading @@ -2459,17 +2481,11 @@ public class AudioManager { * @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} * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and {@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 Loading Loading @@ -2510,7 +2526,7 @@ public class AudioManager { status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack, mAudioFocusDispatcher, getIdForAudioFocusListener(l), mContext.getOpPackageName() /* package name */, flags, ap != null ? ap.token() : null); ap != null ? ap.cb() : null); } catch (RemoteException e) { Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e); } Loading Loading @@ -2887,7 +2903,8 @@ public class AudioManager { } IAudioService service = getService(); try { String regId = service.registerAudioPolicy(policy.getConfig(), policy.token()); String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), policy.hasFocusListener()); if (regId == null) { return ERROR; } else { Loading @@ -2912,7 +2929,7 @@ public class AudioManager { } IAudioService service = getService(); try { service.unregisterAudioPolicyAsync(policy.token()); service.unregisterAudioPolicyAsync(policy.cb()); policy.setRegistration(null); } catch (RemoteException e) { Log.e(TAG, "Dead object in unregisterAudioPolicyAsync()", e); Loading
media/java/android/media/AudioService.java +88 −26 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import android.media.MediaPlayer.OnErrorListener; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicyConfig; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; import android.os.Environment; Loading Loading @@ -5098,7 +5099,7 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IBinder policyToken) { IAudioPolicyCallback pcb) { // permission checks if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) { if (mMediaFocusControl.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { Loading @@ -5110,9 +5111,8 @@ public class AudioService extends IAudioService.Stub { } 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()); if (!mAudioPolicies.containsKey(pcb.asBinder())) { Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus"); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } Loading Loading @@ -5812,30 +5812,34 @@ public class AudioService extends IAudioService.Stub { //========================================================================================== // Audio policy management //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IBinder cb) { //Log.v(TAG, "registerAudioPolicy for " + cb + " got policy:" + policyConfig); public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, boolean hasFocusListener) { if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder() + " with config:" + policyConfig); String regId = null; // error handling boolean hasPermissionForPolicy = (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission( android.Manifest.permission.MODIFY_AUDIO_ROUTING)); if (!hasPermissionForPolicy) { Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid " + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING"); return null; } synchronized (mAudioPolicies) { try { if (mAudioPolicies.containsKey(cb)) { if (mAudioPolicies.containsKey(pcb.asBinder())) { Slog.e(TAG, "Cannot re-register policy"); return null; } AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb); cb.linkToDeath(app, 0/*flags*/); regId = app.connectMixes(); mAudioPolicies.put(cb, app); AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener); pcb.asBinder().linkToDeath(app, 0/*flags*/); regId = app.getRegistrationId(); mAudioPolicies.put(pcb.asBinder(), app); } catch (RemoteException e) { // audio policy owner has already died! Slog.w(TAG, "Audio policy registration failed, could not link to " + cb + Slog.w(TAG, "Audio policy registration failed, could not link to " + pcb + " binder death", e); return null; } Loading @@ -5843,21 +5847,58 @@ public class AudioService extends IAudioService.Stub { return regId; } public void unregisterAudioPolicyAsync(IBinder cb) { public void unregisterAudioPolicyAsync(IAudioPolicyCallback pcb) { if (DEBUG_AP) Log.d(TAG, "unregisterAudioPolicyAsync for " + pcb.asBinder()); synchronized (mAudioPolicies) { AudioPolicyProxy app = mAudioPolicies.remove(cb); AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder()); if (app == null) { Slog.w(TAG, "Trying to unregister unknown audio policy for pid " + Binder.getCallingPid() + " / uid " + Binder.getCallingUid()); return; } else { cb.unlinkToDeath(app, 0/*flags*/); pcb.asBinder().unlinkToDeath(app, 0/*flags*/); } app.disconnectMixes(); app.release(); } // TODO implement clearing mix attribute matching info in native audio policy } public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) { if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior + " policy " + pcb.asBinder()); // error handling boolean hasPermissionForPolicy = (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission( android.Manifest.permission.MODIFY_AUDIO_ROUTING)); if (!hasPermissionForPolicy) { Slog.w(TAG, "Cannot change audio policy ducking handling for pid " + + Binder.getCallingPid() + " / uid " + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING"); return AudioManager.ERROR; } synchronized (mAudioPolicies) { if (!mAudioPolicies.containsKey(pcb.asBinder())) { Slog.e(TAG, "Cannot change audio policy focus properties, unregistered policy"); return AudioManager.ERROR; } final AudioPolicyProxy app = mAudioPolicies.get(pcb.asBinder()); if (duckingBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { // is there already one policy managing ducking? for(AudioPolicyProxy policy : mAudioPolicies.values()) { if (policy.mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { Slog.e(TAG, "Cannot change audio policy ducking behavior, already handled"); return AudioManager.ERROR; } } } app.mFocusDuckBehavior = duckingBehavior; mMediaFocusControl.setDuckingInExtPolicyAvailable( duckingBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY); } return AudioManager.SUCCESS; } private void dumpAudioPolicies(PrintWriter pw) { pw.println("\nAudio policies:"); synchronized (mAudioPolicies) { Loading @@ -5877,27 +5918,48 @@ public class AudioService extends IAudioService.Stub { public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient { private static final String TAG = "AudioPolicyProxy"; AudioPolicyConfig mConfig; IBinder mToken; AudioPolicyProxy(AudioPolicyConfig config, IBinder token) { IAudioPolicyCallback mPolicyToken; boolean mHasFocusListener; /** * Audio focus ducking behavior for an audio policy. * This variable reflects the value that was successfully set in * {@link AudioService#setFocusPropertiesForPolicy(int, IAudioPolicyCallback)}. This * implies that a value of FOCUS_POLICY_DUCKING_IN_POLICY means the corresponding policy * is handling ducking for audio focus. */ int mFocusDuckBehavior = AudioPolicy.FOCUS_POLICY_DUCKING_DEFAULT; AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token, boolean hasFocusListener) { super(config); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); mToken = token; mPolicyToken = token; mHasFocusListener = hasFocusListener; if (mHasFocusListener) { mMediaFocusControl.addFocusFollower(mPolicyToken); } updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE); } public void binderDied() { synchronized (mAudioPolicies) { Log.i(TAG, "audio policy " + mToken + " died"); disconnectMixes(); mAudioPolicies.remove(mToken); Log.i(TAG, "audio policy " + mPolicyToken + " died"); release(); mAudioPolicies.remove(mPolicyToken.asBinder()); } } String connectMixes() { updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE); String getRegistrationId() { return getRegistration(); } void disconnectMixes() { void release() { if (mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) { mMediaFocusControl.setDuckingInExtPolicyAvailable(false); } if (mHasFocusListener) { mMediaFocusControl.removeFocusFollower(mPolicyToken); } updateMixes(AudioSystem.DEVICE_STATE_UNAVAILABLE); } Loading