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

Commit 3be08c72 authored by Joseph Pirozzo's avatar Joseph Pirozzo
Browse files

Prepare Media Session

Update A2DP Sink such that when the associated media session is prepared
audio focus is acquired and streaming is permitted to begin from either
the phone or car until another app takes permanent media focus.

Bug: 69833231
Test: Manually run all associated music apps.
Change-Id: I9e180b0558d80b8f4185c6cc724aeaf833891df5
parent 6b66d887
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -228,6 +228,19 @@ public class A2dpSinkService extends ProfileService {
        }
    }

    /**
     * Called by AVRCP controller to establish audio focus.
     *
     * In order to perform streaming the A2DP sink must have audio focus.  This interface allows the
     * associated MediaSession to inform the sink of intent to play and then allows streaming to be
     * started from either the source or the sink endpoint.
     */
    public void requestAudioFocus(BluetoothDevice device, boolean request) {
        if (mStateMachine != null) {
            mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_REQUEST_FOCUS);
        }
    }

    synchronized boolean isA2dpPlaying(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
        if (DBG) {
+9 −1
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -67,6 +68,7 @@ public class A2dpSinkStateMachine extends StateMachine {
    public static final int EVENT_AVRCP_CT_PAUSE = 302;
    public static final int EVENT_AVRCP_TG_PLAY = 303;
    public static final int EVENT_AVRCP_TG_PAUSE = 304;
    public static final int EVENT_REQUEST_FOCUS = 305;

    private static final int IS_INVALID_DEVICE = 0;
    private static final int IS_VALID_DEVICE = 1;
@@ -500,8 +502,10 @@ public class A2dpSinkStateMachine extends StateMachine {
                    mStreaming = new A2dpSinkStreamHandler(A2dpSinkStateMachine.this, mContext);
                }
            }
            if (mStreaming.getAudioFocus() == AudioManager.AUDIOFOCUS_NONE) {
                informAudioFocusStateNative(0);
            }
        }

        @Override
        public boolean processMessage(Message message) {
@@ -586,6 +590,10 @@ public class A2dpSinkStateMachine extends StateMachine {
                    mStreaming.obtainMessage(A2dpSinkStreamHandler.SRC_PAUSE).sendToTarget();
                    break;

                case EVENT_REQUEST_FOCUS:
                    mStreaming.obtainMessage(A2dpSinkStreamHandler.REQUEST_FOCUS).sendToTarget();
                    break;

                default:
                    return NOT_HANDLED;
            }
+13 −5
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ public class A2dpSinkStreamHandler extends Handler {
    public static final int SRC_PAUSE = 5; // Pause command was generated from remote device
    public static final int DISCONNECT = 6; // Remote device was disconnected
    public static final int AUDIO_FOCUS_CHANGE = 7; // Audio focus callback with associated change
    public static final int REQUEST_FOCUS = 8; // Request focus when the media service is active

    // Used to indicate focus lost
    private static final int STATE_FOCUS_LOST = 0;
@@ -159,11 +160,15 @@ public class A2dpSinkStreamHandler extends Handler {
                stopAvrcpUpdates();
                break;

            case REQUEST_FOCUS:
                if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
                    requestAudioFocus();
                }
                break;

            case DISCONNECT:
                // Remote device has disconnected, restore everything to default state.
                sendAvrcpPause();
                stopAvrcpUpdates();
                abandonAudioFocus();
                mSentPause = false;
                break;

@@ -208,7 +213,6 @@ public class A2dpSinkStreamHandler extends Handler {
                    case AudioManager.AUDIOFOCUS_LOSS:
                        // Permanent loss of focus probably due to another audio app, abandon focus
                        // and stop playback.
                        mAudioFocus = AudioManager.AUDIOFOCUS_NONE;
                        abandonAudioFocus();
                        sendAvrcpPause();
                        break;
@@ -223,7 +227,7 @@ public class A2dpSinkStreamHandler extends Handler {
    /**
     * Utility functions.
     */
    private int requestAudioFocus() {
    private synchronized int requestAudioFocus() {
        // Bluetooth A2DP may carry Music, Audio Books, Navigation, or other sounds so mark content
        // type unknown.
        AudioAttributes streamAttributes =
@@ -249,7 +253,7 @@ public class A2dpSinkStreamHandler extends Handler {
    }


    private void abandonAudioFocus() {
    private synchronized void abandonAudioFocus() {
        stopFluorideStreaming();
        mAudioManager.abandonAudioFocus(mAudioFocusListener);
        mAudioFocus = AudioManager.AUDIOFOCUS_NONE;
@@ -348,6 +352,10 @@ public class A2dpSinkStreamHandler extends Handler {
        }
    }

    synchronized int getAudioFocus() {
        return mAudioFocus;
    }

    private boolean isIotDevice() {
        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
    }
+11 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.util.Log;
import android.util.Pair;

import com.android.bluetooth.R;
import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
import com.android.bluetooth.avrcpcontroller.BrowseTree;

@@ -99,6 +100,7 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
    private AvrcpControllerService mAvrcpCtrlSrvc;
    private boolean mBrowseConnected = false;
    private BluetoothDevice mA2dpDevice = null;
    private A2dpSinkService mA2dpSinkService = null;
    private Handler mAvrcpCommandQueue;
    private final Map<String, Result<List<MediaItem>>> mParentIdToRequestMap = new HashMap<>();

@@ -266,6 +268,14 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
                    AvrcpControllerService.PASS_THRU_CMD_ID_STOP).sendToTarget();
        }

        @Override
        public void onPrepare() {
            Log.d(TAG, "onPrepare");
            if (mA2dpSinkService != null) {
                mA2dpSinkService.requestAudioFocus(mA2dpDevice, true);
            }
        }

        @Override
        public void onRewind() {
            Log.d(TAG, "onRewind");
@@ -390,6 +400,7 @@ public class A2dpMediaBrowserService extends MediaBrowserService {
            return;
        }
        mA2dpDevice = devices.get(0);
        mA2dpSinkService = A2dpSinkService.getA2dpSinkService();

        PlaybackState playbackState = mAvrcpCtrlSrvc.getPlaybackState(mA2dpDevice);
        // Add actions required for playback and rebuild the object.
+2 −2
Original line number Diff line number Diff line
@@ -119,8 +119,8 @@ public class A2dpSinkStreamHandlerTest {
        testSnkPlay();
        mStreamHandler.handleMessage(
                mStreamHandler.obtainMessage(A2dpSinkStreamHandler.DISCONNECT));
        verify(mMockAudioManager, times(1)).abandonAudioFocus(any());
        verify(mMockA2dpSink, times(1)).informAudioFocusStateNative(0);
        verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
        verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(0);
    }

    @Test