From a38e1f4ed997bea3cbfbf8094a52c23570d59d2a Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Tue, 16 Jun 2015 15:28:17 -0700 Subject: [PATCH] Make MediaSessionService not blocked by AudioService. Backgroud: As noted in b/20823981, MediaSessionService calls some audio service methods while holding a lock and the audio service methods also talk to other system services. And, deadlock happens when the other system service fires another request to MediaSessionService while holding its lock. Example1) --- resolved by the change in MediaSessionRecord.java T1: MediaSessionService.dispatchAdjustVolumeLocked() -> MediaSessionRecord.adjustVolume() -> +++AudioServiceInternal.adjustSuggestedStreamVolumeForUid()+++ -> AudioService.adjustSuggestedStreamVolume() -> telecom.TelecomManager.isInCall() --- blocked by lock in TelecomManager. T2: telecom.ConnectionServiceWrapper.handleCreateConnectionComplete() -> MediaSession.setActive() -> MediaSessionRecord$SessionStub.setActive() -> MediaSessionService.updateSession() --- blocked by lock in MediaSessionService. Example2) --- resolved by the change in IAudioService.aidl T1: MediaSessionService.dispatchAdjustVolumeLocked() -> IAudioService.adjustSuggestedStreamVolume() -> AudioService.adjustSuggestedStreamVolume() -> telecom.TelecomManager.isInCall() --- blocked by lock in TelecomManager. T2: telecom.ConnectionServiceWrapper.handleCreateConnectionComplete() -> MediaSession.setActive() -> MediaSessionRecord$SessionStub.setActive() -> MediaSessionService.updateSession() --- blocked by lock in MediaSessionService. Here, this change prevents the deadlock by making related audio IPC oneway and calling the internal audio method without holding the lock. Bug: 20823981 Change-Id: I4c4b2fc796f23d83be67f7edaacd7496c145d985 --- media/java/android/media/IAudioService.aidl | 2 +- .../server/media/MediaSessionRecord.java | 34 +++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 8e9621875f30d..c75c7e5ef9b8a 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -40,7 +40,7 @@ import android.view.KeyEvent; */ interface IAudioService { - void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, + oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller); void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index dca762c9f07f0..569a0fcf72042 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -242,19 +242,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) { int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs); + // Adjust the volume with a handler not to be blocked by other system service. if (useSuggested) { if (AudioSystem.isStreamActive(stream, 0)) { - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, - flags, packageName, uid); + postAdjustSuggestedStreamVolume(stream, direction, flags, packageName, uid); } else { flags |= previousFlagPlaySound; - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( - AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName, - uid); + postAdjustSuggestedStreamVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, direction, + flags, packageName, uid); } } else { - mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, - packageName, uid); + postAdjustStreamVolume(stream, direction, flags, packageName, uid); } } else { if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) { @@ -461,6 +459,28 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return mPackageName + "/" + mTag; } + private void postAdjustSuggestedStreamVolume(final int streamType, final int direction, + final int flags, final String callingPackage, final int uid) { + mHandler.post(new Runnable() { + @Override + public void run() { + mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(streamType, direction, + flags, callingPackage, uid); + } + }); + } + + private void postAdjustStreamVolume(final int streamType, final int direction, final int flags, + final String callingPackage, final int uid) { + mHandler.post(new Runnable() { + @Override + public void run() { + mAudioManagerInternal.adjustStreamVolumeForUid(streamType, direction, flags, + callingPackage, uid); + } + }); + } + private String getShortMetadataString() { int fields = mMetadata == null ? 0 : mMetadata.size(); MediaDescription description = mMetadata == null ? null : mMetadata