Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 731369c3 authored by Ihab Awad's avatar Ihab Awad
Browse files

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
parent cd9e3e9f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -172,7 +172,7 @@ public class CallsManager extends Call.ListenerBase {
                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);
+54 −19
Original line number Diff line number Diff line
@@ -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 @@ public class HeadsetMediaButton extends CallsManagerListenerBase {
            .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)) {
                synchronized (mLock) {
                    Log.v(this, "SessionCallback: HEADSETHOOK");
                    boolean consumed = handleHeadsetHook(event);
                    Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
                    return consumed;
                }
            }
            return true;
        }
    };

    private final CallsManager mCallsManager;
    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 MediaSession mSession;
    private final Context mContext;
    private final CallsManager mCallsManager;
    private final TelecomSystem.SyncRoot mLock;
    private 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 @@ public class HeadsetMediaButton extends CallsManagerListenerBase {
    /** ${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();
        }
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.telecom;

import com.android.server.telecom.components.TelecomService;

import android.content.Context;

/**
@@ -27,5 +29,8 @@ import android.content.Context;
 */
public interface HeadsetMediaButtonFactory {

    HeadsetMediaButton create(Context context, CallsManager callsManager);
    HeadsetMediaButton create(
            Context context,
            CallsManager callsManager,
            TelecomSystem.SyncRoot lock);
}
+5 −3
Original line number Diff line number Diff line
@@ -78,9 +78,11 @@ public class TelecomService extends Service implements TelecomSystem.Component {
                            },
                            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() {
+4 −2
Original line number Diff line number Diff line
@@ -170,7 +170,8 @@ public class TelecomSystemTest extends TelecomTestCase {

        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 @@ public class TelecomSystemTest extends TelecomTestCase {

        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));