Loading jni/com_android_bluetooth_a2dp_sink.cpp +5 −3 Original line number Diff line number Diff line Loading @@ -109,7 +109,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { ALOGI("%s: succeeds", __func__); } static void initNative(JNIEnv* env, jobject object) { static void initNative(JNIEnv* env, jobject object, jint maxConnectedAudioDevices) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == NULL) { ALOGE("Bluetooth module is not loaded"); Loading @@ -136,7 +137,8 @@ static void initNative(JNIEnv* env, jobject object) { return; } bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks); bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, maxConnectedAudioDevices); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status); sBluetoothA2dpInterface = NULL; Loading Loading @@ -243,7 +245,7 @@ static jboolean setActiveDeviceNative(JNIEnv* env, jobject object, static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void*)classInitNative}, {"initNative", "()V", (void*)initNative}, {"initNative", "(I)V", (void*)initNative}, {"cleanupNative", "()V", (void*)cleanupNative}, {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative}, {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative}, Loading src/com/android/bluetooth/a2dpsink/A2dpSinkService.java +43 −8 Original line number Diff line number Diff line Loading @@ -34,7 +34,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** Loading @@ -44,15 +43,18 @@ import java.util.concurrent.ConcurrentHashMap; public class A2dpSinkService extends ProfileService { private static final String TAG = "A2dpSinkService"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final int MAXIMUM_CONNECTED_DEVICES = 1; private int mMaxConnectedAudioDevices; private final BluetoothAdapter mAdapter; private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; protected Map<BluetoothDevice, A2dpSinkStateMachine> mDeviceStateMap = new ConcurrentHashMap<>(1); private final Object mStreamHandlerLock = new Object(); private final Object mActiveDeviceLock = new Object(); private BluetoothDevice mActiveDevice = null; private A2dpSinkStreamHandler mA2dpSinkStreamHandler; private static A2dpSinkService sService; Loading @@ -62,13 +64,16 @@ public class A2dpSinkService extends ProfileService { @Override protected boolean start() { mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), "AdapterService cannot be null when A2dpSinkService starts"); mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), "DatabaseManager cannot be null when A2dpSinkService starts"); synchronized (mStreamHandlerLock) { mA2dpSinkStreamHandler = new A2dpSinkStreamHandler(this, this); } initNative(); mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); initNative(mMaxConnectedAudioDevices); setA2dpSinkService(this); return true; } Loading Loading @@ -104,8 +109,36 @@ public class A2dpSinkService extends ProfileService { } public A2dpSinkService() { mAdapter = BluetoothAdapter.getDefaultAdapter(); public A2dpSinkService() {} /** * Set the device that should be allowed to actively stream */ public boolean setActiveDevice(BluetoothDevice device) { // Translate to byte address for JNI. Use an all 0 MAC for no active device byte[] address = null; if (device != null) { address = Utils.getByteAddress(device); } else { address = Utils.getBytesFromAddress("00:00:00:00:00:00"); } synchronized (mActiveDeviceLock) { if (setActiveDeviceNative(address)) { mActiveDevice = device; return true; } return false; } } /** * Get the device that is allowed to be actively streaming */ public BluetoothDevice getActiveDevice() { synchronized (mActiveDeviceLock) { return mActiveDevice; } } /** Loading Loading @@ -343,7 +376,7 @@ public class A2dpSinkService extends ProfileService { List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); List<BluetoothDevice> deviceList = new ArrayList<>(); Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); int connectionState; for (BluetoothDevice device : bondedDevices) { connectionState = getConnectionState(device); Loading Loading @@ -425,6 +458,8 @@ public class A2dpSinkService extends ProfileService { @Override public void dump(StringBuilder sb) { super.dump(sb); ProfileService.println(sb, "Active Device = " + getActiveDevice()); ProfileService.println(sb, "Max Connected Devices = " + mMaxConnectedAudioDevices); ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size()); for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) { ProfileService.println(sb, Loading @@ -446,7 +481,7 @@ public class A2dpSinkService extends ProfileService { private static native void classInitNative(); private native void initNative(); private native void initNative(int maxConnectedAudioDevices); private native void cleanupNative(); Loading src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +6 −6 Original line number Diff line number Diff line Loading @@ -249,17 +249,16 @@ class AvrcpControllerStateMachine extends StateMachine { */ boolean setActive(boolean becomeActive) { logD("setActive(" + becomeActive + ")"); if (becomeActive) { if (isActive()) { return true; } A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; } if (becomeActive) { if (isActive()) { return true; } if (a2dpSinkService.setActiveDeviceNative(mDeviceAddress)) { if (a2dpSinkService.setActiveDevice(mDevice)) { sActiveDevice = mDevice; BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); Loading @@ -268,6 +267,7 @@ class AvrcpControllerStateMachine extends StateMachine { return mDevice == sActiveDevice; } else if (isActive()) { sActiveDevice = null; a2dpSinkService.setActiveDevice(null); BluetoothMediaBrowserService.trackChanged(null); BluetoothMediaBrowserService.addressedPlayerChanged(null); } Loading tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ public class A2dpSinkServiceTest { MockitoAnnotations.initMocks(this); TestUtils.setAdapterService(mAdapterService); doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); setMaxConnectedAudioDevices(1); TestUtils.startService(mServiceRule, A2dpSinkService.class); mService = A2dpSinkService.getA2dpSinkService(); Assert.assertNotNull(mService); Loading @@ -85,6 +86,13 @@ public class A2dpSinkServiceTest { return mAdapter.getRemoteDevice(address); } /** * Set the upper connected device limit */ private void setMaxConnectedAudioDevices(int maxConnectedAudioDevices) { when(mAdapterService.getMaxConnectedAudioDevices()).thenReturn(maxConnectedAudioDevices); } /** * Mock the priority of a bluetooth device * Loading Loading @@ -120,4 +128,30 @@ public class A2dpSinkServiceTest { mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); Assert.assertFalse(mService.connect(device)); } /** * Test that we can connect multiple devices */ @Test public void testConnectMultipleDevices() { setMaxConnectedAudioDevices(5); BluetoothDevice device1 = makeBluetoothDevice("11:11:11:11:11:11"); BluetoothDevice device2 = makeBluetoothDevice("22:22:22:22:22:22"); BluetoothDevice device3 = makeBluetoothDevice("33:33:33:33:33:33"); BluetoothDevice device4 = makeBluetoothDevice("44:44:44:44:44:44"); BluetoothDevice device5 = makeBluetoothDevice("55:55:55:55:55:55"); mockDevicePriority(device1, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device2, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device3, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device4, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device5, BluetoothProfile.CONNECTION_POLICY_ALLOWED); Assert.assertTrue(mService.connect(device1)); Assert.assertTrue(mService.connect(device2)); Assert.assertTrue(mService.connect(device3)); Assert.assertTrue(mService.connect(device4)); Assert.assertTrue(mService.connect(device5)); } } tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,7 @@ public class AvrcpControllerStateMachineTest { TestUtils.setAdapterService(mA2dpAdapterService); doReturn(mDatabaseManager).when(mA2dpAdapterService).getDatabase(); TestUtils.startService(mA2dpServiceRule, A2dpSinkService.class); when(mA2dpSinkService.setActiveDeviceNative(any())).thenReturn(true); when(mA2dpSinkService.setActiveDevice(any())).thenReturn(true); when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus)) .thenReturn(true); Loading Loading
jni/com_android_bluetooth_a2dp_sink.cpp +5 −3 Original line number Diff line number Diff line Loading @@ -109,7 +109,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { ALOGI("%s: succeeds", __func__); } static void initNative(JNIEnv* env, jobject object) { static void initNative(JNIEnv* env, jobject object, jint maxConnectedAudioDevices) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == NULL) { ALOGE("Bluetooth module is not loaded"); Loading @@ -136,7 +137,8 @@ static void initNative(JNIEnv* env, jobject object) { return; } bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks); bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, maxConnectedAudioDevices); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status); sBluetoothA2dpInterface = NULL; Loading Loading @@ -243,7 +245,7 @@ static jboolean setActiveDeviceNative(JNIEnv* env, jobject object, static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void*)classInitNative}, {"initNative", "()V", (void*)initNative}, {"initNative", "(I)V", (void*)initNative}, {"cleanupNative", "()V", (void*)cleanupNative}, {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative}, {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative}, Loading
src/com/android/bluetooth/a2dpsink/A2dpSinkService.java +43 −8 Original line number Diff line number Diff line Loading @@ -34,7 +34,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** Loading @@ -44,15 +43,18 @@ import java.util.concurrent.ConcurrentHashMap; public class A2dpSinkService extends ProfileService { private static final String TAG = "A2dpSinkService"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); static final int MAXIMUM_CONNECTED_DEVICES = 1; private int mMaxConnectedAudioDevices; private final BluetoothAdapter mAdapter; private AdapterService mAdapterService; private DatabaseManager mDatabaseManager; protected Map<BluetoothDevice, A2dpSinkStateMachine> mDeviceStateMap = new ConcurrentHashMap<>(1); private final Object mStreamHandlerLock = new Object(); private final Object mActiveDeviceLock = new Object(); private BluetoothDevice mActiveDevice = null; private A2dpSinkStreamHandler mA2dpSinkStreamHandler; private static A2dpSinkService sService; Loading @@ -62,13 +64,16 @@ public class A2dpSinkService extends ProfileService { @Override protected boolean start() { mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), "AdapterService cannot be null when A2dpSinkService starts"); mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), "DatabaseManager cannot be null when A2dpSinkService starts"); synchronized (mStreamHandlerLock) { mA2dpSinkStreamHandler = new A2dpSinkStreamHandler(this, this); } initNative(); mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); initNative(mMaxConnectedAudioDevices); setA2dpSinkService(this); return true; } Loading Loading @@ -104,8 +109,36 @@ public class A2dpSinkService extends ProfileService { } public A2dpSinkService() { mAdapter = BluetoothAdapter.getDefaultAdapter(); public A2dpSinkService() {} /** * Set the device that should be allowed to actively stream */ public boolean setActiveDevice(BluetoothDevice device) { // Translate to byte address for JNI. Use an all 0 MAC for no active device byte[] address = null; if (device != null) { address = Utils.getByteAddress(device); } else { address = Utils.getBytesFromAddress("00:00:00:00:00:00"); } synchronized (mActiveDeviceLock) { if (setActiveDeviceNative(address)) { mActiveDevice = device; return true; } return false; } } /** * Get the device that is allowed to be actively streaming */ public BluetoothDevice getActiveDevice() { synchronized (mActiveDeviceLock) { return mActiveDevice; } } /** Loading Loading @@ -343,7 +376,7 @@ public class A2dpSinkService extends ProfileService { List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); List<BluetoothDevice> deviceList = new ArrayList<>(); Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); int connectionState; for (BluetoothDevice device : bondedDevices) { connectionState = getConnectionState(device); Loading Loading @@ -425,6 +458,8 @@ public class A2dpSinkService extends ProfileService { @Override public void dump(StringBuilder sb) { super.dump(sb); ProfileService.println(sb, "Active Device = " + getActiveDevice()); ProfileService.println(sb, "Max Connected Devices = " + mMaxConnectedAudioDevices); ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size()); for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) { ProfileService.println(sb, Loading @@ -446,7 +481,7 @@ public class A2dpSinkService extends ProfileService { private static native void classInitNative(); private native void initNative(); private native void initNative(int maxConnectedAudioDevices); private native void cleanupNative(); Loading
src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +6 −6 Original line number Diff line number Diff line Loading @@ -249,17 +249,16 @@ class AvrcpControllerStateMachine extends StateMachine { */ boolean setActive(boolean becomeActive) { logD("setActive(" + becomeActive + ")"); if (becomeActive) { if (isActive()) { return true; } A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; } if (becomeActive) { if (isActive()) { return true; } if (a2dpSinkService.setActiveDeviceNative(mDeviceAddress)) { if (a2dpSinkService.setActiveDevice(mDevice)) { sActiveDevice = mDevice; BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks); BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState()); Loading @@ -268,6 +267,7 @@ class AvrcpControllerStateMachine extends StateMachine { return mDevice == sActiveDevice; } else if (isActive()) { sActiveDevice = null; a2dpSinkService.setActiveDevice(null); BluetoothMediaBrowserService.trackChanged(null); BluetoothMediaBrowserService.addressedPlayerChanged(null); } Loading
tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ public class A2dpSinkServiceTest { MockitoAnnotations.initMocks(this); TestUtils.setAdapterService(mAdapterService); doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); setMaxConnectedAudioDevices(1); TestUtils.startService(mServiceRule, A2dpSinkService.class); mService = A2dpSinkService.getA2dpSinkService(); Assert.assertNotNull(mService); Loading @@ -85,6 +86,13 @@ public class A2dpSinkServiceTest { return mAdapter.getRemoteDevice(address); } /** * Set the upper connected device limit */ private void setMaxConnectedAudioDevices(int maxConnectedAudioDevices) { when(mAdapterService.getMaxConnectedAudioDevices()).thenReturn(maxConnectedAudioDevices); } /** * Mock the priority of a bluetooth device * Loading Loading @@ -120,4 +128,30 @@ public class A2dpSinkServiceTest { mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); Assert.assertFalse(mService.connect(device)); } /** * Test that we can connect multiple devices */ @Test public void testConnectMultipleDevices() { setMaxConnectedAudioDevices(5); BluetoothDevice device1 = makeBluetoothDevice("11:11:11:11:11:11"); BluetoothDevice device2 = makeBluetoothDevice("22:22:22:22:22:22"); BluetoothDevice device3 = makeBluetoothDevice("33:33:33:33:33:33"); BluetoothDevice device4 = makeBluetoothDevice("44:44:44:44:44:44"); BluetoothDevice device5 = makeBluetoothDevice("55:55:55:55:55:55"); mockDevicePriority(device1, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device2, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device3, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device4, BluetoothProfile.CONNECTION_POLICY_ALLOWED); mockDevicePriority(device5, BluetoothProfile.CONNECTION_POLICY_ALLOWED); Assert.assertTrue(mService.connect(device1)); Assert.assertTrue(mService.connect(device2)); Assert.assertTrue(mService.connect(device3)); Assert.assertTrue(mService.connect(device4)); Assert.assertTrue(mService.connect(device5)); } }
tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,7 @@ public class AvrcpControllerStateMachineTest { TestUtils.setAdapterService(mA2dpAdapterService); doReturn(mDatabaseManager).when(mA2dpAdapterService).getDatabase(); TestUtils.startService(mA2dpServiceRule, A2dpSinkService.class); when(mA2dpSinkService.setActiveDeviceNative(any())).thenReturn(true); when(mA2dpSinkService.setActiveDevice(any())).thenReturn(true); when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus)) .thenReturn(true); Loading