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

Commit cf23fb65 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "A2dpSinkService: merge "start" into constructor" into main

parents 2fed14c7 fc8e680a
Loading
Loading
Loading
Loading
+40 −83
Original line number Diff line number Diff line
@@ -27,8 +27,6 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothA2dpSink;
import android.content.AttributionSource;
import android.content.Context;
import android.media.AudioManager;
import android.os.Looper;
import android.sysprop.BluetoothProperties;
import android.util.Log;
@@ -51,62 +49,43 @@ import java.util.concurrent.ConcurrentHashMap;
public class A2dpSinkService extends ProfileService {
    private static final String TAG = A2dpSinkService.class.getSimpleName();

    private static A2dpSinkService sService;

    // This is also used as a lock for shared data in {@link A2dpSinkService}
    @GuardedBy("mDeviceStateMap")
    private final Map<BluetoothDevice, A2dpSinkStateMachine> mDeviceStateMap =
            new ConcurrentHashMap<>(1);

    private final Object mActiveDeviceLock = new Object();
    private final Object mStreamHandlerLock = new Object();

    private final AdapterService mAdapterService;
    private final DatabaseManager mDatabaseManager;
    private final A2dpSinkNativeInterface mNativeInterface;
    private final Looper mLooper;
    private final int mMaxConnectedAudioDevices;

    private final Object mActiveDeviceLock = new Object();
    @GuardedBy("mStreamHandlerLock")
    private final A2dpSinkStreamHandler mA2dpSinkStreamHandler;

    @GuardedBy("mActiveDeviceLock")
    private BluetoothDevice mActiveDevice = null;

    private final Object mStreamHandlerLock = new Object();

    @GuardedBy("mStreamHandlerLock")
    private A2dpSinkStreamHandler mA2dpSinkStreamHandler;

    private static A2dpSinkService sService;

    private int mMaxConnectedAudioDevices;

    private AdapterService mAdapterService;
    private DatabaseManager mDatabaseManager;

    public A2dpSinkService(Context ctx) {
        super(ctx);
        mNativeInterface = requireNonNull(A2dpSinkNativeInterface.getInstance());
        mLooper = Looper.getMainLooper();
    public A2dpSinkService(AdapterService adapterService) {
        this(adapterService, A2dpSinkNativeInterface.getInstance(), Looper.getMainLooper());
    }

    @VisibleForTesting
    A2dpSinkService(Context ctx, A2dpSinkNativeInterface nativeInterface, Looper looper) {
        super(ctx);
    A2dpSinkService(
            AdapterService adapterService, A2dpSinkNativeInterface nativeInterface, Looper looper) {
        super(requireNonNull(adapterService));
        mAdapterService = adapterService;
        mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
        mNativeInterface = requireNonNull(nativeInterface);
        mLooper = looper;
    }

    public static boolean isEnabled() {
        return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
    }

    @Override
    public void start() {
        mAdapterService =
                requireNonNull(
                        AdapterService.getAdapterService(),
                        "AdapterService cannot be null when A2dpSinkService starts");
        mDatabaseManager =
                requireNonNull(
                        AdapterService.getAdapterService().getDatabase(),
                        "DatabaseManager cannot be null when A2dpSinkService starts");

        mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
        mNativeInterface.init(mMaxConnectedAudioDevices);

        synchronized (mStreamHandlerLock) {
            mA2dpSinkStreamHandler = new A2dpSinkStreamHandler(this, mNativeInterface);
        }
@@ -114,6 +93,10 @@ public class A2dpSinkService extends ProfileService {
        setA2dpSinkService(this);
    }

    public static boolean isEnabled() {
        return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
    }

    @Override
    public void stop() {
        setA2dpSinkService(null);
@@ -125,10 +108,7 @@ public class A2dpSinkService extends ProfileService {
            mDeviceStateMap.clear();
        }
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler != null) {
            mA2dpSinkStreamHandler.cleanup();
                mA2dpSinkStreamHandler = null;
            }
        }
    }

@@ -164,7 +144,6 @@ public class A2dpSinkService extends ProfileService {
    /** Request audio focus such that the designated device can stream audio */
    public void requestAudioFocus(BluetoothDevice device, boolean request) {
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler == null) return;
            mA2dpSinkStreamHandler.requestAudioFocus(request);
        }
    }
@@ -176,17 +155,12 @@ public class A2dpSinkService extends ProfileService {
     */
    public int getFocusState() {
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler == null) return AudioManager.ERROR;
            return mA2dpSinkStreamHandler.getFocusState();
        }
    }

    @RequiresPermission(BLUETOOTH_PRIVILEGED)
    boolean isA2dpPlaying(BluetoothDevice device) {
        enforceCallingOrSelfPermission(
                BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler == null) return false;
            return mA2dpSinkStreamHandler.isPlaying();
        }
    }
