1 package com.android.bluetooth.btservice;
2 
3 import static android.Manifest.permission.BLUETOOTH_CONNECT;
4 import static org.mockito.Mockito.*;
5 
6 import android.bluetooth.BluetoothAdapter;
7 import android.bluetooth.BluetoothAssignedNumbers;
8 import android.bluetooth.BluetoothDevice;
9 import android.bluetooth.BluetoothHeadset;
10 import android.bluetooth.BluetoothProfile;
11 import android.content.Intent;
12 import android.os.Bundle;
13 import android.os.HandlerThread;
14 import android.os.Message;
15 import android.os.TestLooperManager;
16 
17 import androidx.test.InstrumentationRegistry;
18 import androidx.test.filters.MediumTest;
19 import androidx.test.runner.AndroidJUnit4;
20 
21 import com.android.bluetooth.Utils;
22 import com.android.bluetooth.hfp.HeadsetHalConstants;
23 
24 import org.junit.After;
25 import org.junit.Assert;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.mockito.ArgumentCaptor;
30 import org.mockito.Mock;
31 import org.mockito.MockitoAnnotations;
32 
33 import java.util.ArrayList;
34 
35 @MediumTest
36 @RunWith(AndroidJUnit4.class)
37 public class RemoteDevicesTest {
38     private static final String TEST_BT_ADDR_1 = "00:11:22:33:44:55";
39 
40     private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class);
41     private ArgumentCaptor<String> mStringArgument = ArgumentCaptor.forClass(String.class);
42     private BluetoothDevice mDevice1;
43     private RemoteDevices mRemoteDevices;
44     private HandlerThread mHandlerThread;
45     private TestLooperManager mTestLooperManager;
46 
47     @Mock private AdapterService mAdapterService;
48 
49     @Before
setUp()50     public void setUp() {
51         MockitoAnnotations.initMocks(this);
52         mDevice1 = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(TEST_BT_ADDR_1);
53         mHandlerThread = new HandlerThread("RemoteDevicesTestHandlerThread");
54         mHandlerThread.start();
55         mTestLooperManager = InstrumentationRegistry.getInstrumentation()
56                 .acquireLooperManager(mHandlerThread.getLooper());
57         mRemoteDevices = new RemoteDevices(mAdapterService, mHandlerThread.getLooper());
58     }
59 
60     @After
tearDown()61     public void tearDown() {
62         mTestLooperManager.release();
63         mHandlerThread.quit();
64     }
65 
66     @Test
testSendUuidIntent()67     public void testSendUuidIntent() {
68         // Verify that a handler message is sent by the method call
69         mRemoteDevices.updateUuids(mDevice1);
70         Message msg = mTestLooperManager.next();
71         Assert.assertNotNull(msg);
72 
73         // Verify that executing that message results in a broadcast intent
74         mTestLooperManager.execute(msg);
75         verify(mAdapterService).sendBroadcast(any(), anyString(), any());
76         verifyNoMoreInteractions(mAdapterService);
77     }
78 
79     @Test
testUpdateBatteryLevel_normalSequence()80     public void testUpdateBatteryLevel_normalSequence() {
81         int batteryLevel = 10;
82 
83         // Verify that device property is null initially
84         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
85 
86         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent
87         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
88         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
89                 any(Bundle.class));
90         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
91         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
92 
93         // Verify that user can get battery level after the update
94         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
95         Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
96                 batteryLevel);
97 
98         // Verify that update same battery level for the same device does not trigger intent
99         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
100         verify(mAdapterService).sendBroadcast(any(), anyString(), any());
101 
102         // Verify that updating battery level to different value triggers the intent again
103         batteryLevel = 15;
104         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
105         verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(),
106                 mStringArgument.capture(), any(Bundle.class));
107         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
108         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
109 
110         // Verify that user can get battery level after the update
111         Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
112                 batteryLevel);
113 
114         verifyNoMoreInteractions(mAdapterService);
115     }
116 
117     @Test
testUpdateBatteryLevel_errorNegativeValue()118     public void testUpdateBatteryLevel_errorNegativeValue() {
119         int batteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
120 
121         // Verify that device property is null initially
122         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
123 
124         // Verify that updating with invalid battery level does not trigger the intent
125         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
126         verify(mAdapterService, never()).sendBroadcast(any(), anyString(), any());
127 
128         // Verify that device property stays null after invalid update
129         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
130 
131         verifyNoMoreInteractions(mAdapterService);
132     }
133 
134     @Test
testUpdateBatteryLevel_errorTooLargeValue()135     public void testUpdateBatteryLevel_errorTooLargeValue() {
136         int batteryLevel = 101;
137 
138         // Verify that device property is null initially
139         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
140 
141         // Verify that updating invalid battery level does not trigger the intent
142         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
143         verify(mAdapterService, never()).sendBroadcast(any(), anyString(), any());
144 
145         // Verify that device property stays null after invalid update
146         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
147 
148         verifyNoMoreInteractions(mAdapterService);
149     }
150 
151     @Test
testResetBatteryLevel_testResetBeforeUpdate()152     public void testResetBatteryLevel_testResetBeforeUpdate() {
153         // Verify that device property is null initially
154         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
155 
156         // Verify that resetting battery level keeps device property null
157         mRemoteDevices.resetBatteryLevel(mDevice1);
158         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
159 
160         verifyNoMoreInteractions(mAdapterService);
161     }
162 
163     @Test
testResetBatteryLevel_testResetAfterUpdate()164     public void testResetBatteryLevel_testResetAfterUpdate() {
165         int batteryLevel = 10;
166 
167         // Verify that device property is null initially
168         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
169 
170         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent
171         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
172         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
173                 any(Bundle.class));
174         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
175         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
176 
177         // Verify that user can get battery level after the update
178         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
179         Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
180                 batteryLevel);
181 
182         // Verify that resetting battery level changes it back to BluetoothDevice
183         // .BATTERY_LEVEL_UNKNOWN
184         mRemoteDevices.resetBatteryLevel(mDevice1);
185         // Verify BATTERY_LEVEL_CHANGED intent is sent after first reset
186         verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(),
187                 mStringArgument.capture(), any(Bundle.class));
188         verifyBatteryLevelChangedIntent(mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
189                 mIntentArgument);
190         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
191         // Verify value is reset in properties
192         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
193         Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
194                 BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
195 
196         // Verify no intent is sent after second reset
197         mRemoteDevices.resetBatteryLevel(mDevice1);
198         verify(mAdapterService, times(2)).sendBroadcast(any(), anyString(),
199                 any());
200 
201         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again
202         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
203         verify(mAdapterService, times(3)).sendBroadcast(mIntentArgument.capture(),
204                 mStringArgument.capture(), any(Bundle.class));
205         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
206         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
207 
208         verifyNoMoreInteractions(mAdapterService);
209     }
210 
211     @Test
testResetBatteryLevelOnHeadsetStateChange()212     public void testResetBatteryLevelOnHeadsetStateChange() {
213         int batteryLevel = 10;
214 
215         // Verify that device property is null initially
216         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
217 
218         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent
219         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
220         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
221                 any(Bundle.class));
222         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
223         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
224 
225         // Verify that user can get battery level after the update
226         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
227         Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
228                 batteryLevel);
229 
230         // Verify that resetting battery level changes it back to BluetoothDevice
231         // .BATTERY_LEVEL_UNKNOWN
232         mRemoteDevices.onHeadsetConnectionStateChanged(
233                 getHeadsetConnectionStateChangedIntent(mDevice1,
234                         BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED));
235         // Verify BATTERY_LEVEL_CHANGED intent is sent after first reset
236         verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(),
237                 mStringArgument.capture(), any(Bundle.class));
238         verifyBatteryLevelChangedIntent(mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
239                 mIntentArgument);
240         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
241         // Verify value is reset in properties
242         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
243         Assert.assertEquals(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
244                 BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
245 
246         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again
247         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
248         verify(mAdapterService, times(3)).sendBroadcast(mIntentArgument.capture(),
249                 mStringArgument.capture(), any(Bundle.class));
250         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
251         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
252 
253         verifyNoMoreInteractions(mAdapterService);
254     }
255 
256     @Test
testResetBatteryLevel_testAclStateChangeCallback()257     public void testResetBatteryLevel_testAclStateChangeCallback() {
258         int batteryLevel = 10;
259 
260         // Verify that device property is null initially
261         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
262 
263         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent
264         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
265         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
266                 any(Bundle.class));
267         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
268         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
269 
270         // Verify that user can get battery level after the update
271         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
272         Assert.assertEquals(batteryLevel,
273                 mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
274 
275         // Verify that when device is completely disconnected, RemoteDevices reset battery level to
276         // BluetoothDevice.BATTERY_LEVEL_UNKNOWN
277         when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON);
278         mRemoteDevices.aclStateChangeCallback(0, Utils.getByteAddress(mDevice1),
279                 AbstractionLayer.BT_ACL_STATE_DISCONNECTED, 19); // HCI code 19 remote terminated
280         // Verify ACTION_ACL_DISCONNECTED and BATTERY_LEVEL_CHANGED intent are sent
281         verify(mAdapterService, times(3)).sendBroadcast(mIntentArgument.capture(),
282                 mStringArgument.capture(), any(Bundle.class));
283         verify(mAdapterService, times(2)).obfuscateAddress(mDevice1);
284         verifyBatteryLevelChangedIntent(mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
285                 mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 2));
286         Assert.assertEquals(BLUETOOTH_CONNECT,
287                 mStringArgument.getAllValues().get(mStringArgument.getAllValues().size() - 2));
288         Assert.assertEquals(BluetoothDevice.ACTION_ACL_DISCONNECTED,
289                 mIntentArgument.getValue().getAction());
290         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
291         // Verify value is reset in properties
292         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
293         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
294                 mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
295 
296         // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again
297         mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel);
298         verify(mAdapterService, times(4)).sendBroadcast(mIntentArgument.capture(),
299                 mStringArgument.capture(), any(Bundle.class));
300         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
301         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
302     }
303 
304     @Test
testHfIndicatorParser_testCorrectValue()305     public void testHfIndicatorParser_testCorrectValue() {
306         int batteryLevel = 10;
307 
308         // Verify that device property is null initially
309         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
310 
311         // Verify that ACTION_HF_INDICATORS_VALUE_CHANGED intent updates battery level
312         mRemoteDevices.onHfIndicatorValueChanged(getHfIndicatorIntent(mDevice1, batteryLevel,
313                 HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS));
314         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
315                 any(Bundle.class));
316         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
317         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
318     }
319 
320     @Test
testHfIndicatorParser_testWrongIndicatorId()321     public void testHfIndicatorParser_testWrongIndicatorId() {
322         int batteryLevel = 10;
323 
324         // Verify that device property is null initially
325         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
326 
327         // Verify that ACTION_HF_INDICATORS_VALUE_CHANGED intent updates battery level
328         mRemoteDevices.onHfIndicatorValueChanged(getHfIndicatorIntent(mDevice1, batteryLevel, 3));
329         verify(mAdapterService, never()).sendBroadcast(any(), anyString());
330         // Verify that device property is still null after invalid update
331         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
332     }
333 
334     @Test
testOnVendorSpecificHeadsetEvent_testCorrectPlantronicsXEvent()335     public void testOnVendorSpecificHeadsetEvent_testCorrectPlantronicsXEvent() {
336         // Verify that device property is null initially
337         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
338 
339         // Verify that correct ACTION_VENDOR_SPECIFIC_HEADSET_EVENT updates battery level
340         mRemoteDevices.onVendorSpecificHeadsetEvent(getVendorSpecificHeadsetEventIntent(
341                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT,
342                 BluetoothAssignedNumbers.PLANTRONICS, BluetoothHeadset.AT_CMD_TYPE_SET,
343                 getXEventArray(3, 8), mDevice1));
344         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
345                 any(Bundle.class));
346         verifyBatteryLevelChangedIntent(mDevice1, 42, mIntentArgument);
347         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
348     }
349 
350     @Test
testOnVendorSpecificHeadsetEvent_testCorrectAppleBatteryVsc()351     public void testOnVendorSpecificHeadsetEvent_testCorrectAppleBatteryVsc() {
352         // Verify that device property is null initially
353         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
354 
355         // Verify that correct ACTION_VENDOR_SPECIFIC_HEADSET_EVENT updates battery level
356         mRemoteDevices.onVendorSpecificHeadsetEvent(getVendorSpecificHeadsetEventIntent(
357                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV,
358                 BluetoothAssignedNumbers.APPLE, BluetoothHeadset.AT_CMD_TYPE_SET, new Object[]{
359                         3,
360                         BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
361                         5,
362                         2,
363                         1,
364                         3,
365                         10
366                 }, mDevice1));
367         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
368                 any(Bundle.class));
369         verifyBatteryLevelChangedIntent(mDevice1, 60, mIntentArgument);
370         Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
371     }
372 
373     @Test
testGetBatteryLevelFromXEventVsc()374     public void testGetBatteryLevelFromXEventVsc() {
375         Assert.assertEquals(42, RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(3, 8)));
376         Assert.assertEquals(100,
377                 RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(10, 11)));
378         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
379                 RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(1, 1)));
380         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
381                 RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(3, 1)));
382         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
383                 RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(-1, 1)));
384         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
385                 RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(-1, -1)));
386     }
387 
388     @Test
testGetBatteryLevelFromAppleBatteryVsc()389     public void testGetBatteryLevelFromAppleBatteryVsc() {
390         Assert.assertEquals(10, RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
391                 1, BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL, 0
392         }));
393         Assert.assertEquals(100, RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
394                 1, BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL, 9
395         }));
396         Assert.assertEquals(60, RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
397                 3,
398                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
399                 5,
400                 2,
401                 1,
402                 3,
403                 10
404         }));
405         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
406                 RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
407                         3,
408                         BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
409                         5,
410                         2,
411                         1,
412                         3
413                 }));
414         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
415                 RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
416                         1,
417                         BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
418                         10
419                 }));
420         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
421                 RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
422                         1,
423                         BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
424                         -1
425                 }));
426         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
427                 RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{
428                         1,
429                         BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
430                         "5"
431                 }));
432         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
433                 RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[]{1, 35, 37}));
434         Assert.assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
435                 RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
436                         new Object[]{1, "WRONG", "WRONG"}));
437     }
438 
verifyBatteryLevelChangedIntent(BluetoothDevice device, int batteryLevel, ArgumentCaptor<Intent> intentArgument)439     private static void verifyBatteryLevelChangedIntent(BluetoothDevice device, int batteryLevel,
440             ArgumentCaptor<Intent> intentArgument) {
441         verifyBatteryLevelChangedIntent(device, batteryLevel, intentArgument.getValue());
442     }
443 
verifyBatteryLevelChangedIntent(BluetoothDevice device, int batteryLevel, Intent intent)444     private static void verifyBatteryLevelChangedIntent(BluetoothDevice device, int batteryLevel,
445             Intent intent) {
446         Assert.assertEquals(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, intent.getAction());
447         Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
448         Assert.assertEquals(batteryLevel,
449                 intent.getIntExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, -15));
450         Assert.assertEquals(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
451                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, intent.getFlags());
452     }
453 
getHeadsetConnectionStateChangedIntent(BluetoothDevice device, int oldState, int newState)454     private static Intent getHeadsetConnectionStateChangedIntent(BluetoothDevice device,
455             int oldState, int newState) {
456         Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
457         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
458         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState);
459         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
460         return intent;
461     }
462 
getHfIndicatorIntent(BluetoothDevice device, int batteryLevel, int indicatorId)463     private static Intent getHfIndicatorIntent(BluetoothDevice device, int batteryLevel,
464             int indicatorId) {
465         Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
466         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
467         intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indicatorId);
468         intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, batteryLevel);
469         return intent;
470     }
471 
getVendorSpecificHeadsetEventIntent(String command, int companyId, int commandType, Object[] arguments, BluetoothDevice device)472     private static Intent getVendorSpecificHeadsetEventIntent(String command, int companyId,
473             int commandType, Object[] arguments, BluetoothDevice device) {
474         Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
475         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
476         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
477         // assert: all elements of args are Serializable
478         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
479         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
480         intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
481                 + Integer.toString(companyId));
482         return intent;
483     }
484 
getXEventArray(int batteryLevel, int numLevels)485     private static Object[] getXEventArray(int batteryLevel, int numLevels) {
486         ArrayList<Object> list = new ArrayList<>();
487         list.add(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL);
488         list.add(batteryLevel);
489         list.add(numLevels);
490         list.add(0);
491         list.add(0);
492         return list.toArray();
493     }
494 }
495