Sync for HeadsetMediaButton
Add synchronization and deferred execution to our interactions with
MediaSession, since MediaSession sometimes calls back via an RPC into
Telecom and can cause us to deadlock.
Bug: 21028885
Change-Id: I8fc0574269a81e817e1c139aa0fe56258c96bd64
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 845fe7f..24214cb 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -172,7 +172,7 @@
context, statusBarNotifier, mWiredHeadsetManager, mDockManager, this);
InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager, lock);
mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
- mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this);
+ mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
mTtyManager = new TtyManager(context, mWiredHeadsetManager);
mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
diff --git a/src/com/android/server/telecom/HeadsetMediaButton.java b/src/com/android/server/telecom/HeadsetMediaButton.java
index 5d55edd..93dc6de 100644
--- a/src/com/android/server/telecom/HeadsetMediaButton.java
+++ b/src/com/android/server/telecom/HeadsetMediaButton.java
@@ -16,11 +16,13 @@
package com.android.server.telecom;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.media.session.MediaSession;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.view.KeyEvent;
/**
@@ -36,35 +38,72 @@
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
+ private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
+ private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
+
private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
@Override
public boolean onMediaButtonEvent(Intent intent) {
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
Log.v(this, "SessionCallback.onMediaButton()... event = %s.", event);
if ((event != null) && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
- Log.v(this, "SessionCallback: HEADSETHOOK");
- boolean consumed = handleHeadsetHook(event);
- Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
- return consumed;
+ synchronized (mLock) {
+ Log.v(this, "SessionCallback: HEADSETHOOK");
+ boolean consumed = handleHeadsetHook(event);
+ Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
+ return consumed;
+ }
}
return true;
}
};
+ private final Handler mMediaSessionHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_MEDIA_SESSION_INITIALIZE: {
+ MediaSession session = new MediaSession(
+ mContext,
+ HeadsetMediaButton.class.getSimpleName());
+ session.setCallback(mSessionCallback);
+ session.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
+ | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
+ session.setPlaybackToLocal(AUDIO_ATTRIBUTES);
+ mSession = session;
+ break;
+ }
+ case MSG_MEDIA_SESSION_SET_ACTIVE: {
+ if (mSession != null) {
+ boolean activate = msg.arg1 != 0;
+ if (activate != mSession.isActive()) {
+ mSession.setActive(activate);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ };
+
+ private final Context mContext;
private final CallsManager mCallsManager;
+ private final TelecomSystem.SyncRoot mLock;
+ private MediaSession mSession;
- private final MediaSession mSession;
-
- public HeadsetMediaButton(Context context, CallsManager callsManager) {
+ public HeadsetMediaButton(
+ Context context,
+ CallsManager callsManager,
+ TelecomSystem.SyncRoot lock) {
+ mContext = context;
mCallsManager = callsManager;
+ mLock = lock;
// Create a MediaSession but don't enable it yet. This is a
// replacement for MediaButtonReceiver
- mSession = new MediaSession(context, HeadsetMediaButton.class.getSimpleName());
- mSession.setCallback(mSessionCallback);
- mSession.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
- | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
- mSession.setPlaybackToLocal(AUDIO_ATTRIBUTES);
+ mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_INITIALIZE).sendToTarget();
}
/**
@@ -87,18 +126,14 @@
/** ${inheritDoc} */
@Override
public void onCallAdded(Call call) {
- if (!mSession.isActive()) {
- mSession.setActive(true);
- }
+ mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
}
/** ${inheritDoc} */
@Override
public void onCallRemoved(Call call) {
if (!mCallsManager.hasAnyCalls()) {
- if (mSession.isActive()) {
- mSession.setActive(false);
- }
+ mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
}
}
}
diff --git a/src/com/android/server/telecom/HeadsetMediaButtonFactory.java b/src/com/android/server/telecom/HeadsetMediaButtonFactory.java
index becabbf..13d5e4f 100644
--- a/src/com/android/server/telecom/HeadsetMediaButtonFactory.java
+++ b/src/com/android/server/telecom/HeadsetMediaButtonFactory.java
@@ -16,6 +16,8 @@
package com.android.server.telecom;
+import com.android.server.telecom.components.TelecomService;
+
import android.content.Context;
/**
@@ -27,5 +29,8 @@
*/
public interface HeadsetMediaButtonFactory {
- HeadsetMediaButton create(Context context, CallsManager callsManager);
+ HeadsetMediaButton create(
+ Context context,
+ CallsManager callsManager,
+ TelecomSystem.SyncRoot lock);
}
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index f7f4054..529645f 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -78,9 +78,11 @@
},
new HeadsetMediaButtonFactory() {
@Override
- public HeadsetMediaButton create(Context context,
- CallsManager callsManager) {
- return new HeadsetMediaButton(context, callsManager);
+ public HeadsetMediaButton create(
+ Context context,
+ CallsManager callsManager,
+ TelecomSystem.SyncRoot lock) {
+ return new HeadsetMediaButton(context, callsManager, lock);
}
},
new ProximitySensorManagerFactory() {
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 59ca981..b2ee6a2 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -170,7 +170,8 @@
when(headsetMediaButtonFactory.create(
any(Context.class),
- any(CallsManager.class)))
+ any(CallsManager.class),
+ any(TelecomSystem.SyncRoot.class)))
.thenReturn(mHeadsetMediaButton);
when(proximitySensorManagerFactory.create(
any(Context.class),
@@ -191,7 +192,8 @@
verify(headsetMediaButtonFactory).create(
eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
- any(CallsManager.class));
+ any(CallsManager.class),
+ any(TelecomSystem.SyncRoot.class));
verify(proximitySensorManagerFactory).create(
eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
any(CallsManager.class));