Loading src/com/android/bluetooth/a2dp/A2dpService.java +22 −8 Original line number Original line Diff line number Diff line Loading @@ -857,7 +857,8 @@ public class A2dpService extends ProfileService { * @param sameAudioFeedingParameters if true the audio feeding parameters * @param sameAudioFeedingParameters if true the audio feeding parameters * haven't been changed * haven't been changed */ */ void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, @VisibleForTesting public void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, boolean sameAudioFeedingParameters) { boolean sameAudioFeedingParameters) { // Log codec config and capability metrics // Log codec config and capability metrics BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig(); BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig(); Loading Loading @@ -1009,9 +1010,17 @@ public class A2dpService extends ProfileService { } } } } private void updateOptionalCodecsSupport(BluetoothDevice device) { /** * Update and initiate optional codec status change to native. * * @param device the device to change optional codec status */ @VisibleForTesting public void updateOptionalCodecsSupport(BluetoothDevice device) { int previousSupport = getSupportsOptionalCodecs(device); int previousSupport = getSupportsOptionalCodecs(device); boolean supportsOptional = false; boolean supportsOptional = false; boolean hasMandatoryCodec = false; synchronized (mStateMachines) { synchronized (mStateMachines) { A2dpStateMachine sm = mStateMachines.get(device); A2dpStateMachine sm = mStateMachines.get(device); Loading @@ -1021,13 +1030,22 @@ public class A2dpService extends ProfileService { BluetoothCodecStatus codecStatus = sm.getCodecStatus(); BluetoothCodecStatus codecStatus = sm.getCodecStatus(); if (codecStatus != null) { if (codecStatus != null) { for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { if (!config.isMandatoryCodec()) { if (config.isMandatoryCodec()) { hasMandatoryCodec = true; } else { supportsOptional = true; supportsOptional = true; break; } } } } } } } } if (!hasMandatoryCodec) { // Mandatory codec(SBC) is not selectable. It could be caused by the remote device // select codec before native finish get codec capabilities. Stop use this codec // status as the reference to support/enable optional codecs. Log.i(TAG, "updateOptionalCodecsSupport: Mandatory codec is not selectable."); return; } if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN || supportsOptional != (previousSupport || supportsOptional != (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { Loading Loading @@ -1056,10 +1074,6 @@ public class A2dpService extends ProfileService { } } synchronized (mStateMachines) { synchronized (mStateMachines) { if (toState == BluetoothProfile.STATE_CONNECTED) { if (toState == BluetoothProfile.STATE_CONNECTED) { // Each time a device connects, we want to re-check if it supports optional // codecs (perhaps it's had a firmware update, etc.) and save that state if // it differs from what we had saved before. updateOptionalCodecsSupport(device); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); } } // Set the active device if only one connected device is supported and it was connected // Set the active device if only one connected device is supported and it was connected Loading src/com/android/bluetooth/a2dp/A2dpStateMachine.java +26 −2 Original line number Original line Diff line number Diff line Loading @@ -89,7 +89,8 @@ final class A2dpStateMachine extends StateMachine { private A2dpService mA2dpService; private A2dpService mA2dpService; private A2dpNativeInterface mA2dpNativeInterface; private A2dpNativeInterface mA2dpNativeInterface; private boolean mA2dpOffloadEnabled = false; @VisibleForTesting boolean mA2dpOffloadEnabled = false; private final BluetoothDevice mDevice; private final BluetoothDevice mDevice; private boolean mIsPlaying = false; private boolean mIsPlaying = false; private BluetoothCodecStatus mCodecStatus; private BluetoothCodecStatus mCodecStatus; Loading Loading @@ -467,6 +468,10 @@ final class A2dpStateMachine extends StateMachine { removeDeferredMessages(CONNECT); removeDeferredMessages(CONNECT); // Each time a device connects, we want to re-check if it supports optional // codecs (perhaps it's had a firmware update, etc.) and save that state if // it differs from what we had saved before. mA2dpService.updateOptionalCodecsSupport(mDevice); broadcastConnectionState(mConnectionState, mLastConnectionState); broadcastConnectionState(mConnectionState, mLastConnectionState); // Upon connected, the audio starts out as stopped // Upon connected, the audio starts out as stopped broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, Loading Loading @@ -608,8 +613,11 @@ final class A2dpStateMachine extends StateMachine { } } // NOTE: This event is processed in any state // NOTE: This event is processed in any state private void processCodecConfigEvent(BluetoothCodecStatus newCodecStatus) { @VisibleForTesting void processCodecConfigEvent(BluetoothCodecStatus newCodecStatus) { BluetoothCodecConfig prevCodecConfig = null; BluetoothCodecConfig prevCodecConfig = null; BluetoothCodecStatus prevCodecStatus = mCodecStatus; synchronized (this) { synchronized (this) { if (mCodecStatus != null) { if (mCodecStatus != null) { prevCodecConfig = mCodecStatus.getCodecConfig(); prevCodecConfig = mCodecStatus.getCodecConfig(); Loading @@ -630,6 +638,12 @@ final class A2dpStateMachine extends StateMachine { } } } } if (isConnected() && !sameSelectableCodec(prevCodecStatus, mCodecStatus)) { // Remote selectable codec could be changed if codec config changed // in connected state, we need to re-check optional codec status // for this codec change event. mA2dpService.updateOptionalCodecsSupport(mDevice); } if (mA2dpOffloadEnabled) { if (mA2dpOffloadEnabled) { boolean update = false; boolean update = false; BluetoothCodecConfig newCodecConfig = mCodecStatus.getCodecConfig(); BluetoothCodecConfig newCodecConfig = mCodecStatus.getCodecConfig(); Loading Loading @@ -696,6 +710,16 @@ final class A2dpStateMachine extends StateMachine { return builder.toString(); return builder.toString(); } } private static boolean sameSelectableCodec(BluetoothCodecStatus prevCodecStatus, BluetoothCodecStatus newCodecStatus) { if (prevCodecStatus == null) { return false; } return BluetoothCodecStatus.sameCapabilities( prevCodecStatus.getCodecsSelectableCapabilities(), newCodecStatus.getCodecsSelectableCapabilities()); } private static String messageWhatToString(int what) { private static String messageWhatToString(int what) { switch (what) { switch (what) { case CONNECT: case CONNECT: Loading tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java +18 −7 Original line number Original line Diff line number Diff line Loading @@ -1073,7 +1073,6 @@ public class A2dpServiceTest { BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.CHANNEL_MODE_STEREO, BluetoothCodecConfig.CHANNEL_MODE_STEREO, 0, 0, 0, 0); // Codec-specific fields 0, 0, 0, 0); // Codec-specific fields BluetoothCodecConfig codecConfig = codecConfigSbc; BluetoothCodecConfig[] codecsLocalCapabilities; BluetoothCodecConfig[] codecsLocalCapabilities; BluetoothCodecConfig[] codecsSelectableCapabilities; BluetoothCodecConfig[] codecsSelectableCapabilities; Loading @@ -1090,24 +1089,36 @@ public class A2dpServiceTest { codecsLocalCapabilities[0] = codecConfigSbc; codecsLocalCapabilities[0] = codecConfigSbc; codecsSelectableCapabilities[0] = codecConfigSbc; codecsSelectableCapabilities[0] = codecConfigSbc; } } BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(codecConfig, BluetoothCodecConfig[] badCodecsSelectableCapabilities; codecsLocalCapabilities, badCodecsSelectableCapabilities = new BluetoothCodecConfig[1]; codecsSelectableCapabilities); badCodecsSelectableCapabilities[0] = codecConfigAac; BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(codecConfigSbc, codecsLocalCapabilities, codecsSelectableCapabilities); BluetoothCodecStatus badCodecStatus = new BluetoothCodecStatus(codecConfigAac, codecsLocalCapabilities, badCodecsSelectableCapabilities); when(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice)) when(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice)) .thenReturn(previousSupport); .thenReturn(previousSupport); when(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice)) when(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice)) .thenReturn(previousEnabled); .thenReturn(previousEnabled); // Generate connection request from native with bad codec status connectDeviceWithCodecStatus(mTestDevice, badCodecStatus); generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Generate connection request from native with good codec status connectDeviceWithCodecStatus(mTestDevice, codecStatus); connectDeviceWithCodecStatus(mTestDevice, codecStatus); generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Check optional codec status is set properly verify(mDatabaseManager, times(verifyNotSupportTime)).setA2dpSupportsOptionalCodecs( verify(mDatabaseManager, times(verifyNotSupportTime)).setA2dpSupportsOptionalCodecs( mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); verify(mDatabaseManager, times(verifySupportTime)).setA2dpSupportsOptionalCodecs( verify(mDatabaseManager, times(verifySupportTime)).setA2dpSupportsOptionalCodecs( mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); verify(mDatabaseManager, times(verifyEnabledTime)).setA2dpOptionalCodecsEnabled( verify(mDatabaseManager, times(verifyEnabledTime)).setA2dpOptionalCodecsEnabled( mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); } } } } tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java +104 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static org.mockito.Mockito.*; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Context; Loading Loading @@ -54,6 +56,9 @@ public class A2dpStateMachineTest { private BluetoothDevice mTestDevice; private BluetoothDevice mTestDevice; private static final int TIMEOUT_MS = 1000; // 1s private static final int TIMEOUT_MS = 1000; // 1s private BluetoothCodecConfig mCodecConfigSbc; private BluetoothCodecConfig mCodecConfigAac; @Mock private AdapterService mAdapterService; @Mock private AdapterService mAdapterService; @Mock private A2dpService mA2dpService; @Mock private A2dpService mA2dpService; @Mock private A2dpNativeInterface mA2dpNativeInterface; @Mock private A2dpNativeInterface mA2dpNativeInterface; Loading @@ -72,6 +77,22 @@ public class A2dpStateMachineTest { // Get a device for testing // Get a device for testing mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); // Set up sample codec config mCodecConfigSbc = new BluetoothCodecConfig( BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.CHANNEL_MODE_STEREO, 0, 0, 0, 0); // Codec-specific fields mCodecConfigAac = new BluetoothCodecConfig( BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_48000, BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.CHANNEL_MODE_STEREO, 0, 0, 0, 0); // Codec-specific fields // Set up thread and looper // Set up thread and looper mHandlerThread = new HandlerThread("A2dpStateMachineTestHandlerThread"); mHandlerThread = new HandlerThread("A2dpStateMachineTestHandlerThread"); mHandlerThread.start(); mHandlerThread.start(); Loading Loading @@ -255,4 +276,87 @@ public class A2dpStateMachineTest { Assert.assertThat(mA2dpStateMachine.getCurrentState(), Assert.assertThat(mA2dpStateMachine.getCurrentState(), IsInstanceOf.instanceOf(A2dpStateMachine.Disconnected.class)); IsInstanceOf.instanceOf(A2dpStateMachine.Disconnected.class)); } } /** * Test that codec config change been reported to A2dpService properly. */ @Test public void testProcessCodecConfigEvent() { testProcessCodecConfigEventCase(false); } /** * Test that codec config change been reported to A2dpService properly when * A2DP hardware offloading is enabled. */ @Test public void testProcessCodecConfigEvent_OffloadEnabled() { testProcessCodecConfigEventCase(true); } /** * Helper methold to test processCodecConfigEvent() */ public void testProcessCodecConfigEventCase(boolean offloadEnabled) { if (offloadEnabled) { mA2dpStateMachine.mA2dpOffloadEnabled = true; } doNothing().when(mA2dpService).codecConfigUpdated(any(BluetoothDevice.class), any(BluetoothCodecStatus.class), anyBoolean()); doNothing().when(mA2dpService).updateOptionalCodecsSupport(any(BluetoothDevice.class)); allowConnection(true); BluetoothCodecConfig[] codecsSelectableSbc; codecsSelectableSbc = new BluetoothCodecConfig[1]; codecsSelectableSbc[0] = mCodecConfigSbc; BluetoothCodecConfig[] codecsSelectableSbcAac; codecsSelectableSbcAac = new BluetoothCodecConfig[2]; codecsSelectableSbcAac[0] = mCodecConfigSbc; codecsSelectableSbcAac[1] = mCodecConfigAac; BluetoothCodecStatus codecStatusSbcAndSbc = new BluetoothCodecStatus(mCodecConfigSbc, codecsSelectableSbcAac, codecsSelectableSbc); BluetoothCodecStatus codecStatusSbcAndSbcAac = new BluetoothCodecStatus(mCodecConfigSbc, codecsSelectableSbcAac, codecsSelectableSbcAac); BluetoothCodecStatus codecStatusAacAndSbcAac = new BluetoothCodecStatus(mCodecConfigAac, codecsSelectableSbcAac, codecsSelectableSbcAac); // Set default codec status when device disconnected // Selected codec = SBC, selectable codec = SBC mA2dpStateMachine.processCodecConfigEvent(codecStatusSbcAndSbc); verify(mA2dpService).codecConfigUpdated(mTestDevice, codecStatusSbcAndSbc, false); // Inject an event to change state machine to connected state A2dpStackEvent connStCh = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.device = mTestDevice; connStCh.valueInt = A2dpStackEvent.CONNECTION_STATE_CONNECTED; mA2dpStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, connStCh); // Verify that the expected number of broadcasts are executed: // - two calls to broadcastConnectionState(): Disconnected -> Conecting -> Connected // - one call to broadcastAudioState() when entering Connected state ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); verify(mA2dpService, timeout(TIMEOUT_MS).times(2)).sendBroadcast(intentArgument2.capture(), anyString()); // Verify that state machine update optional codec when enter connected state verify(mA2dpService, times(1)).updateOptionalCodecsSupport(mTestDevice); // Change codec status when device connected. // Selected codec = SBC, selectable codec = SBC+AAC mA2dpStateMachine.processCodecConfigEvent(codecStatusSbcAndSbcAac); if (!offloadEnabled) { verify(mA2dpService).codecConfigUpdated(mTestDevice, codecStatusSbcAndSbcAac, true); } verify(mA2dpService, times(2)).updateOptionalCodecsSupport(mTestDevice); // Update selected codec with selectable codec unchanged. // Selected codec = AAC, selectable codec = SBC+AAC mA2dpStateMachine.processCodecConfigEvent(codecStatusAacAndSbcAac); verify(mA2dpService).codecConfigUpdated(mTestDevice, codecStatusAacAndSbcAac, false); verify(mA2dpService, times(2)).updateOptionalCodecsSupport(mTestDevice); } } } Loading
src/com/android/bluetooth/a2dp/A2dpService.java +22 −8 Original line number Original line Diff line number Diff line Loading @@ -857,7 +857,8 @@ public class A2dpService extends ProfileService { * @param sameAudioFeedingParameters if true the audio feeding parameters * @param sameAudioFeedingParameters if true the audio feeding parameters * haven't been changed * haven't been changed */ */ void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, @VisibleForTesting public void codecConfigUpdated(BluetoothDevice device, BluetoothCodecStatus codecStatus, boolean sameAudioFeedingParameters) { boolean sameAudioFeedingParameters) { // Log codec config and capability metrics // Log codec config and capability metrics BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig(); BluetoothCodecConfig codecConfig = codecStatus.getCodecConfig(); Loading Loading @@ -1009,9 +1010,17 @@ public class A2dpService extends ProfileService { } } } } private void updateOptionalCodecsSupport(BluetoothDevice device) { /** * Update and initiate optional codec status change to native. * * @param device the device to change optional codec status */ @VisibleForTesting public void updateOptionalCodecsSupport(BluetoothDevice device) { int previousSupport = getSupportsOptionalCodecs(device); int previousSupport = getSupportsOptionalCodecs(device); boolean supportsOptional = false; boolean supportsOptional = false; boolean hasMandatoryCodec = false; synchronized (mStateMachines) { synchronized (mStateMachines) { A2dpStateMachine sm = mStateMachines.get(device); A2dpStateMachine sm = mStateMachines.get(device); Loading @@ -1021,13 +1030,22 @@ public class A2dpService extends ProfileService { BluetoothCodecStatus codecStatus = sm.getCodecStatus(); BluetoothCodecStatus codecStatus = sm.getCodecStatus(); if (codecStatus != null) { if (codecStatus != null) { for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { if (!config.isMandatoryCodec()) { if (config.isMandatoryCodec()) { hasMandatoryCodec = true; } else { supportsOptional = true; supportsOptional = true; break; } } } } } } } } if (!hasMandatoryCodec) { // Mandatory codec(SBC) is not selectable. It could be caused by the remote device // select codec before native finish get codec capabilities. Stop use this codec // status as the reference to support/enable optional codecs. Log.i(TAG, "updateOptionalCodecsSupport: Mandatory codec is not selectable."); return; } if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN || supportsOptional != (previousSupport || supportsOptional != (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { Loading Loading @@ -1056,10 +1074,6 @@ public class A2dpService extends ProfileService { } } synchronized (mStateMachines) { synchronized (mStateMachines) { if (toState == BluetoothProfile.STATE_CONNECTED) { if (toState == BluetoothProfile.STATE_CONNECTED) { // Each time a device connects, we want to re-check if it supports optional // codecs (perhaps it's had a firmware update, etc.) and save that state if // it differs from what we had saved before. updateOptionalCodecsSupport(device); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP); } } // Set the active device if only one connected device is supported and it was connected // Set the active device if only one connected device is supported and it was connected Loading
src/com/android/bluetooth/a2dp/A2dpStateMachine.java +26 −2 Original line number Original line Diff line number Diff line Loading @@ -89,7 +89,8 @@ final class A2dpStateMachine extends StateMachine { private A2dpService mA2dpService; private A2dpService mA2dpService; private A2dpNativeInterface mA2dpNativeInterface; private A2dpNativeInterface mA2dpNativeInterface; private boolean mA2dpOffloadEnabled = false; @VisibleForTesting boolean mA2dpOffloadEnabled = false; private final BluetoothDevice mDevice; private final BluetoothDevice mDevice; private boolean mIsPlaying = false; private boolean mIsPlaying = false; private BluetoothCodecStatus mCodecStatus; private BluetoothCodecStatus mCodecStatus; Loading Loading @@ -467,6 +468,10 @@ final class A2dpStateMachine extends StateMachine { removeDeferredMessages(CONNECT); removeDeferredMessages(CONNECT); // Each time a device connects, we want to re-check if it supports optional // codecs (perhaps it's had a firmware update, etc.) and save that state if // it differs from what we had saved before. mA2dpService.updateOptionalCodecsSupport(mDevice); broadcastConnectionState(mConnectionState, mLastConnectionState); broadcastConnectionState(mConnectionState, mLastConnectionState); // Upon connected, the audio starts out as stopped // Upon connected, the audio starts out as stopped broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, Loading Loading @@ -608,8 +613,11 @@ final class A2dpStateMachine extends StateMachine { } } // NOTE: This event is processed in any state // NOTE: This event is processed in any state private void processCodecConfigEvent(BluetoothCodecStatus newCodecStatus) { @VisibleForTesting void processCodecConfigEvent(BluetoothCodecStatus newCodecStatus) { BluetoothCodecConfig prevCodecConfig = null; BluetoothCodecConfig prevCodecConfig = null; BluetoothCodecStatus prevCodecStatus = mCodecStatus; synchronized (this) { synchronized (this) { if (mCodecStatus != null) { if (mCodecStatus != null) { prevCodecConfig = mCodecStatus.getCodecConfig(); prevCodecConfig = mCodecStatus.getCodecConfig(); Loading @@ -630,6 +638,12 @@ final class A2dpStateMachine extends StateMachine { } } } } if (isConnected() && !sameSelectableCodec(prevCodecStatus, mCodecStatus)) { // Remote selectable codec could be changed if codec config changed // in connected state, we need to re-check optional codec status // for this codec change event. mA2dpService.updateOptionalCodecsSupport(mDevice); } if (mA2dpOffloadEnabled) { if (mA2dpOffloadEnabled) { boolean update = false; boolean update = false; BluetoothCodecConfig newCodecConfig = mCodecStatus.getCodecConfig(); BluetoothCodecConfig newCodecConfig = mCodecStatus.getCodecConfig(); Loading Loading @@ -696,6 +710,16 @@ final class A2dpStateMachine extends StateMachine { return builder.toString(); return builder.toString(); } } private static boolean sameSelectableCodec(BluetoothCodecStatus prevCodecStatus, BluetoothCodecStatus newCodecStatus) { if (prevCodecStatus == null) { return false; } return BluetoothCodecStatus.sameCapabilities( prevCodecStatus.getCodecsSelectableCapabilities(), newCodecStatus.getCodecsSelectableCapabilities()); } private static String messageWhatToString(int what) { private static String messageWhatToString(int what) { switch (what) { switch (what) { case CONNECT: case CONNECT: Loading
tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java +18 −7 Original line number Original line Diff line number Diff line Loading @@ -1073,7 +1073,6 @@ public class A2dpServiceTest { BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.CHANNEL_MODE_STEREO, BluetoothCodecConfig.CHANNEL_MODE_STEREO, 0, 0, 0, 0); // Codec-specific fields 0, 0, 0, 0); // Codec-specific fields BluetoothCodecConfig codecConfig = codecConfigSbc; BluetoothCodecConfig[] codecsLocalCapabilities; BluetoothCodecConfig[] codecsLocalCapabilities; BluetoothCodecConfig[] codecsSelectableCapabilities; BluetoothCodecConfig[] codecsSelectableCapabilities; Loading @@ -1090,24 +1089,36 @@ public class A2dpServiceTest { codecsLocalCapabilities[0] = codecConfigSbc; codecsLocalCapabilities[0] = codecConfigSbc; codecsSelectableCapabilities[0] = codecConfigSbc; codecsSelectableCapabilities[0] = codecConfigSbc; } } BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(codecConfig, BluetoothCodecConfig[] badCodecsSelectableCapabilities; codecsLocalCapabilities, badCodecsSelectableCapabilities = new BluetoothCodecConfig[1]; codecsSelectableCapabilities); badCodecsSelectableCapabilities[0] = codecConfigAac; BluetoothCodecStatus codecStatus = new BluetoothCodecStatus(codecConfigSbc, codecsLocalCapabilities, codecsSelectableCapabilities); BluetoothCodecStatus badCodecStatus = new BluetoothCodecStatus(codecConfigAac, codecsLocalCapabilities, badCodecsSelectableCapabilities); when(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice)) when(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice)) .thenReturn(previousSupport); .thenReturn(previousSupport); when(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice)) when(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice)) .thenReturn(previousEnabled); .thenReturn(previousEnabled); // Generate connection request from native with bad codec status connectDeviceWithCodecStatus(mTestDevice, badCodecStatus); generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Generate connection request from native with good codec status connectDeviceWithCodecStatus(mTestDevice, codecStatus); connectDeviceWithCodecStatus(mTestDevice, codecStatus); generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Check optional codec status is set properly verify(mDatabaseManager, times(verifyNotSupportTime)).setA2dpSupportsOptionalCodecs( verify(mDatabaseManager, times(verifyNotSupportTime)).setA2dpSupportsOptionalCodecs( mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); verify(mDatabaseManager, times(verifySupportTime)).setA2dpSupportsOptionalCodecs( verify(mDatabaseManager, times(verifySupportTime)).setA2dpSupportsOptionalCodecs( mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); verify(mDatabaseManager, times(verifyEnabledTime)).setA2dpOptionalCodecsEnabled( verify(mDatabaseManager, times(verifyEnabledTime)).setA2dpOptionalCodecsEnabled( mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); mTestDevice, BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); } } } }
tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java +104 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static org.mockito.Mockito.*; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Context; Loading Loading @@ -54,6 +56,9 @@ public class A2dpStateMachineTest { private BluetoothDevice mTestDevice; private BluetoothDevice mTestDevice; private static final int TIMEOUT_MS = 1000; // 1s private static final int TIMEOUT_MS = 1000; // 1s private BluetoothCodecConfig mCodecConfigSbc; private BluetoothCodecConfig mCodecConfigAac; @Mock private AdapterService mAdapterService; @Mock private AdapterService mAdapterService; @Mock private A2dpService mA2dpService; @Mock private A2dpService mA2dpService; @Mock private A2dpNativeInterface mA2dpNativeInterface; @Mock private A2dpNativeInterface mA2dpNativeInterface; Loading @@ -72,6 +77,22 @@ public class A2dpStateMachineTest { // Get a device for testing // Get a device for testing mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); // Set up sample codec config mCodecConfigSbc = new BluetoothCodecConfig( BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.CHANNEL_MODE_STEREO, 0, 0, 0, 0); // Codec-specific fields mCodecConfigAac = new BluetoothCodecConfig( BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_48000, BluetoothCodecConfig.BITS_PER_SAMPLE_16, BluetoothCodecConfig.CHANNEL_MODE_STEREO, 0, 0, 0, 0); // Codec-specific fields // Set up thread and looper // Set up thread and looper mHandlerThread = new HandlerThread("A2dpStateMachineTestHandlerThread"); mHandlerThread = new HandlerThread("A2dpStateMachineTestHandlerThread"); mHandlerThread.start(); mHandlerThread.start(); Loading Loading @@ -255,4 +276,87 @@ public class A2dpStateMachineTest { Assert.assertThat(mA2dpStateMachine.getCurrentState(), Assert.assertThat(mA2dpStateMachine.getCurrentState(), IsInstanceOf.instanceOf(A2dpStateMachine.Disconnected.class)); IsInstanceOf.instanceOf(A2dpStateMachine.Disconnected.class)); } } /** * Test that codec config change been reported to A2dpService properly. */ @Test public void testProcessCodecConfigEvent() { testProcessCodecConfigEventCase(false); } /** * Test that codec config change been reported to A2dpService properly when * A2DP hardware offloading is enabled. */ @Test public void testProcessCodecConfigEvent_OffloadEnabled() { testProcessCodecConfigEventCase(true); } /** * Helper methold to test processCodecConfigEvent() */ public void testProcessCodecConfigEventCase(boolean offloadEnabled) { if (offloadEnabled) { mA2dpStateMachine.mA2dpOffloadEnabled = true; } doNothing().when(mA2dpService).codecConfigUpdated(any(BluetoothDevice.class), any(BluetoothCodecStatus.class), anyBoolean()); doNothing().when(mA2dpService).updateOptionalCodecsSupport(any(BluetoothDevice.class)); allowConnection(true); BluetoothCodecConfig[] codecsSelectableSbc; codecsSelectableSbc = new BluetoothCodecConfig[1]; codecsSelectableSbc[0] = mCodecConfigSbc; BluetoothCodecConfig[] codecsSelectableSbcAac; codecsSelectableSbcAac = new BluetoothCodecConfig[2]; codecsSelectableSbcAac[0] = mCodecConfigSbc; codecsSelectableSbcAac[1] = mCodecConfigAac; BluetoothCodecStatus codecStatusSbcAndSbc = new BluetoothCodecStatus(mCodecConfigSbc, codecsSelectableSbcAac, codecsSelectableSbc); BluetoothCodecStatus codecStatusSbcAndSbcAac = new BluetoothCodecStatus(mCodecConfigSbc, codecsSelectableSbcAac, codecsSelectableSbcAac); BluetoothCodecStatus codecStatusAacAndSbcAac = new BluetoothCodecStatus(mCodecConfigAac, codecsSelectableSbcAac, codecsSelectableSbcAac); // Set default codec status when device disconnected // Selected codec = SBC, selectable codec = SBC mA2dpStateMachine.processCodecConfigEvent(codecStatusSbcAndSbc); verify(mA2dpService).codecConfigUpdated(mTestDevice, codecStatusSbcAndSbc, false); // Inject an event to change state machine to connected state A2dpStackEvent connStCh = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.device = mTestDevice; connStCh.valueInt = A2dpStackEvent.CONNECTION_STATE_CONNECTED; mA2dpStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, connStCh); // Verify that the expected number of broadcasts are executed: // - two calls to broadcastConnectionState(): Disconnected -> Conecting -> Connected // - one call to broadcastAudioState() when entering Connected state ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); verify(mA2dpService, timeout(TIMEOUT_MS).times(2)).sendBroadcast(intentArgument2.capture(), anyString()); // Verify that state machine update optional codec when enter connected state verify(mA2dpService, times(1)).updateOptionalCodecsSupport(mTestDevice); // Change codec status when device connected. // Selected codec = SBC, selectable codec = SBC+AAC mA2dpStateMachine.processCodecConfigEvent(codecStatusSbcAndSbcAac); if (!offloadEnabled) { verify(mA2dpService).codecConfigUpdated(mTestDevice, codecStatusSbcAndSbcAac, true); } verify(mA2dpService, times(2)).updateOptionalCodecsSupport(mTestDevice); // Update selected codec with selectable codec unchanged. // Selected codec = AAC, selectable codec = SBC+AAC mA2dpStateMachine.processCodecConfigEvent(codecStatusAacAndSbcAac); verify(mA2dpService).codecConfigUpdated(mTestDevice, codecStatusAacAndSbcAac, false); verify(mA2dpService, times(2)).updateOptionalCodecsSupport(mTestDevice); } } }