Loading android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +37 −14 Original line number Diff line number Diff line Loading @@ -113,6 +113,8 @@ public class BassClientStateMachine extends StateMachine { static final int ARGTYPE_METADATA = 1; static final int ARGTYPE_RCVSTATE = 2; static final int ATT_WRITE_CMD_HDR_LEN = 3; /*key is combination of sourceId, Address and advSid for this hashmap*/ private final Map<Integer, BluetoothLeBroadcastReceiveState> mBluetoothLeBroadcastReceiveStates = Loading Loading @@ -169,6 +171,7 @@ public class BassClientStateMachine extends StateMachine { @VisibleForTesting BluetoothGattTestableWrapper mBluetoothGatt = null; BluetoothGattCallback mGattCallback = null; int mMaxSingleAttributeWriteValueLen = 0; BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs) { Loading Loading @@ -1037,6 +1040,11 @@ public class BassClientStateMachine extends StateMachine { log("onMtuChanged is remote initiated trigger, mBluetoothGatt:" + mBluetoothGatt); } if (status == BluetoothGatt.GATT_SUCCESS) { Log.d(TAG, "mtu: " + mtu); mMaxSingleAttributeWriteValueLen = mtu - ATT_WRITE_CMD_HDR_LEN; } } @Override Loading Loading @@ -1110,8 +1118,16 @@ public class BassClientStateMachine extends StateMachine { log("Total number of chars" + numOfChars); for (int i = 0; i < allChars.size(); i++) { if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) { int properties = allChars.get(i).getProperties(); if (((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0)) { Log.w(TAG, "Broadcast Audio Scan Control Point characteristic has invalid " + "properties!"); } else { mBroadcastScanControlPoint = allChars.get(i); log("Index of ScanCtrlPoint:" + i); } } else { log("Reading " + i + "th ReceiverState"); mBroadcastCharacteristics.add(allChars.get(i)); Loading Loading @@ -1505,6 +1521,19 @@ public class BassClientStateMachine extends StateMachine { mLastConnectionState = BluetoothProfile.STATE_CONNECTED; } private void writeBassControlPoint(byte[] value) { if (value.length > mMaxSingleAttributeWriteValueLen) { mBroadcastScanControlPoint.setWriteType( BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); } else { mBroadcastScanControlPoint.setWriteType( BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); } mBroadcastScanControlPoint.setValue(value); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); } @Override public boolean processMessage(Message message) { log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what)); Loading Loading @@ -1550,8 +1579,7 @@ public class BassClientStateMachine extends StateMachine { break; case START_SCAN_OFFLOAD: if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(REMOTE_SCAN_START); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(REMOTE_SCAN_START); mPendingOperation = message.what; transitionTo(mConnectedProcessing); } else { Loading @@ -1560,8 +1588,7 @@ public class BassClientStateMachine extends StateMachine { break; case STOP_SCAN_OFFLOAD: if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(REMOTE_SCAN_STOP); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(REMOTE_SCAN_STOP); mPendingOperation = message.what; transitionTo(mConnectedProcessing); } else { Loading Loading @@ -1593,8 +1620,7 @@ public class BassClientStateMachine extends StateMachine { break; } if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(addSourceInfo); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(addSourceInfo); mPendingOperation = message.what; mPendingMetadata = metaData; if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) { Loading @@ -1620,8 +1646,7 @@ public class BassClientStateMachine extends StateMachine { break; } if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(updateSourceInfo); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(updateSourceInfo); mPendingOperation = message.what; mPendingSourceId = (byte) sourceId; if (paSync == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE) { Loading Loading @@ -1665,8 +1690,7 @@ public class BassClientStateMachine extends StateMachine { break; } if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(setBroadcastPINcmd); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(setBroadcastPINcmd); mPendingOperation = message.what; mPendingSourceId = (byte) recvState.getSourceId(); transitionTo(mConnectedProcessing); Loading @@ -1684,8 +1708,7 @@ public class BassClientStateMachine extends StateMachine { setPendingRemove((int) sid, false); } mBroadcastScanControlPoint.setValue(removeSourceInfo); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(removeSourceInfo); mPendingOperation = message.what; mPendingSourceId = sid; transitionTo(mConnectedProcessing); Loading android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java +68 −0 Original line number Diff line number Diff line Loading @@ -1593,6 +1593,74 @@ public class BassClientStateMachineTest { mBassClientStateMachine.dump(new StringBuilder()); } @Test public void sendAddBcastSourceMessage_NoResponseWrite() { mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; cb.onMtuChanged(null, 250, GATT_SUCCESS); initToConnectedState(); BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); when(mBassClientService.getCallbacks()).thenReturn(callbacks); BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); // verify local broadcast doesn't require active synced source when(mBassClientService.isLocalBroadcast(any())).thenReturn(true); mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); verify(mBassClientService).getCallbacks(); verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); mBassClientStateMachine.mBluetoothGatt = btGatt; BluetoothGattCharacteristic scanControlPoint = Mockito.mock(BluetoothGattCharacteristic.class); mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; sendMessageAndVerifyTransition( mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), BassClientStateMachine.ConnectedProcessing.class); verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); verify(scanControlPoint).setValue(any(byte[].class)); verify(btGatt).writeCharacteristic(any()); } @Test public void sendAddBcastSourceMessage_LongWrite() { mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; cb.onMtuChanged(null, 23, GATT_SUCCESS); initToConnectedState(); BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); when(mBassClientService.getCallbacks()).thenReturn(callbacks); BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); // verify local broadcast doesn't require active synced source when(mBassClientService.isLocalBroadcast(any())).thenReturn(true); mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); verify(mBassClientService).getCallbacks(); verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); mBassClientStateMachine.mBluetoothGatt = btGatt; BluetoothGattCharacteristic scanControlPoint = Mockito.mock(BluetoothGattCharacteristic.class); mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; sendMessageAndVerifyTransition( mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), BassClientStateMachine.ConnectedProcessing.class); verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); verify(scanControlPoint).setValue(any(byte[].class)); verify(btGatt).writeCharacteristic(any()); } private void initToDisconnectedState() { allowConnection(true); allowConnectGatt(true); Loading Loading
android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +37 −14 Original line number Diff line number Diff line Loading @@ -113,6 +113,8 @@ public class BassClientStateMachine extends StateMachine { static final int ARGTYPE_METADATA = 1; static final int ARGTYPE_RCVSTATE = 2; static final int ATT_WRITE_CMD_HDR_LEN = 3; /*key is combination of sourceId, Address and advSid for this hashmap*/ private final Map<Integer, BluetoothLeBroadcastReceiveState> mBluetoothLeBroadcastReceiveStates = Loading Loading @@ -169,6 +171,7 @@ public class BassClientStateMachine extends StateMachine { @VisibleForTesting BluetoothGattTestableWrapper mBluetoothGatt = null; BluetoothGattCallback mGattCallback = null; int mMaxSingleAttributeWriteValueLen = 0; BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs) { Loading Loading @@ -1037,6 +1040,11 @@ public class BassClientStateMachine extends StateMachine { log("onMtuChanged is remote initiated trigger, mBluetoothGatt:" + mBluetoothGatt); } if (status == BluetoothGatt.GATT_SUCCESS) { Log.d(TAG, "mtu: " + mtu); mMaxSingleAttributeWriteValueLen = mtu - ATT_WRITE_CMD_HDR_LEN; } } @Override Loading Loading @@ -1110,8 +1118,16 @@ public class BassClientStateMachine extends StateMachine { log("Total number of chars" + numOfChars); for (int i = 0; i < allChars.size(); i++) { if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) { int properties = allChars.get(i).getProperties(); if (((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0)) { Log.w(TAG, "Broadcast Audio Scan Control Point characteristic has invalid " + "properties!"); } else { mBroadcastScanControlPoint = allChars.get(i); log("Index of ScanCtrlPoint:" + i); } } else { log("Reading " + i + "th ReceiverState"); mBroadcastCharacteristics.add(allChars.get(i)); Loading Loading @@ -1505,6 +1521,19 @@ public class BassClientStateMachine extends StateMachine { mLastConnectionState = BluetoothProfile.STATE_CONNECTED; } private void writeBassControlPoint(byte[] value) { if (value.length > mMaxSingleAttributeWriteValueLen) { mBroadcastScanControlPoint.setWriteType( BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); } else { mBroadcastScanControlPoint.setWriteType( BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); } mBroadcastScanControlPoint.setValue(value); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); } @Override public boolean processMessage(Message message) { log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what)); Loading Loading @@ -1550,8 +1579,7 @@ public class BassClientStateMachine extends StateMachine { break; case START_SCAN_OFFLOAD: if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(REMOTE_SCAN_START); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(REMOTE_SCAN_START); mPendingOperation = message.what; transitionTo(mConnectedProcessing); } else { Loading @@ -1560,8 +1588,7 @@ public class BassClientStateMachine extends StateMachine { break; case STOP_SCAN_OFFLOAD: if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(REMOTE_SCAN_STOP); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(REMOTE_SCAN_STOP); mPendingOperation = message.what; transitionTo(mConnectedProcessing); } else { Loading Loading @@ -1593,8 +1620,7 @@ public class BassClientStateMachine extends StateMachine { break; } if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(addSourceInfo); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(addSourceInfo); mPendingOperation = message.what; mPendingMetadata = metaData; if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) { Loading @@ -1620,8 +1646,7 @@ public class BassClientStateMachine extends StateMachine { break; } if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(updateSourceInfo); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(updateSourceInfo); mPendingOperation = message.what; mPendingSourceId = (byte) sourceId; if (paSync == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE) { Loading Loading @@ -1665,8 +1690,7 @@ public class BassClientStateMachine extends StateMachine { break; } if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { mBroadcastScanControlPoint.setValue(setBroadcastPINcmd); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(setBroadcastPINcmd); mPendingOperation = message.what; mPendingSourceId = (byte) recvState.getSourceId(); transitionTo(mConnectedProcessing); Loading @@ -1684,8 +1708,7 @@ public class BassClientStateMachine extends StateMachine { setPendingRemove((int) sid, false); } mBroadcastScanControlPoint.setValue(removeSourceInfo); mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); writeBassControlPoint(removeSourceInfo); mPendingOperation = message.what; mPendingSourceId = sid; transitionTo(mConnectedProcessing); Loading
android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java +68 −0 Original line number Diff line number Diff line Loading @@ -1593,6 +1593,74 @@ public class BassClientStateMachineTest { mBassClientStateMachine.dump(new StringBuilder()); } @Test public void sendAddBcastSourceMessage_NoResponseWrite() { mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; cb.onMtuChanged(null, 250, GATT_SUCCESS); initToConnectedState(); BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); when(mBassClientService.getCallbacks()).thenReturn(callbacks); BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); // verify local broadcast doesn't require active synced source when(mBassClientService.isLocalBroadcast(any())).thenReturn(true); mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); verify(mBassClientService).getCallbacks(); verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); mBassClientStateMachine.mBluetoothGatt = btGatt; BluetoothGattCharacteristic scanControlPoint = Mockito.mock(BluetoothGattCharacteristic.class); mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; sendMessageAndVerifyTransition( mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), BassClientStateMachine.ConnectedProcessing.class); verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); verify(scanControlPoint).setValue(any(byte[].class)); verify(btGatt).writeCharacteristic(any()); } @Test public void sendAddBcastSourceMessage_LongWrite() { mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; cb.onMtuChanged(null, 23, GATT_SUCCESS); initToConnectedState(); BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); when(mBassClientService.getCallbacks()).thenReturn(callbacks); BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); // verify local broadcast doesn't require active synced source when(mBassClientService.isLocalBroadcast(any())).thenReturn(true); mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); verify(mBassClientService).getCallbacks(); verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); mBassClientStateMachine.mBluetoothGatt = btGatt; BluetoothGattCharacteristic scanControlPoint = Mockito.mock(BluetoothGattCharacteristic.class); mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; sendMessageAndVerifyTransition( mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), BassClientStateMachine.ConnectedProcessing.class); verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); verify(scanControlPoint).setValue(any(byte[].class)); verify(btGatt).writeCharacteristic(any()); } private void initToDisconnectedState() { allowConnection(true); allowConnectGatt(true); Loading