Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -1281,6 +1281,17 @@ public class AdapterService extends Service { return service.sdpSearch(device,uuid); return service.sdpSearch(device,uuid); } } public int getBatteryLevel(BluetoothDevice device) { if (!Utils.checkCaller()) { Log.w(TAG, "getBatteryLevel(): not allowed for non-active user"); return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; } AdapterService service = getService(); if (service == null) return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; return service.getBatteryLevel(device); } public boolean factoryReset() { public boolean factoryReset() { AdapterService service = getService(); AdapterService service = getService(); if (service == null) return false; if (service == null) return false; Loading Loading @@ -1695,6 +1706,13 @@ public class AdapterService extends Service { return true; return true; } } int getBatteryLevel(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; return deviceProp.getBatteryLevel(); } boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); Loading android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +83 −10 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Intent; import android.os.Handler; import android.os.Handler; import android.os.Message; import android.os.Message; import android.os.ParcelUuid; import android.os.ParcelUuid; import android.support.annotation.VisibleForTesting; import android.util.Log; import android.util.Log; import com.android.bluetooth.R; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; Loading @@ -41,7 +42,7 @@ final class RemoteDevices { private static BluetoothAdapter mAdapter; private static BluetoothAdapter mAdapter; private static AdapterService mAdapterService; private static AdapterService mAdapterService; private static ArrayList<BluetoothDevice> mSdpTracker; private static ArrayList<BluetoothDevice> mSdpTracker; private Object mObject = new Object(); private final Object mObject = new Object(); private static final int UUID_INTENT_DELAY = 6000; private static final int UUID_INTENT_DELAY = 6000; private static final int MESSAGE_UUID_INTENT = 1; private static final int MESSAGE_UUID_INTENT = 1; Loading Loading @@ -121,6 +122,7 @@ final class RemoteDevices { private int mBondState; private int mBondState; private BluetoothDevice mDevice; private BluetoothDevice mDevice; private boolean isBondingInitiatedLocally; private boolean isBondingInitiatedLocally; private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; DeviceProperties() { DeviceProperties() { mBondState = BluetoothDevice.BOND_NONE; mBondState = BluetoothDevice.BOND_NONE; Loading Loading @@ -256,6 +258,21 @@ final class RemoteDevices { return isBondingInitiatedLocally; return isBondingInitiatedLocally; } } } } int getBatteryLevel() { synchronized (mObject) { return mBatteryLevel; } } /** * @param batteryLevel the mBatteryLevel to set */ void setBatteryLevel(int batteryLevel) { synchronized (mObject) { this.mBatteryLevel = batteryLevel; } } } } private void sendUuidIntent(BluetoothDevice device) { private void sendUuidIntent(BluetoothDevice device) { Loading @@ -270,8 +287,9 @@ final class RemoteDevices { } } /** /** * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, we * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, * must add device first before setting it's properties. This is a helper method for doing that. * we must add device first before setting it's properties. This is a helper method for doing * that. */ */ void setBondingInitiatedLocally(byte[] address) { void setBondingInitiatedLocally(byte[] address) { DeviceProperties properties; DeviceProperties properties; Loading @@ -286,6 +304,57 @@ final class RemoteDevices { properties.setBondingInitiatedLocally(true); properties.setBondingInitiatedLocally(true); } } /** * Update battery level in device properties * @param device The remote device to be updated * @param batteryLevel Battery level Indicator between 0-100, * {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error */ @VisibleForTesting void updateBatteryLevel(BluetoothDevice device, int batteryLevel) { if (device == null || batteryLevel < 0 || batteryLevel > 100) { warnLog("Invalid parameters device=" + String.valueOf(device == null) + ", batteryLevel=" + String.valueOf(batteryLevel)); return; } DeviceProperties deviceProperties = getDeviceProperties(device); if (deviceProperties == null) { deviceProperties = addDeviceProperties(Utils.getByteAddress(device)); } synchronized (mObject) { int currentBatteryLevel = deviceProperties.getBatteryLevel(); if (batteryLevel == currentBatteryLevel) { debugLog("Same battery level for device " + device + " received " + String.valueOf(batteryLevel) + "%"); return; } deviceProperties.setBatteryLevel(batteryLevel); } Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); Log.d(TAG, "Updated device " + device + " battery level to " + String.valueOf(batteryLevel) + "%"); } /** * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device * @param device device whose battery level property needs to be reset */ @VisibleForTesting void resetBatteryLevel(BluetoothDevice device) { if (device == null) { warnLog("device is null"); return; } DeviceProperties deviceProperties = getDeviceProperties(device); if (deviceProperties == null) { return; } deviceProperties.setBatteryLevel(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); } void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { Intent intent; Intent intent; Loading Loading @@ -419,6 +488,10 @@ final class RemoteDevices { } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); } } // Reset battery level on complete disconnection if (mAdapterService.getConnectionState(device) == 0) { resetBatteryLevel(device); } debugLog("aclStateChangeCallback: Adapter State: " debugLog("aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state) + " Disconnected: " + device); + BluetoothAdapter.nameForState(state) + " Disconnected: " + device); } } Loading Loading @@ -459,19 +532,19 @@ final class RemoteDevices { } } }; }; private void errorLog(String msg) { private static void errorLog(String msg) { Log.e(TAG, msg); Log.e(TAG, msg); } } private void debugLog(String msg) { private static void debugLog(String msg) { if (DBG) Log.d(TAG, msg); if (DBG) Log.d(TAG, msg); } } private void infoLog(String msg) { private static void infoLog(String msg) { if (DBG) Log.i(TAG, msg); if (DBG) Log.i(TAG, msg); } } private void warnLog(String msg) { private static void warnLog(String msg) { Log.w(TAG, msg); Log.w(TAG, msg); } } Loading android/app/tests/src/com/android/bluetooth/btservice/RemoteDevicesTest.java +208 −26 Original line number Original line Diff line number Diff line package com.android.bluetooth.btservice; package com.android.bluetooth.btservice; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.support.test.runner.AndroidJUnit4; import android.os.ParcelUuid; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; import java.lang.Thread; import android.test.AndroidTestCase; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.mockito.Mockito.mock; @RunWith(AndroidJUnit4.class) import static org.mockito.Mockito.any; public class RemoteDevicesTest { import static org.mockito.Mockito.anyString; private static final String TEST_BT_ADDR_1 = "00:11:22:33:44:55"; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; public class RemoteDevicesTest extends AndroidTestCase { private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class); public void testSendUuidIntent() { private ArgumentCaptor<String> mStringArgument = ArgumentCaptor.forClass(String.class); if (Looper.myLooper() == null) Looper.prepare(); private BluetoothDevice mDevice1; private RemoteDevices mRemoteDevices; AdapterService mockService = mock(AdapterService.class); @Mock private AdapterService mAdapterService; RemoteDevices devices = new RemoteDevices(mockService); BluetoothDevice device = @Before BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:44:55"); public void setUp() { devices.updateUuids(device); MockitoAnnotations.initMocks(this); if (Looper.myLooper() == null) Looper.prepare(); mDevice1 = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_BT_ADDR_1); mRemoteDevices = new RemoteDevices(mAdapterService); } @Test public void testSendUuidIntent() { mRemoteDevices.updateUuids(mDevice1); Looper.myLooper().quitSafely(); Looper.myLooper().quitSafely(); Looper.loop(); Looper.loop(); verify(mockService).sendBroadcast(any(), anyString()); verify(mAdapterService).sendBroadcast(any(), anyString()); verifyNoMoreInteractions(mockService); verifyNoMoreInteractions(mAdapterService); } @Test public void testUpdateBatteryLevel_normalSequence() { 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, 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 update same battery level for the same device does not trigger intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService).sendBroadcast(any(), anyString()); // Verify that updating battery level to different value triggers the intent again batteryLevel = 15; mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, times(2)) .sendBroadcast(mIntentArgument.capture(), mStringArgument.capture()); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); // Verify that user can get battery level after the update Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); verifyNoMoreInteractions(mAdapterService); } @Test public void testUpdateBatteryLevel_errorNegativeValue() { int batteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that updating with invalid battery level does not trigger the intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property stays null after invalid update Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); verifyNoMoreInteractions(mAdapterService); } @Test public void testUpdateBatteryLevel_errorTooLargeValue() { int batteryLevel = 101; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that updating invalid battery level does not trigger the intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property stays null after invalid update Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); verifyNoMoreInteractions(mAdapterService); } @Test public void testResetBatteryLevel_testResetBeforeUpdate() { // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that resetting battery level keeps device property null mRemoteDevices.resetBatteryLevel(mDevice1); Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); verifyNoMoreInteractions(mAdapterService); } @Test public void testResetBatteryLevel_testResetAfterUpdate() { 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, 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.resetBatteryLevel(mDevice1); 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(2)) .sendBroadcast(mIntentArgument.capture(), mStringArgument.capture()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); verifyNoMoreInteractions(mAdapterService); } @Test public void testResetBatteryLevel_testAclStateChangeCallback() { 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); // Verify that user can get battery level after the update Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals( batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); // Verify that when device is completely disconnected, RemoteDevices reset battery level to // BluetoothDevice.BATTERY_LEVEL_UNKNOWN when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); mRemoteDevices.aclStateChangeCallback( 0, Utils.getByteAddress(mDevice1), AbstractionLayer.BT_ACL_STATE_DISCONNECTED); verify(mAdapterService).getState(); verify(mAdapterService).getConnectionState(mDevice1); verify(mAdapterService, times(2)).sendBroadcast(any(), anyString()); Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); // 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); verifyNoMoreInteractions(mAdapterService); } private static void verfyBatteryLevelChangedIntent( BluetoothDevice device, int batteryLevel, ArgumentCaptor<Intent> intentArgument) { Assert.assertEquals(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, intentArgument.getValue().getAction()); Assert.assertEquals( device, intentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); Assert.assertEquals(batteryLevel, intentArgument.getValue().getIntExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, -15)); Assert.assertEquals(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, intentArgument.getValue().getFlags()); } } } } Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +18 −0 Original line number Original line Diff line number Diff line Loading @@ -1281,6 +1281,17 @@ public class AdapterService extends Service { return service.sdpSearch(device,uuid); return service.sdpSearch(device,uuid); } } public int getBatteryLevel(BluetoothDevice device) { if (!Utils.checkCaller()) { Log.w(TAG, "getBatteryLevel(): not allowed for non-active user"); return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; } AdapterService service = getService(); if (service == null) return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; return service.getBatteryLevel(device); } public boolean factoryReset() { public boolean factoryReset() { AdapterService service = getService(); AdapterService service = getService(); if (service == null) return false; if (service == null) return false; Loading Loading @@ -1695,6 +1706,13 @@ public class AdapterService extends Service { return true; return true; } } int getBatteryLevel(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; return deviceProp.getBatteryLevel(); } boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); Loading
android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +83 −10 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Intent; import android.os.Handler; import android.os.Handler; import android.os.Message; import android.os.Message; import android.os.ParcelUuid; import android.os.ParcelUuid; import android.support.annotation.VisibleForTesting; import android.util.Log; import android.util.Log; import com.android.bluetooth.R; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; Loading @@ -41,7 +42,7 @@ final class RemoteDevices { private static BluetoothAdapter mAdapter; private static BluetoothAdapter mAdapter; private static AdapterService mAdapterService; private static AdapterService mAdapterService; private static ArrayList<BluetoothDevice> mSdpTracker; private static ArrayList<BluetoothDevice> mSdpTracker; private Object mObject = new Object(); private final Object mObject = new Object(); private static final int UUID_INTENT_DELAY = 6000; private static final int UUID_INTENT_DELAY = 6000; private static final int MESSAGE_UUID_INTENT = 1; private static final int MESSAGE_UUID_INTENT = 1; Loading Loading @@ -121,6 +122,7 @@ final class RemoteDevices { private int mBondState; private int mBondState; private BluetoothDevice mDevice; private BluetoothDevice mDevice; private boolean isBondingInitiatedLocally; private boolean isBondingInitiatedLocally; private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; DeviceProperties() { DeviceProperties() { mBondState = BluetoothDevice.BOND_NONE; mBondState = BluetoothDevice.BOND_NONE; Loading Loading @@ -256,6 +258,21 @@ final class RemoteDevices { return isBondingInitiatedLocally; return isBondingInitiatedLocally; } } } } int getBatteryLevel() { synchronized (mObject) { return mBatteryLevel; } } /** * @param batteryLevel the mBatteryLevel to set */ void setBatteryLevel(int batteryLevel) { synchronized (mObject) { this.mBatteryLevel = batteryLevel; } } } } private void sendUuidIntent(BluetoothDevice device) { private void sendUuidIntent(BluetoothDevice device) { Loading @@ -270,8 +287,9 @@ final class RemoteDevices { } } /** /** * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, we * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, * must add device first before setting it's properties. This is a helper method for doing that. * we must add device first before setting it's properties. This is a helper method for doing * that. */ */ void setBondingInitiatedLocally(byte[] address) { void setBondingInitiatedLocally(byte[] address) { DeviceProperties properties; DeviceProperties properties; Loading @@ -286,6 +304,57 @@ final class RemoteDevices { properties.setBondingInitiatedLocally(true); properties.setBondingInitiatedLocally(true); } } /** * Update battery level in device properties * @param device The remote device to be updated * @param batteryLevel Battery level Indicator between 0-100, * {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error */ @VisibleForTesting void updateBatteryLevel(BluetoothDevice device, int batteryLevel) { if (device == null || batteryLevel < 0 || batteryLevel > 100) { warnLog("Invalid parameters device=" + String.valueOf(device == null) + ", batteryLevel=" + String.valueOf(batteryLevel)); return; } DeviceProperties deviceProperties = getDeviceProperties(device); if (deviceProperties == null) { deviceProperties = addDeviceProperties(Utils.getByteAddress(device)); } synchronized (mObject) { int currentBatteryLevel = deviceProperties.getBatteryLevel(); if (batteryLevel == currentBatteryLevel) { debugLog("Same battery level for device " + device + " received " + String.valueOf(batteryLevel) + "%"); return; } deviceProperties.setBatteryLevel(batteryLevel); } Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); Log.d(TAG, "Updated device " + device + " battery level to " + String.valueOf(batteryLevel) + "%"); } /** * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device * @param device device whose battery level property needs to be reset */ @VisibleForTesting void resetBatteryLevel(BluetoothDevice device) { if (device == null) { warnLog("device is null"); return; } DeviceProperties deviceProperties = getDeviceProperties(device); if (deviceProperties == null) { return; } deviceProperties.setBatteryLevel(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); } void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { Intent intent; Intent intent; Loading Loading @@ -419,6 +488,10 @@ final class RemoteDevices { } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); } } // Reset battery level on complete disconnection if (mAdapterService.getConnectionState(device) == 0) { resetBatteryLevel(device); } debugLog("aclStateChangeCallback: Adapter State: " debugLog("aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state) + " Disconnected: " + device); + BluetoothAdapter.nameForState(state) + " Disconnected: " + device); } } Loading Loading @@ -459,19 +532,19 @@ final class RemoteDevices { } } }; }; private void errorLog(String msg) { private static void errorLog(String msg) { Log.e(TAG, msg); Log.e(TAG, msg); } } private void debugLog(String msg) { private static void debugLog(String msg) { if (DBG) Log.d(TAG, msg); if (DBG) Log.d(TAG, msg); } } private void infoLog(String msg) { private static void infoLog(String msg) { if (DBG) Log.i(TAG, msg); if (DBG) Log.i(TAG, msg); } } private void warnLog(String msg) { private static void warnLog(String msg) { Log.w(TAG, msg); Log.w(TAG, msg); } } Loading
android/app/tests/src/com/android/bluetooth/btservice/RemoteDevicesTest.java +208 −26 Original line number Original line Diff line number Diff line package com.android.bluetooth.btservice; package com.android.bluetooth.btservice; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.support.test.runner.AndroidJUnit4; import android.os.ParcelUuid; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.Utils; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; import java.lang.Thread; import android.test.AndroidTestCase; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.mockito.Mockito.mock; @RunWith(AndroidJUnit4.class) import static org.mockito.Mockito.any; public class RemoteDevicesTest { import static org.mockito.Mockito.anyString; private static final String TEST_BT_ADDR_1 = "00:11:22:33:44:55"; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; public class RemoteDevicesTest extends AndroidTestCase { private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class); public void testSendUuidIntent() { private ArgumentCaptor<String> mStringArgument = ArgumentCaptor.forClass(String.class); if (Looper.myLooper() == null) Looper.prepare(); private BluetoothDevice mDevice1; private RemoteDevices mRemoteDevices; AdapterService mockService = mock(AdapterService.class); @Mock private AdapterService mAdapterService; RemoteDevices devices = new RemoteDevices(mockService); BluetoothDevice device = @Before BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:11:22:33:44:55"); public void setUp() { devices.updateUuids(device); MockitoAnnotations.initMocks(this); if (Looper.myLooper() == null) Looper.prepare(); mDevice1 = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_BT_ADDR_1); mRemoteDevices = new RemoteDevices(mAdapterService); } @Test public void testSendUuidIntent() { mRemoteDevices.updateUuids(mDevice1); Looper.myLooper().quitSafely(); Looper.myLooper().quitSafely(); Looper.loop(); Looper.loop(); verify(mockService).sendBroadcast(any(), anyString()); verify(mAdapterService).sendBroadcast(any(), anyString()); verifyNoMoreInteractions(mockService); verifyNoMoreInteractions(mAdapterService); } @Test public void testUpdateBatteryLevel_normalSequence() { 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, 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 update same battery level for the same device does not trigger intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService).sendBroadcast(any(), anyString()); // Verify that updating battery level to different value triggers the intent again batteryLevel = 15; mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, times(2)) .sendBroadcast(mIntentArgument.capture(), mStringArgument.capture()); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); // Verify that user can get battery level after the update Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); verifyNoMoreInteractions(mAdapterService); } @Test public void testUpdateBatteryLevel_errorNegativeValue() { int batteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that updating with invalid battery level does not trigger the intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property stays null after invalid update Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); verifyNoMoreInteractions(mAdapterService); } @Test public void testUpdateBatteryLevel_errorTooLargeValue() { int batteryLevel = 101; // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that updating invalid battery level does not trigger the intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property stays null after invalid update Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); verifyNoMoreInteractions(mAdapterService); } @Test public void testResetBatteryLevel_testResetBeforeUpdate() { // Verify that device property is null initially Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); // Verify that resetting battery level keeps device property null mRemoteDevices.resetBatteryLevel(mDevice1); Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); verifyNoMoreInteractions(mAdapterService); } @Test public void testResetBatteryLevel_testResetAfterUpdate() { 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, 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.resetBatteryLevel(mDevice1); 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(2)) .sendBroadcast(mIntentArgument.capture(), mStringArgument.capture()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); verifyNoMoreInteractions(mAdapterService); } @Test public void testResetBatteryLevel_testAclStateChangeCallback() { 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); // Verify that user can get battery level after the update Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals( batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); // Verify that when device is completely disconnected, RemoteDevices reset battery level to // BluetoothDevice.BATTERY_LEVEL_UNKNOWN when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON); mRemoteDevices.aclStateChangeCallback( 0, Utils.getByteAddress(mDevice1), AbstractionLayer.BT_ACL_STATE_DISCONNECTED); verify(mAdapterService).getState(); verify(mAdapterService).getConnectionState(mDevice1); verify(mAdapterService, times(2)).sendBroadcast(any(), anyString()); Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); // 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()); verfyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument); Assert.assertEquals(AdapterService.BLUETOOTH_PERM, mStringArgument.getValue()); verifyNoMoreInteractions(mAdapterService); } private static void verfyBatteryLevelChangedIntent( BluetoothDevice device, int batteryLevel, ArgumentCaptor<Intent> intentArgument) { Assert.assertEquals(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, intentArgument.getValue().getAction()); Assert.assertEquals( device, intentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); Assert.assertEquals(batteryLevel, intentArgument.getValue().getIntExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, -15)); Assert.assertEquals(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, intentArgument.getValue().getFlags()); } } } }