Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +20 −0 Original line number Diff line number Diff line Loading @@ -2875,6 +2875,7 @@ public class LeAudioService extends ProfileService { return; } removeStateMachine(device); removeAuthorizationInfoForRelatedProfiles(device); } } Loading Loading @@ -2962,6 +2963,7 @@ public class LeAudioService extends ProfileService { Log.d(TAG, device + " is unbond. Remove state machine"); } removeStateMachine(device); removeAuthorizationInfoForRelatedProfiles(device); } if (!isScannerNeeded()) { Loading Loading @@ -3341,6 +3343,23 @@ public class LeAudioService extends ProfileService { } } void removeAuthorizationInfoForRelatedProfiles(BluetoothDevice device) { if (!mFeatureFlags.leaudioMcsTbsAuthorizationRebondFix()) { Log.i(TAG, "leaudio_mcs_tbs_authorization_rebond_fix is disabled"); return; } McpService mcpService = getMcpService(); if (mcpService != null) { mcpService.removeDeviceAuthorizationInfo(device); } TbsService tbsService = getTbsService(); if (tbsService != null) { tbsService.removeDeviceAuthorizationInfo(device); } } /** * 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. Loading Loading @@ -3499,6 +3518,7 @@ public class LeAudioService extends ProfileService { } setAuthorizationForRelatedProfiles(device, false); removeAuthorizationInfoForRelatedProfiles(device); } private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { Loading android/app/src/com/android/bluetooth/mcp/McpService.java +11 −0 Original line number Diff line number Diff line Loading @@ -200,6 +200,17 @@ public class McpService extends ProfileService { setDeviceAuthorized(device, false); } /** * Remove authorization information for the device. * * @param device device to remove from the service information * @hide */ public void removeDeviceAuthorizationInfo(BluetoothDevice device) { Log.i(TAG, "removeDeviceAuthorizationInfo(): device: " + device); mDeviceAuthorizations.remove(device); } public void setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized) { Log.i(TAG, "\tsetDeviceAuthorized(): device: " + device + ", isAuthorized: " + isAuthorized); Loading android/app/src/com/android/bluetooth/tbs/TbsService.java +11 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,17 @@ public class TbsService extends ProfileService { setDeviceAuthorized(device, false); } /** * Remove authorization information for the device. * * @param device device to remove from the service information * @hide */ public void removeDeviceAuthorizationInfo(BluetoothDevice device) { Log.i(TAG, "removeDeviceAuthorizationInfo(): device: " + device); mDeviceAuthorizations.remove(device); } /** * Sets device authorization for TBS. * Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +134 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,7 @@ public class LeAudioServiceTest { mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT, false); mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false); mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, false); mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX, false); mService.setFeatureFlags(mFakeFlagsImpl); mService.mAudioManager = mAudioManager; Loading Loading @@ -804,6 +805,139 @@ public class LeAudioServiceTest { assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); } /** Test that authorization info is removed from TBS and MCS after the device is unbond. */ @Test public void testAuthorizationInfoRemovedFromTbsMcsOnUnbondEvents() { mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true); mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX, true); // Update the device priority so okToConnect() returns true when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); when(mDatabaseManager.getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); // Create device descriptor with connect request assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); // Unbond received in CONNECTION_STATE_CONNECTING state generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTING); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond doReturn(BluetoothDevice.BOND_NONE) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); verifyConnectionStateIntent( TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); verify(mTbsService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Unbond received in CONNECTION_STATE_CONNECTED // Create device descriptor with connect request. To connect service, // device needs to be bonded doReturn(BluetoothDevice.BOND_BONDED) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond doReturn(BluetoothDevice.BOND_NONE) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); verifyConnectionStateIntent( TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_DISCONNECTING); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); verify(mTbsService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Inject CONNECTION_STATE_DISCONNECTED generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_DISCONNECTING); verify(mTbsService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Unbond received in CONNECTION_STATE_DISCONNECTED // Create device descriptor with connect request. To connect service, // device needs to be bonded doReturn(BluetoothDevice.BOND_BONDED) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); injectAndVerifyDeviceDisconnected(mLeftDevice); verify(mTbsService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Device unbond doReturn(BluetoothDevice.BOND_NONE) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); verify(mTbsService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); } /** * Test that a CONNECTION_STATE_DISCONNECTED Le Audio stack event will remove the state * machine only if the device is unbond. Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +20 −0 Original line number Diff line number Diff line Loading @@ -2875,6 +2875,7 @@ public class LeAudioService extends ProfileService { return; } removeStateMachine(device); removeAuthorizationInfoForRelatedProfiles(device); } } Loading Loading @@ -2962,6 +2963,7 @@ public class LeAudioService extends ProfileService { Log.d(TAG, device + " is unbond. Remove state machine"); } removeStateMachine(device); removeAuthorizationInfoForRelatedProfiles(device); } if (!isScannerNeeded()) { Loading Loading @@ -3341,6 +3343,23 @@ public class LeAudioService extends ProfileService { } } void removeAuthorizationInfoForRelatedProfiles(BluetoothDevice device) { if (!mFeatureFlags.leaudioMcsTbsAuthorizationRebondFix()) { Log.i(TAG, "leaudio_mcs_tbs_authorization_rebond_fix is disabled"); return; } McpService mcpService = getMcpService(); if (mcpService != null) { mcpService.removeDeviceAuthorizationInfo(device); } TbsService tbsService = getTbsService(); if (tbsService != null) { tbsService.removeDeviceAuthorizationInfo(device); } } /** * 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. Loading Loading @@ -3499,6 +3518,7 @@ public class LeAudioService extends ProfileService { } setAuthorizationForRelatedProfiles(device, false); removeAuthorizationInfoForRelatedProfiles(device); } private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { Loading
android/app/src/com/android/bluetooth/mcp/McpService.java +11 −0 Original line number Diff line number Diff line Loading @@ -200,6 +200,17 @@ public class McpService extends ProfileService { setDeviceAuthorized(device, false); } /** * Remove authorization information for the device. * * @param device device to remove from the service information * @hide */ public void removeDeviceAuthorizationInfo(BluetoothDevice device) { Log.i(TAG, "removeDeviceAuthorizationInfo(): device: " + device); mDeviceAuthorizations.remove(device); } public void setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized) { Log.i(TAG, "\tsetDeviceAuthorized(): device: " + device + ", isAuthorized: " + isAuthorized); Loading
android/app/src/com/android/bluetooth/tbs/TbsService.java +11 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,17 @@ public class TbsService extends ProfileService { setDeviceAuthorized(device, false); } /** * Remove authorization information for the device. * * @param device device to remove from the service information * @hide */ public void removeDeviceAuthorizationInfo(BluetoothDevice device) { Log.i(TAG, "removeDeviceAuthorizationInfo(): device: " + device); mDeviceAuthorizations.remove(device); } /** * Sets device authorization for TBS. * Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +134 −0 Original line number Diff line number Diff line Loading @@ -198,6 +198,7 @@ public class LeAudioServiceTest { mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT, false); mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, false); mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, false); mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX, false); mService.setFeatureFlags(mFakeFlagsImpl); mService.mAudioManager = mAudioManager; Loading Loading @@ -804,6 +805,139 @@ public class LeAudioServiceTest { assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); } /** Test that authorization info is removed from TBS and MCS after the device is unbond. */ @Test public void testAuthorizationInfoRemovedFromTbsMcsOnUnbondEvents() { mFakeFlagsImpl.setFlag(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION, true); mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_MCS_TBS_AUTHORIZATION_REBOND_FIX, true); // Update the device priority so okToConnect() returns true when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); when(mDatabaseManager.getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); // Create device descriptor with connect request assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); // Unbond received in CONNECTION_STATE_CONNECTING state generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTING); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond doReturn(BluetoothDevice.BOND_NONE) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); verifyConnectionStateIntent( TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); verify(mTbsService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Unbond received in CONNECTION_STATE_CONNECTED // Create device descriptor with connect request. To connect service, // device needs to be bonded doReturn(BluetoothDevice.BOND_BONDED) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); // Device unbond doReturn(BluetoothDevice.BOND_NONE) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); verifyConnectionStateIntent( TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_DISCONNECTING); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); verify(mTbsService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Inject CONNECTION_STATE_DISCONNECTED generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_DISCONNECTING); verify(mTbsService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Unbond received in CONNECTION_STATE_DISCONNECTED // Create device descriptor with connect request. To connect service, // device needs to be bonded doReturn(BluetoothDevice.BOND_BONDED) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING); assertThat(mService.getConnectionState(mLeftDevice)) .isEqualTo(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); injectAndVerifyDeviceDisconnected(mLeftDevice); verify(mTbsService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(0)).removeDeviceAuthorizationInfo(mLeftDevice); reset(mTbsService); reset(mMcpService); // Device unbond doReturn(BluetoothDevice.BOND_NONE) .when(mAdapterService) .getBondState(any(BluetoothDevice.class)); mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); verify(mTbsService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); verify(mMcpService, times(1)).removeDeviceAuthorizationInfo(mLeftDevice); } /** * Test that a CONNECTION_STATE_DISCONNECTED Le Audio stack event will remove the state * machine only if the device is unbond. Loading