Loading android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +41 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothAssignedNumbers; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothConnectionCallback; import android.content.BroadcastReceiver; Loading Loading @@ -110,6 +111,12 @@ final class RemoteDevices { case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: onHeadsetConnectionStateChanged(intent); break; case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED: onHeadsetClientConnectionStateChanged(intent); break; case BluetoothHeadsetClient.ACTION_AG_EVENT: onAgIndicatorValueChanged(intent); break; default: Log.w(TAG, "Unhandled intent: " + intent); break; Loading Loading @@ -157,6 +164,8 @@ final class RemoteDevices { filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.APPLE); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT); sAdapterService.registerReceiver(mReceiver, filter); } Loading Loading @@ -1042,6 +1051,38 @@ final class RemoteDevices { return batteryLevel * 100 / (numberOfLevels - 1); } /** * Handles headset client connection state change event * @param intent must be {@link BluetoothHeadsetClient#ACTION_CONNECTION_STATE_CHANGED} intent */ @VisibleForTesting void onHeadsetClientConnectionStateChanged(Intent intent) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device == null) { Log.e(TAG, "onHeadsetClientConnectionStateChanged() remote device is null"); return; } if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED) == BluetoothProfile.STATE_DISCONNECTED) { // TODO: Rework this when non-HFP sources of battery level indication is added resetBatteryLevel(device); } } @VisibleForTesting void onAgIndicatorValueChanged(Intent intent) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device == null) { Log.e(TAG, "onAgIndicatorValueChanged() remote device is null"); return; } if (intent.hasExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL)) { int batteryLevel = intent.getIntExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, -1); updateBatteryLevel(device, batteryLevel); } } private static void errorLog(String msg) { Log.e(TAG, msg); } Loading android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java +113 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAssignedNumbers; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.os.Bundle; Loading Loading @@ -437,6 +438,84 @@ public class RemoteDevicesTest { new Object[]{1, "WRONG", "WRONG"})); } @Test public void testResetBatteryLevelOnHeadsetClientStateChange() { int batteryLevel = 10; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); // Verify that resetting battery level changes it back to BluetoothDevice // .BATTERY_LEVEL_UNKNOWN mRemoteDevices.onHeadsetClientConnectionStateChanged( getHeadsetClientConnectionStateChangedIntent(mDevice1, BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED)); // Verify BATTERY_LEVEL_CHANGED intent is sent after first reset verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify value is reset in properties Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), BluetoothDevice.BATTERY_LEVEL_UNKNOWN); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, times(3)).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); verifyNoMoreInteractions(mAdapterService); } @Test public void testAGIndicatorParser_testCorrectValue() { int batteryLevel = 10; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that ACTION_AG_EVENT intent updates battery level mRemoteDevices.onAgIndicatorValueChanged( getAgIndicatorIntent(mDevice1, null, null, null, new Integer(batteryLevel), null)); verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); } @Test public void testAgIndicatorParser_testWrongIndicatorId() { int batteryLevel = 10; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that ACTION_AG_EVENT intent updates battery level mRemoteDevices.onAgIndicatorValueChanged( getAgIndicatorIntent(mDevice1, new Integer(1), null, null, null, null)); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property is still null after invalid update Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); } private static void verifyBatteryLevelChangedIntent(BluetoothDevice device, int batteryLevel, ArgumentCaptor<Intent> intentArgument) { verifyBatteryLevelChangedIntent(device, batteryLevel, intentArgument.getValue()); Loading Loading @@ -492,4 +571,38 @@ public class RemoteDevicesTest { list.add(0); return list.toArray(); } private static Intent getHeadsetClientConnectionStateChangedIntent(BluetoothDevice device, int oldState, int newState) { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); return intent; } private static Intent getAgIndicatorIntent(BluetoothDevice device, Integer networkStatus, Integer networkStrength, Integer roaming, Integer batteryLevel, String operatorName) { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); if (networkStatus != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, networkStatus.intValue()); } if (networkStrength != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, networkStrength.intValue()); } if (roaming != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, roaming.intValue()); } if (batteryLevel != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, batteryLevel.intValue()); } if (operatorName != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, operatorName); } return intent; } } Loading
android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +41 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothAssignedNumbers; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothConnectionCallback; import android.content.BroadcastReceiver; Loading Loading @@ -110,6 +111,12 @@ final class RemoteDevices { case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: onHeadsetConnectionStateChanged(intent); break; case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED: onHeadsetClientConnectionStateChanged(intent); break; case BluetoothHeadsetClient.ACTION_AG_EVENT: onAgIndicatorValueChanged(intent); break; default: Log.w(TAG, "Unhandled intent: " + intent); break; Loading Loading @@ -157,6 +164,8 @@ final class RemoteDevices { filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." + BluetoothAssignedNumbers.APPLE); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT); sAdapterService.registerReceiver(mReceiver, filter); } Loading Loading @@ -1042,6 +1051,38 @@ final class RemoteDevices { return batteryLevel * 100 / (numberOfLevels - 1); } /** * Handles headset client connection state change event * @param intent must be {@link BluetoothHeadsetClient#ACTION_CONNECTION_STATE_CHANGED} intent */ @VisibleForTesting void onHeadsetClientConnectionStateChanged(Intent intent) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device == null) { Log.e(TAG, "onHeadsetClientConnectionStateChanged() remote device is null"); return; } if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED) == BluetoothProfile.STATE_DISCONNECTED) { // TODO: Rework this when non-HFP sources of battery level indication is added resetBatteryLevel(device); } } @VisibleForTesting void onAgIndicatorValueChanged(Intent intent) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device == null) { Log.e(TAG, "onAgIndicatorValueChanged() remote device is null"); return; } if (intent.hasExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL)) { int batteryLevel = intent.getIntExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, -1); updateBatteryLevel(device, batteryLevel); } } private static void errorLog(String msg) { Log.e(TAG, msg); } Loading
android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java +113 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAssignedNumbers; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.os.Bundle; Loading Loading @@ -437,6 +438,84 @@ public class RemoteDevicesTest { new Object[]{1, "WRONG", "WRONG"})); } @Test public void testResetBatteryLevelOnHeadsetClientStateChange() { int batteryLevel = 10; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); // Verify that resetting battery level changes it back to BluetoothDevice // .BATTERY_LEVEL_UNKNOWN mRemoteDevices.onHeadsetClientConnectionStateChanged( getHeadsetClientConnectionStateChangedIntent(mDevice1, BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED)); // Verify BATTERY_LEVEL_CHANGED intent is sent after first reset verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify value is reset in properties Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), BluetoothDevice.BATTERY_LEVEL_UNKNOWN); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, times(3)).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); verifyNoMoreInteractions(mAdapterService); } @Test public void testAGIndicatorParser_testCorrectValue() { int batteryLevel = 10; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that ACTION_AG_EVENT intent updates battery level mRemoteDevices.onAgIndicatorValueChanged( getAgIndicatorIntent(mDevice1, null, null, null, new Integer(batteryLevel), null)); verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class)); verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); } @Test public void testAgIndicatorParser_testWrongIndicatorId() { int batteryLevel = 10; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that ACTION_AG_EVENT intent updates battery level mRemoteDevices.onAgIndicatorValueChanged( getAgIndicatorIntent(mDevice1, new Integer(1), null, null, null, null)); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property is still null after invalid update Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); } private static void verifyBatteryLevelChangedIntent(BluetoothDevice device, int batteryLevel, ArgumentCaptor<Intent> intentArgument) { verifyBatteryLevelChangedIntent(device, batteryLevel, intentArgument.getValue()); Loading Loading @@ -492,4 +571,38 @@ public class RemoteDevicesTest { list.add(0); return list.toArray(); } private static Intent getHeadsetClientConnectionStateChangedIntent(BluetoothDevice device, int oldState, int newState) { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); return intent; } private static Intent getAgIndicatorIntent(BluetoothDevice device, Integer networkStatus, Integer networkStrength, Integer roaming, Integer batteryLevel, String operatorName) { Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); if (networkStatus != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, networkStatus.intValue()); } if (networkStrength != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, networkStrength.intValue()); } if (roaming != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, roaming.intValue()); } if (batteryLevel != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, batteryLevel.intValue()); } if (operatorName != null) { intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, operatorName); } return intent; } }