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

Commit f2133c57 authored by Gowtham Anandha Babu's avatar Gowtham Anandha Babu
Browse files

Fix a2dp state machine java crash



Multiple threads trying to execute the a2dp state machine block,
where the message is sent even before starting the state machine,
resulting in FATAL exception on com.android.bluetooth.

Make the Device State Machine Map synchronized to avoid multiple
threads accessing the same resource at a time. Gaurding
mDeviceStateMap with itself to resolve this synchronization issue.

Bug: 373331233
Flag: EXEMPT, minor bugfix
Test: m com.android.btservices

Change-Id: I5adf470dd623fd674f11526b0f692b9ef1f2cf8a
Signed-off-by: default avatarGowtham Anandha Babu <gowtham.anandha.babu@intel.com>
parent 2e689390
Loading
Loading
Loading
Loading
+46 −25
Original line number Original line Diff line number Diff line
@@ -51,6 +51,8 @@ import java.util.concurrent.ConcurrentHashMap;
public class A2dpSinkService extends ProfileService {
public class A2dpSinkService extends ProfileService {
    private static final String TAG = A2dpSinkService.class.getSimpleName();
    private static final String TAG = A2dpSinkService.class.getSimpleName();


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


@@ -116,10 +118,12 @@ public class A2dpSinkService extends ProfileService {
    public void stop() {
    public void stop() {
        setA2dpSinkService(null);
        setA2dpSinkService(null);
        mNativeInterface.cleanup();
        mNativeInterface.cleanup();
        synchronized (mDeviceStateMap) {
            for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) {
            for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) {
                stateMachine.quitNow();
                stateMachine.quitNow();
            }
            }
            mDeviceStateMap.clear();
            mDeviceStateMap.clear();
        }
        synchronized (mStreamHandlerLock) {
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler != null) {
            if (mA2dpSinkStreamHandler != null) {
                mA2dpSinkStreamHandler.cleanup();
                mA2dpSinkStreamHandler.cleanup();
@@ -362,7 +366,10 @@ public class A2dpSinkService extends ProfileService {
            throw new IllegalArgumentException("Null device");
            throw new IllegalArgumentException("Null device");
        }
        }


        A2dpSinkStateMachine stateMachine = mDeviceStateMap.get(device);
        A2dpSinkStateMachine stateMachine;
        synchronized (mDeviceStateMap) {
            stateMachine = mDeviceStateMap.get(device);
        }
        // a state machine instance doesn't exist. maybe it is already gone?
        // a state machine instance doesn't exist. maybe it is already gone?
        if (stateMachine == null) {
        if (stateMachine == null) {
            return false;
            return false;
@@ -389,7 +396,9 @@ public class A2dpSinkService extends ProfileService {
        if (stateMachine == null) {
        if (stateMachine == null) {
            return;
            return;
        }
        }
        synchronized (mDeviceStateMap) {
            mDeviceStateMap.remove(stateMachine.getDevice());
            mDeviceStateMap.remove(stateMachine.getDevice());
        }
        stateMachine.quitNow();
        stateMachine.quitNow();
    }
    }


@@ -400,6 +409,7 @@ public class A2dpSinkService extends ProfileService {
    protected A2dpSinkStateMachine getOrCreateStateMachine(BluetoothDevice device) {
    protected A2dpSinkStateMachine getOrCreateStateMachine(BluetoothDevice device) {
        A2dpSinkStateMachine newStateMachine =
        A2dpSinkStateMachine newStateMachine =
                new A2dpSinkStateMachine(mLooper, device, this, mNativeInterface);
                new A2dpSinkStateMachine(mLooper, device, this, mNativeInterface);
        synchronized (mDeviceStateMap) {
            A2dpSinkStateMachine existingStateMachine =
            A2dpSinkStateMachine existingStateMachine =
                    mDeviceStateMap.putIfAbsent(device, newStateMachine);
                    mDeviceStateMap.putIfAbsent(device, newStateMachine);
            // Given null is not a valid value in our map, ConcurrentHashMap will return null if the
            // Given null is not a valid value in our map, ConcurrentHashMap will return null if the
@@ -411,11 +421,14 @@ public class A2dpSinkService extends ProfileService {
            }
            }
            return existingStateMachine;
            return existingStateMachine;
        }
        }
    }


    @VisibleForTesting
    @VisibleForTesting
    protected A2dpSinkStateMachine getStateMachineForDevice(BluetoothDevice device) {
    protected A2dpSinkStateMachine getStateMachineForDevice(BluetoothDevice device) {
        synchronized (mDeviceStateMap) {
            return mDeviceStateMap.get(device);
            return mDeviceStateMap.get(device);
        }
        }
    }


    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + ")");
        Log.d(TAG, "getDevicesMatchingConnectionStates(states=" + Arrays.toString(states) + ")");
@@ -451,7 +464,10 @@ public class A2dpSinkService extends ProfileService {
     */
     */
    public int getConnectionState(BluetoothDevice device) {
    public int getConnectionState(BluetoothDevice device) {
        if (device == null) return BluetoothProfile.STATE_DISCONNECTED;
        if (device == null) return BluetoothProfile.STATE_DISCONNECTED;
        A2dpSinkStateMachine stateMachine = mDeviceStateMap.get(device);
        A2dpSinkStateMachine stateMachine;
        synchronized (mDeviceStateMap) {
            stateMachine = mDeviceStateMap.get(device);
        }
        return (stateMachine == null)
        return (stateMachine == null)
                    ? BluetoothProfile.STATE_DISCONNECTED
                    ? BluetoothProfile.STATE_DISCONNECTED
                    : stateMachine.getState();
                    : stateMachine.getState();
@@ -501,6 +517,7 @@ public class A2dpSinkService extends ProfileService {
        super.dump(sb);
        super.dump(sb);
        ProfileService.println(sb, "Active Device = " + getActiveDevice());
        ProfileService.println(sb, "Active Device = " + getActiveDevice());
        ProfileService.println(sb, "Max Connected Devices = " + mMaxConnectedAudioDevices);
        ProfileService.println(sb, "Max Connected Devices = " + mMaxConnectedAudioDevices);
        synchronized (mDeviceStateMap) {
            ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size());
            ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size());
            for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) {
            for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) {
                ProfileService.println(
                ProfileService.println(
@@ -508,10 +525,14 @@ public class A2dpSinkService extends ProfileService {
                stateMachine.dump(sb);
                stateMachine.dump(sb);
            }
            }
        }
        }
    }


    BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
    BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
        if (device == null) return null;
        if (device == null) return null;
        A2dpSinkStateMachine stateMachine = mDeviceStateMap.get(device);
        A2dpSinkStateMachine stateMachine;
        synchronized (mDeviceStateMap) {
            stateMachine = mDeviceStateMap.get(device);
        }
        // a state machine instance doesn't exist. maybe it is already gone?
        // a state machine instance doesn't exist. maybe it is already gone?
        if (stateMachine == null) {
        if (stateMachine == null) {
            return null;
            return null;