@@ -309,6 +283,9 @@ public class A2dpSinkService extends ProfileService {
            if (service == null) {
                return false;
            }

            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);

            return service.isA2dpPlaying(device);
        }

@@ -407,19 +384,14 @@ public class A2dpSinkService extends ProfileService {
    }

    protected A2dpSinkStateMachine getOrCreateStateMachine(BluetoothDevice device) {
        A2dpSinkStateMachine newStateMachine =
                new A2dpSinkStateMachine(mLooper, device, this, mNativeInterface);
        synchronized (mDeviceStateMap) {
            A2dpSinkStateMachine existingStateMachine =
                    mDeviceStateMap.putIfAbsent(device, newStateMachine);
            // Given null is not a valid value in our map, ConcurrentHashMap will return null if the
            // key was absent and our new value was added. We should then start and return it. Else
            // we quit the new one so we don't leak a thread
            if (existingStateMachine == null) {
                newStateMachine.start();
                return newStateMachine;
            A2dpSinkStateMachine sm = mDeviceStateMap.get(device);
            if (sm != null) {
                return sm;
            }
            return existingStateMachine;
            sm = new A2dpSinkStateMachine(this, device, mLooper, mNativeInterface);
            mDeviceStateMap.put(device, sm);
            return sm;
        }
    }

@@ -543,18 +515,10 @@ public class A2dpSinkService extends ProfileService {
    /** Receive and route a stack event from the JNI */
    protected void messageFromNative(StackEvent event) {
        switch (event.mType) {
            case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
                onConnectionStateChanged(event);
                return;
            case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
                onAudioStateChanged(event);
                return;
            case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED:
                onAudioConfigChanged(event);
                return;
            default:
                Log.e(TAG, "Received unknown stack event of type " + event.mType);
                return;
            case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> onConnectionStateChanged(event);
            case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED -> onAudioStateChanged(event);
            case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED -> onAudioConfigChanged(event);
            default -> Log.e(TAG, "Received unknown stack event of type " + event.mType);
        }
    }

@@ -570,18 +534,11 @@ public class A2dpSinkService extends ProfileService {
    private void onAudioStateChanged(StackEvent event) {
        int state = event.mState;
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler == null) {
                Log.e(TAG, "Received audio state change before we've been started");
                return;
            } else if (state == StackEvent.AUDIO_STATE_STARTED) {
                mA2dpSinkStreamHandler
                        .obtainMessage(A2dpSinkStreamHandler.SRC_STR_START)
                        .sendToTarget();
            if (state == StackEvent.AUDIO_STATE_STARTED) {
                mA2dpSinkStreamHandler.sendEmptyMessage(A2dpSinkStreamHandler.SRC_STR_START);
            } else if (state == StackEvent.AUDIO_STATE_STOPPED
                    || state == StackEvent.AUDIO_STATE_REMOTE_SUSPEND) {
                mA2dpSinkStreamHandler
                        .obtainMessage(A2dpSinkStreamHandler.SRC_STR_STOP)
                        .sendToTarget();
                mA2dpSinkStreamHandler.sendEmptyMessage(A2dpSinkStreamHandler.SRC_STR_STOP);
            } else {
                Log.w(TAG, "Unhandled audio state change, state=" + state);
            }
+78 −86
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@ package com.android.bluetooth.a2dpsink;

import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;

import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothA2dpSink;
@@ -41,15 +46,15 @@ class A2dpSinkStateMachine extends StateMachine {
    private static final String TAG = A2dpSinkStateMachine.class.getSimpleName();

    // 0->99 Events from Outside
    @VisibleForTesting static final int CONNECT = 1;
    @VisibleForTesting static final int DISCONNECT = 2;
    @VisibleForTesting static final int MESSAGE_CONNECT = 1;
    @VisibleForTesting static final int MESSAGE_DISCONNECT = 2;

    // 100->199 Internal Events
    @VisibleForTesting static final int CLEANUP = 100;
    @VisibleForTesting static final int CONNECT_TIMEOUT = 101;
    @VisibleForTesting static final int MESSAGE_CONNECT_TIMEOUT = 101;

    // 200->299 Events from Native
    @VisibleForTesting static final int STACK_EVENT = 200;
    @VisibleForTesting static final int MESSAGE_STACK_EVENT = 200;

    static final int CONNECT_TIMEOUT_MS = 10000;

@@ -62,13 +67,13 @@ class A2dpSinkStateMachine extends StateMachine {
    protected final Connected mConnected;
    protected final Disconnecting mDisconnecting;

    protected int mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;
    protected int mMostRecentState = STATE_DISCONNECTED;
    protected BluetoothAudioConfig mAudioConfig = null;

    A2dpSinkStateMachine(
            Looper looper,
            BluetoothDevice device,
            A2dpSinkService service,
            BluetoothDevice device,
            Looper looper,
            A2dpSinkNativeInterface nativeInterface) {
        super(TAG, looper);
        mDevice = device;
@@ -88,6 +93,7 @@ class A2dpSinkStateMachine extends StateMachine {

        setInitialState(mDisconnected);
        Log.d(TAG, "[" + mDevice + "] State machine created");
        start();
    }

    /**
@@ -115,17 +121,17 @@ class A2dpSinkStateMachine extends StateMachine {

    /** send the Connect command asynchronously */
    final void connect() {
        sendMessage(CONNECT);
        sendMessage(MESSAGE_CONNECT);
    }

    /** send the Disconnect command asynchronously */
    final void disconnect() {
        sendMessage(DISCONNECT);
        sendMessage(MESSAGE_DISCONNECT);
    }

    /** send the stack event asynchronously */
    final void onStackEvent(StackEvent event) {
        sendMessage(STACK_EVENT, event);
        sendMessage(MESSAGE_STACK_EVENT, event);
    }

    /**
@@ -154,37 +160,36 @@ class A2dpSinkStateMachine extends StateMachine {
        @Override
        public void enter() {
            Log.d(TAG, "[" + mDevice + "] Enter Disconnected");
            if (mMostRecentState != BluetoothProfile.STATE_DISCONNECTED) {
            if (mMostRecentState != STATE_DISCONNECTED) {
                sendMessage(CLEANUP);
            }
            onConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTED);
            onConnectionStateChanged(STATE_DISCONNECTED);
        }

        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case STACK_EVENT:
                    processStackEvent((StackEvent) message.obj);
                    return true;
                case CONNECT:
                case MESSAGE_STACK_EVENT -> processStackEvent((StackEvent) message.obj);
                case MESSAGE_CONNECT -> {
                    Log.d(TAG, "[" + mDevice + "] Connect");
                    transitionTo(mConnecting);
                    return true;
                case CLEANUP:
                    mService.removeStateMachine(A2dpSinkStateMachine.this);
                    return true;
                }
                case CLEANUP -> mService.removeStateMachine(A2dpSinkStateMachine.this);
                default -> {
                    return false;
                }
            }
            return true;
        }

        @RequiresPermission(BLUETOOTH_PRIVILEGED)
        void processStackEvent(StackEvent event) {
            switch (event.mType) {
                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
            if (event.mType != StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
                return;
            }
            switch (event.mState) {
                        case StackEvent.CONNECTION_STATE_CONNECTING:
                            if (mService.getConnectionPolicy(mDevice)
                                    == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
                case STATE_CONNECTING -> {
                    if (mService.getConnectionPolicy(mDevice) == CONNECTION_POLICY_FORBIDDEN) {
                        Log.w(
                                TAG,
                                "["
@@ -196,14 +201,9 @@ class A2dpSinkStateMachine extends StateMachine {
                        mConnecting.mIncomingConnection = true;
                        transitionTo(mConnecting);
                    }
                            break;
                        case StackEvent.CONNECTION_STATE_CONNECTED:
                            transitionTo(mConnected);
                            break;
                        case StackEvent.CONNECTION_STATE_DISCONNECTED:
                            sendMessage(CLEANUP);
                            break;
                }
                case STATE_CONNECTED -> transitionTo(mConnected);
                case STATE_DISCONNECTED -> sendMessage(CLEANUP);
            }
        }
    }
@@ -214,8 +214,8 @@ class A2dpSinkStateMachine extends StateMachine {
        @Override
        public void enter() {
            Log.d(TAG, "[" + mDevice + "] Enter Connecting");
            onConnectionStateChanged(BluetoothProfile.STATE_CONNECTING);
            sendMessageDelayed(CONNECT_TIMEOUT, CONNECT_TIMEOUT_MS);
            onConnectionStateChanged(STATE_CONNECTING);
            sendMessageDelayed(MESSAGE_CONNECT_TIMEOUT, CONNECT_TIMEOUT_MS);

            if (!mIncomingConnection) {
                mNativeInterface.connectA2dpSink(mDevice);
@@ -227,13 +227,9 @@ class A2dpSinkStateMachine extends StateMachine {
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case STACK_EVENT:
                    processStackEvent((StackEvent) message.obj);
                    return true;
                case CONNECT_TIMEOUT:
                    transitionTo(mDisconnected);
                    return true;
                case DISCONNECT:
                case MESSAGE_STACK_EVENT -> processStackEvent((StackEvent) message.obj);
                case MESSAGE_CONNECT_TIMEOUT -> transitionTo(mDisconnected);
                case MESSAGE_DISCONNECT -> {
                    Log.d(
                            TAG,
                            "["
@@ -241,28 +237,27 @@ class A2dpSinkStateMachine extends StateMachine {
                                    + "] Received disconnect message while connecting."
                                    + "deferred");
                    deferMessage(message);
                    return true;
                }
                default -> {
                    return false;
                }
            }
            return true;
        }

        void processStackEvent(StackEvent event) {
            switch (event.mType) {
                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
                    switch (event.mState) {
                        case StackEvent.CONNECTION_STATE_CONNECTED:
                            transitionTo(mConnected);
                            break;
                        case StackEvent.CONNECTION_STATE_DISCONNECTED:
                            transitionTo(mDisconnected);
                            break;
            if (event.mType != StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
                return;
            }
            switch (event.mState) {
                case STATE_CONNECTED -> transitionTo(mConnected);
                case STATE_DISCONNECTED -> transitionTo(mDisconnected);
            }
        }

        @Override
        public void exit() {
            removeMessages(CONNECT_TIMEOUT);
            removeMessages(MESSAGE_CONNECT_TIMEOUT);
            mIncomingConnection = false;
        }
    }
@@ -271,42 +266,39 @@ class A2dpSinkStateMachine extends StateMachine {
        @Override
        public void enter() {
            Log.d(TAG, "[" + mDevice + "] Enter Connected");
            onConnectionStateChanged(BluetoothProfile.STATE_CONNECTED);
            onConnectionStateChanged(STATE_CONNECTED);
        }

        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case DISCONNECT:
                case MESSAGE_DISCONNECT -> {
                    transitionTo(mDisconnecting);
                    mNativeInterface.disconnectA2dpSink(mDevice);
                    return true;
                case STACK_EVENT:
                    processStackEvent((StackEvent) message.obj);
                    return true;
                }
                case MESSAGE_STACK_EVENT -> processStackEvent((StackEvent) message.obj);
                default -> {
                    return false;
                }
            }
            return true;
        }

        void processStackEvent(StackEvent event) {
            switch (event.mType) {
                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> {
                    switch (event.mState) {
                        case StackEvent.CONNECTION_STATE_DISCONNECTING:
                            transitionTo(mDisconnecting);
                            break;
                        case StackEvent.CONNECTION_STATE_DISCONNECTED:
                            transitionTo(mDisconnected);
                            break;
                        case STATE_DISCONNECTING -> transitionTo(mDisconnecting);
                        case STATE_DISCONNECTED -> transitionTo(mDisconnected);
                    }
                    break;
                case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED:
                }
                case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED -> {
                    mAudioConfig =
                            new BluetoothAudioConfig(
                                    event.mSampleRate,
                                    event.mChannelCount,
                                    AudioFormat.ENCODING_PCM_16BIT);
                    break;
                }
            }
        }
    }
@@ -315,7 +307,7 @@ class A2dpSinkStateMachine extends StateMachine {
        @Override
        public void enter() {
            Log.d(TAG, "[" + mDevice + "] Enter Disconnecting");
            onConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING);
            onConnectionStateChanged(STATE_DISCONNECTING);
            transitionTo(mDisconnected);
        }
    }
@@ -324,7 +316,7 @@ class A2dpSinkStateMachine extends StateMachine {
        if (mMostRecentState == currentState) {
            return;
        }
        if (currentState == BluetoothProfile.STATE_CONNECTED) {
        if (currentState == STATE_CONNECTED) {
            MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP_SINK);
        }
        Log.d(TAG, "[" + mDevice + "] Connection state: " + mMostRecentState + "->" + currentState);
+4 −3
Original line number Diff line number Diff line
@@ -70,9 +70,10 @@ public class A2dpSinkStreamHandler extends Handler {
    private static final int STATE_FOCUS_GRANTED = 1;

    // Private variables.
    private A2dpSinkService mA2dpSinkService;
    private A2dpSinkNativeInterface mNativeInterface;
    private AudioManager mAudioManager;
    private final A2dpSinkService mA2dpSinkService;
    private final A2dpSinkNativeInterface mNativeInterface;
    private final AudioManager mAudioManager;

    // Keep track if the remote device is providing audio
    private boolean mStreamAvailable = false;
    // Keep track of the relevant audio focus (None, Transient, Gain)
+0 −6
Original line number Diff line number Diff line
@@ -24,12 +24,6 @@ final class StackEvent {
    static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
    static final int EVENT_TYPE_AUDIO_CONFIG_CHANGED = 3;

    // match up with btav_connection_state_t enum of bt_av.h
    static final int CONNECTION_STATE_DISCONNECTED = 0;
    static final int CONNECTION_STATE_CONNECTING = 1;
    static final int CONNECTION_STATE_CONNECTED = 2;
    static final int CONNECTION_STATE_DISCONNECTING = 3;

    // match up with btav_audio_state_t enum of bt_av.h
    static final int AUDIO_STATE_REMOTE_SUSPEND = 0;
    static final int AUDIO_STATE_STOPPED = 1;
+97 −131

File changed.

Preview size limit exceeded, changes collapsed.

Loading