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

Commit dc124dae authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

McsService: Fix authorization issues

With this patch, known le audio device got authorized in MCS when added
to the group.
This patch also makes sure that authorization is set when Bluetooth is
enabled.

Bug: 254192601
Test: atest BluetoothInstrumentationTests
Tag: #feature

Change-Id: I969578802afec4722e3a0d9f946d79384d80a941
parent 39daa09b
Loading
Loading
Loading
Loading
+66 −11
Original line number Diff line number Diff line
@@ -115,17 +115,21 @@ public class LeAudioService extends ProfileService {
    private volatile BluetoothDevice mActiveAudioOutDevice;
    private volatile BluetoothDevice mActiveAudioInDevice;
    private LeAudioCodecConfig mLeAudioCodecConfig;
    private Object mGroupLock = new Object();
    private final Object mGroupLock = new Object();
    ServiceFactory mServiceFactory = new ServiceFactory();

    LeAudioNativeInterface mLeAudioNativeInterface;
    boolean mLeAudioNativeIsInitialized = false;
    boolean mBluetoothEnabled = false;
    BluetoothDevice mHfpHandoverDevice = null;
    LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null;
    @VisibleForTesting
    AudioManager mAudioManager;
    LeAudioTmapGattServer mTmapGattServer;

    @VisibleForTesting
    McpService mMcpService;

    @VisibleForTesting
    VolumeControlService mVolumeControlService;

@@ -330,6 +334,7 @@ public class LeAudioService extends ProfileService {
        mLeAudioNativeInterface.cleanup();
        mLeAudioNativeInterface = null;
        mLeAudioNativeIsInitialized = false;
        mBluetoothEnabled = false;
        mHfpHandoverDevice = null;

        mActiveAudioOutDevice = null;
@@ -391,6 +396,7 @@ public class LeAudioService extends ProfileService {

        mAdapterService = null;
        mAudioManager = null;
        mMcpService = null;
        mVolumeControlService = null;

        return true;
@@ -1689,11 +1695,6 @@ public class LeAudioService extends ProfileService {
            } else {
                Log.e(TAG, "no descriptors for group: " + myGroupId);
            }

            McpService mcpService = mServiceFactory.getMcpService();
            if (mcpService != null) {
                mcpService.setDeviceAuthorized(device, true);
            }
        }
        // Check if the device is disconnected - if unbond, remove the state machine
        if (toState == BluetoothProfile.STATE_DISCONNECTED) {
@@ -1705,11 +1706,6 @@ public class LeAudioService extends ProfileService {
                removeStateMachine(device);
            }

            McpService mcpService = mServiceFactory.getMcpService();
            if (mcpService != null) {
                mcpService.setDeviceAuthorized(device, false);
            }

            int myGroupId = getGroupId(device);
            LeAudioGroupDescriptor descriptor = getGroupDescriptor(myGroupId);
            if (descriptor == null) {
@@ -1977,6 +1973,50 @@ public class LeAudioService extends ProfileService {
        }
    }

    McpService getMcpService() {
        if (mMcpService != null) {
            return mMcpService;
        }

        mMcpService = mServiceFactory.getMcpService();
        return mMcpService;
    }

    /**
     * This function is called when the framework registers
     * a callback with the service for this first time.
     * This is used as an indication that Bluetooth has been enabled.
     * 
     * It is used to authorize all known LeAudio devices in the services
     * which requires that e.g. GMCS
     */
    @VisibleForTesting
    void handleBluetoothEnabled() {
        if (DBG) {
            Log.d(TAG, "handleBluetoothEnabled ");
        }

        mBluetoothEnabled = true;

        synchronized (mGroupLock) {
            if (mDeviceGroupIdMap.isEmpty()) {
                return;
            }
        }

        McpService mcpService = getMcpService();
        if (mcpService == null) {
            Log.e(TAG, "mcpService not available ");
            return;
        }

        synchronized (mGroupLock) {
            for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceGroupIdMap.entrySet()) {
                mcpService.setDeviceAuthorized(entry.getKey(), true);
            }
        }
    }

    private LeAudioGroupDescriptor getGroupDescriptor(int groupId) {
        synchronized (mGroupLock) {
            return mGroupDescriptors.get(groupId);
@@ -1996,6 +2036,13 @@ public class LeAudioService extends ProfileService {
            }
            notifyGroupNodeAdded(device, groupId);
        }

        if (mBluetoothEnabled) {
            McpService mcpService = getMcpService();
            if (mcpService != null) {
                mcpService.setDeviceAuthorized(device, true);
            }
        }
    }

    private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) {
@@ -2039,6 +2086,11 @@ public class LeAudioService extends ProfileService {
            }
            notifyGroupNodeRemoved(device, groupId);
        }

        McpService mcpService = getMcpService();
        if (mcpService != null) {
            mcpService.setDeviceAuthorized(device, false);
        }
    }

    private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) {
@@ -2674,6 +2726,9 @@ public class LeAudioService extends ProfileService {

                enforceBluetoothPrivilegedPermission(service);
                service.mLeAudioCallbacks.register(callback);
                if (!service.mBluetoothEnabled) {
                    service.handleBluetoothEnabled();
                }
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
+63 −2
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.vc.VolumeControlService;

import org.junit.After;
@@ -109,11 +110,12 @@ public class LeAudioServiceTest {
    private BroadcastReceiver mLeAudioIntentReceiver;

    @Mock private AdapterService mAdapterService;
    @Mock private AudioManager mAudioManager;
    @Mock private DatabaseManager mDatabaseManager;
    @Mock private LeAudioNativeInterface mNativeInterface;
    @Mock private AudioManager mAudioManager;
    @Mock private VolumeControlService mVolumeControlService;
    @Mock private LeAudioTmapGattServer mTmapGattServer;
    @Mock private McpService mMcpService;
    @Mock private VolumeControlService mVolumeControlService;
    @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
    @Spy private ServiceFactory mServiceFactory = new ServiceFactory();

@@ -179,6 +181,7 @@ public class LeAudioServiceTest {
        LeAudioNativeInterface.setInstance(mNativeInterface);
        startService();
        mService.mAudioManager = mAudioManager;
        mService.mMcpService = mMcpService;
        mService.mServiceFactory = mServiceFactory;
        when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService);

@@ -860,6 +863,24 @@ public class LeAudioServiceTest {
        verifyNoConnectionStateIntent(TIMEOUT_MS, device);
    }

    private void generateGroupNodeAdded(BluetoothDevice device, int groupId) {
        LeAudioStackEvent nodeGroupAdded =
        new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
        nodeGroupAdded.device = device;
        nodeGroupAdded.valueInt1 = groupId;
        nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED;
        mService.messageFromNative(nodeGroupAdded);
    }

    private void generateGroupNodeRemoved(BluetoothDevice device, int groupId) {
        LeAudioStackEvent nodeGroupRemoved =
        new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
        nodeGroupRemoved.device = device;
        nodeGroupRemoved.valueInt1 = groupId;
        nodeGroupRemoved.valueInt2 = LeAudioStackEvent.GROUP_NODE_REMOVED;
        mService.messageFromNative(nodeGroupRemoved);
    }

    private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) {
        Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device));
        assertThat(intent).isNull();
@@ -1562,4 +1583,44 @@ public class LeAudioServiceTest {
        StringBuilder sb = new StringBuilder();
        mService.dump(sb);
    }

    /**
     * Test setting authorization for LeAudio device in the McpService
     */
    @Test
    public void testAuthorizeMcpServiceWhenDeviceConnecting() {
        int groupId = 1;

        mService.handleBluetoothEnabled();
        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mLeftDevice, groupId);
        connectTestDevice(mRightDevice, groupId);
        verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, true);
        verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, true);
    }

    /**
     * Test setting authorization for LeAudio device in the McpService
     */
    @Test
    public void testAuthorizeMcpServiceOnBluetoothEnableAndNodeRemoval() {
        int groupId = 1;

        generateGroupNodeAdded(mLeftDevice, groupId);
        generateGroupNodeAdded(mRightDevice, groupId);

        verify(mMcpService, times(0)).setDeviceAuthorized(mLeftDevice, true);
        verify(mMcpService, times(0)).setDeviceAuthorized(mRightDevice, true);

        mService.handleBluetoothEnabled();

        verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, true);
        verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, true);

        generateGroupNodeRemoved(mLeftDevice, groupId);
        verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, false);

        generateGroupNodeRemoved(mRightDevice, groupId);
        verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, false);
    }
}