• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.hid;
18 
19 import static org.mockito.Mockito.*;
20 
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHidDevice;
24 import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
25 import android.bluetooth.BluetoothProfile;
26 import android.bluetooth.IBluetoothHidDeviceCallback;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.os.Looper;
32 
33 import androidx.test.InstrumentationRegistry;
34 import androidx.test.filters.MediumTest;
35 import androidx.test.rule.ServiceTestRule;
36 import androidx.test.runner.AndroidJUnit4;
37 
38 import com.android.bluetooth.R;
39 import com.android.bluetooth.TestUtils;
40 import com.android.bluetooth.btservice.AdapterService;
41 
42 import org.junit.After;
43 import org.junit.Assert;
44 import org.junit.Assume;
45 import org.junit.Before;
46 import org.junit.Rule;
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 import org.mockito.Mock;
50 import org.mockito.MockitoAnnotations;
51 
52 import java.lang.reflect.Field;
53 import java.lang.reflect.Method;
54 import java.util.concurrent.BlockingQueue;
55 import java.util.concurrent.LinkedBlockingQueue;
56 import java.util.concurrent.TimeUnit;
57 
58 @MediumTest
59 @RunWith(AndroidJUnit4.class)
60 public class HidDeviceTest {
61     private static final int TIMEOUT_MS = 1000;    // 1s
62     private static final byte[] SAMPLE_HID_REPORT = new byte[]{0x01, 0x00, 0x02};
63     private static final byte SAMPLE_REPORT_ID = 0x00;
64     private static final byte SAMPLE_REPORT_TYPE = 0x00;
65     private static final byte SAMPLE_REPORT_ERROR = 0x02;
66     private static final byte SAMPLE_BUFFER_SIZE = 100;
67 
68     private static final int CALLBACK_APP_REGISTERED = 0;
69     private static final int CALLBACK_APP_UNREGISTERED = 1;
70     private static final int CALLBACK_ON_GET_REPORT = 2;
71     private static final int CALLBACK_ON_SET_REPORT = 3;
72     private static final int CALLBACK_ON_SET_PROTOCOL = 4;
73     private static final int CALLBACK_ON_INTR_DATA = 5;
74     private static final int CALLBACK_ON_VIRTUAL_UNPLUG = 6;
75 
76     @Mock private AdapterService mAdapterService;
77     @Mock private HidDeviceNativeInterface mHidDeviceNativeInterface;
78 
79     private BluetoothAdapter mAdapter;
80     private BluetoothDevice mTestDevice;
81     private HidDeviceService mHidDeviceService;
82     private Context mTargetContext;
83     private BluetoothHidDeviceAppSdpSettings mSettings;
84     private BroadcastReceiver mConnectionStateChangedReceiver;
85     private final BlockingQueue<Intent> mConnectionStateChangedQueue = new LinkedBlockingQueue<>();
86     private final BlockingQueue<Integer> mCallbackQueue = new LinkedBlockingQueue<>();
87 
88     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
89 
setHidDeviceNativeInterfaceInstance(HidDeviceNativeInterface instance)90     private static void setHidDeviceNativeInterfaceInstance(HidDeviceNativeInterface instance)
91             throws Exception {
92         Method method = HidDeviceNativeInterface.class.getDeclaredMethod("setInstance",
93                 HidDeviceNativeInterface.class);
94         method.setAccessible(true);
95         method.invoke(null, instance);
96     }
97 
98     @Before
setUp()99     public void setUp() throws Exception {
100         mTargetContext = InstrumentationRegistry.getTargetContext();
101         Assume.assumeTrue("Ignore test when HidDeviceService is not enabled",
102                 mTargetContext.getResources().getBoolean(R.bool.profile_supported_hid_device));
103         if (Looper.myLooper() == null) {
104             Looper.prepare();
105         }
106         Assert.assertNotNull(Looper.myLooper());
107 
108         // Set up mocks and test assets
109         MockitoAnnotations.initMocks(this);
110         TestUtils.setAdapterService(mAdapterService);
111         setHidDeviceNativeInterfaceInstance(mHidDeviceNativeInterface);
112         // This line must be called to make sure relevant objects are initialized properly
113         mAdapter = BluetoothAdapter.getDefaultAdapter();
114         // Get a device for testing
115         mTestDevice = mAdapter.getRemoteDevice("10:11:12:13:14:15");
116 
117         TestUtils.startService(mServiceRule, HidDeviceService.class);
118         mHidDeviceService = HidDeviceService.getHidDeviceService();
119         Assert.assertNotNull(mHidDeviceService);
120 
121         // Force unregister app first
122         mHidDeviceService.unregisterApp();
123 
124         Field field = HidDeviceService.class.getDeclaredField("mHidDeviceNativeInterface");
125         field.setAccessible(true);
126         HidDeviceNativeInterface nativeInterface =
127                 (HidDeviceNativeInterface) field.get(mHidDeviceService);
128         Assert.assertEquals(nativeInterface, mHidDeviceNativeInterface);
129 
130         // Dummy SDP settings
131         mSettings = new BluetoothHidDeviceAppSdpSettings("Unit test", "test", "Android",
132                 BluetoothHidDevice.SUBCLASS1_COMBO, new byte[]{});
133 
134         // Set up the Connection State Changed receiver
135         IntentFilter filter = new IntentFilter();
136         filter.addAction(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
137         mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
138         mTargetContext.registerReceiver(mConnectionStateChangedReceiver, filter);
139         reset(mHidDeviceNativeInterface, mAdapterService);
140     }
141 
142     @After
tearDown()143     public void tearDown() throws Exception {
144         if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_hid_device)) {
145             return;
146         }
147         TestUtils.stopService(mServiceRule, HidDeviceService.class);
148         mHidDeviceService = HidDeviceService.getHidDeviceService();
149         Assert.assertNull(mHidDeviceService);
150         mTargetContext.unregisterReceiver(mConnectionStateChangedReceiver);
151         mConnectionStateChangedQueue.clear();
152         mCallbackQueue.clear();
153         setHidDeviceNativeInterfaceInstance(null);
154         TestUtils.clearAdapterService(mAdapterService);
155     }
156 
157     private class ConnectionStateChangedReceiver extends BroadcastReceiver {
158         @Override
onReceive(Context context, Intent intent)159         public void onReceive(Context context, Intent intent) {
160             if (!BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
161                 return;
162             }
163             try {
164                 mConnectionStateChangedQueue.put(intent);
165             } catch (InterruptedException e) {
166                 Assert.fail("Cannot add Intent to the queue");
167             }
168         }
169     }
170 
waitForIntent(int timeoutMs, BlockingQueue<Intent> queue)171     private Intent waitForIntent(int timeoutMs, BlockingQueue<Intent> queue) {
172         try {
173             Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
174             Assert.assertNotNull(intent);
175             return intent;
176         } catch (InterruptedException e) {
177             Assert.fail("Cannot obtain an Intent from the queue");
178         }
179         return null;
180     }
181 
verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)182     private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState,
183             int prevState) {
184         Intent intent = waitForIntent(timeoutMs, mConnectionStateChangedQueue);
185         Assert.assertNotNull(intent);
186         Assert.assertEquals(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED, intent.getAction());
187         Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
188         Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
189         Assert.assertEquals(prevState,
190                 intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1));
191     }
192 
verifyCallback(int timeoutMs, int callbackType, BlockingQueue<Integer> queue)193     private void verifyCallback(int timeoutMs, int callbackType, BlockingQueue<Integer> queue) {
194         try {
195             Integer lastCallback = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
196             Assert.assertNotNull(lastCallback);
197             int lastCallbackType = lastCallback;
198             Assert.assertEquals(callbackType, lastCallbackType);
199         } catch (InterruptedException e) {
200             Assert.fail("Cannot obtain a callback from the queue");
201         }
202     }
203 
204     class BluetoothHidDeviceCallbackTestHelper extends IBluetoothHidDeviceCallback.Stub {
onAppStatusChanged(BluetoothDevice device, boolean registered)205         public void onAppStatusChanged(BluetoothDevice device, boolean registered) {
206             try {
207                 if (registered) {
208                     mCallbackQueue.put(CALLBACK_APP_REGISTERED);
209                 } else {
210                     mCallbackQueue.put(CALLBACK_APP_UNREGISTERED);
211                 }
212             } catch (InterruptedException e) {
213                 Assert.fail("Cannot add Intent to the queue");
214             }
215         }
216 
onConnectionStateChanged(BluetoothDevice device, int state)217         public void onConnectionStateChanged(BluetoothDevice device, int state) {
218 
219         }
220 
onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize)221         public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
222             try {
223                 mCallbackQueue.put(CALLBACK_ON_GET_REPORT);
224             } catch (InterruptedException e) {
225                 Assert.fail("Cannot add Intent to the queue");
226             }
227         }
228 
onSetReport(BluetoothDevice device, byte type, byte id, byte[] data)229         public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
230             try {
231                 mCallbackQueue.put(CALLBACK_ON_SET_REPORT);
232             } catch (InterruptedException e) {
233                 Assert.fail("Cannot add Intent to the queue");
234             }
235         }
236 
onSetProtocol(BluetoothDevice device, byte protocol)237         public void onSetProtocol(BluetoothDevice device, byte protocol) {
238             try {
239                 mCallbackQueue.put(CALLBACK_ON_SET_PROTOCOL);
240             } catch (InterruptedException e) {
241                 Assert.fail("Cannot add Intent to the queue");
242             }
243         }
244 
onInterruptData(BluetoothDevice device, byte reportId, byte[] data)245         public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
246             try {
247                 mCallbackQueue.put(CALLBACK_ON_INTR_DATA);
248             } catch (InterruptedException e) {
249                 Assert.fail("Cannot add Intent to the queue");
250             }
251         }
252 
onVirtualCableUnplug(BluetoothDevice device)253         public void onVirtualCableUnplug(BluetoothDevice device) {
254             try {
255                 mCallbackQueue.put(CALLBACK_ON_VIRTUAL_UNPLUG);
256             } catch (InterruptedException e) {
257                 Assert.fail("Cannot add Intent to the queue");
258             }
259         }
260     }
261 
262     /**
263      * Test getting HidDeviceService: getHidDeviceService().
264      */
265     @Test
testGetHidDeviceService()266     public void testGetHidDeviceService() {
267         Assert.assertEquals(mHidDeviceService, HidDeviceService.getHidDeviceService());
268     }
269 
270     /**
271      * Test the logic in registerApp and unregisterApp. Should get a callback
272      * onApplicationStateChangedFromNative.
273      */
274     @Test
testRegistration()275     public void testRegistration() throws Exception {
276         doReturn(true).when(mHidDeviceNativeInterface)
277                 .registerApp(anyString(), anyString(), anyString(), anyByte(), any(byte[].class),
278                         isNull(), isNull());
279 
280         verify(mHidDeviceNativeInterface, never()).registerApp(anyString(), anyString(),
281                 anyString(), anyByte(), any(byte[].class), isNull(), isNull());
282 
283         // Register app
284         BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper();
285         Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper));
286 
287         verify(mHidDeviceNativeInterface).registerApp(anyString(), anyString(), anyString(),
288                 anyByte(), any(byte[].class), isNull(), isNull());
289 
290         // App registered
291         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true);
292         verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
293 
294         // Unregister app
295         doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
296         Assert.assertEquals(true, mHidDeviceService.unregisterApp());
297 
298         verify(mHidDeviceNativeInterface).unregisterApp();
299 
300         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, false);
301         verifyCallback(TIMEOUT_MS, CALLBACK_APP_UNREGISTERED, mCallbackQueue);
302 
303     }
304 
305     /**
306      * Test the logic in sendReport(). This should fail when the app is not registered.
307      */
308     @Test
testSendReport()309     public void testSendReport() throws Exception {
310         doReturn(true).when(mHidDeviceNativeInterface).sendReport(anyInt(), any(byte[].class));
311         // sendReport() should fail without app registered
312         Assert.assertEquals(false,
313                 mHidDeviceService.sendReport(mTestDevice, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT));
314 
315         // Register app
316         doReturn(true).when(mHidDeviceNativeInterface)
317                 .registerApp(anyString(), anyString(), anyString(), anyByte(), any(byte[].class),
318                         isNull(), isNull());
319         BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper();
320         Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper));
321 
322         // App registered
323         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true);
324 
325         // Wait for the app registration callback to complete and verify it
326         verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
327 
328         // sendReport() should work when app is registered
329         Assert.assertEquals(true,
330                 mHidDeviceService.sendReport(mTestDevice, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT));
331 
332         verify(mHidDeviceNativeInterface).sendReport(eq((int) SAMPLE_REPORT_ID),
333                 eq(SAMPLE_HID_REPORT));
334 
335         // Unregister app
336         doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
337         Assert.assertEquals(true, mHidDeviceService.unregisterApp());
338     }
339 
340     /**
341      * Test the logic in replyReport(). This should fail when the app is not registered.
342      */
343     @Test
testReplyReport()344     public void testReplyReport() throws Exception {
345         doReturn(true).when(mHidDeviceNativeInterface)
346                 .replyReport(anyByte(), anyByte(), any(byte[].class));
347         // replyReport() should fail without app registered
348         Assert.assertEquals(false,
349                 mHidDeviceService.replyReport(mTestDevice, SAMPLE_REPORT_TYPE, SAMPLE_REPORT_ID,
350                         SAMPLE_HID_REPORT));
351 
352         // Register app
353         doReturn(true).when(mHidDeviceNativeInterface)
354                 .registerApp(anyString(), anyString(), anyString(), anyByte(), any(byte[].class),
355                         isNull(), isNull());
356         BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper();
357         Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper));
358 
359         // App registered
360         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true);
361 
362         // Wait for the app registration callback to complete and verify it
363         verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
364 
365         // replyReport() should work when app is registered
366         Assert.assertEquals(true,
367                 mHidDeviceService.replyReport(mTestDevice, SAMPLE_REPORT_TYPE, SAMPLE_REPORT_ID,
368                         SAMPLE_HID_REPORT));
369 
370         verify(mHidDeviceNativeInterface).replyReport(eq(SAMPLE_REPORT_TYPE), eq(SAMPLE_REPORT_ID),
371                 eq(SAMPLE_HID_REPORT));
372 
373         // Unregister app
374         doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
375         Assert.assertEquals(true, mHidDeviceService.unregisterApp());
376     }
377 
378     /**
379      * Test the logic in reportError(). This should fail when the app is not registered.
380      */
381     @Test
testReportError()382     public void testReportError() throws Exception {
383         doReturn(true).when(mHidDeviceNativeInterface).reportError(anyByte());
384         // reportError() should fail without app registered
385         Assert.assertEquals(false, mHidDeviceService.reportError(mTestDevice, SAMPLE_REPORT_ERROR));
386 
387         // Register app
388         doReturn(true).when(mHidDeviceNativeInterface)
389                 .registerApp(anyString(), anyString(), anyString(), anyByte(), any(byte[].class),
390                         isNull(), isNull());
391         BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper();
392         Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper));
393 
394         // App registered
395         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true);
396 
397         // Wait for the app registration callback to complete and verify it
398         verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
399 
400         // reportError() should work when app is registered
401         Assert.assertEquals(true, mHidDeviceService.reportError(mTestDevice, SAMPLE_REPORT_ERROR));
402 
403         verify(mHidDeviceNativeInterface).reportError(eq(SAMPLE_REPORT_ERROR));
404 
405         // Unregister app
406         doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
407         Assert.assertEquals(true, mHidDeviceService.unregisterApp());
408     }
409 
410     /**
411      * Test that an outgoing connection/disconnection succeeds
412      */
413     @Test
testOutgoingConnectDisconnectSuccess()414     public void testOutgoingConnectDisconnectSuccess() {
415         doReturn(true).when(mHidDeviceNativeInterface).connect(any(BluetoothDevice.class));
416         doReturn(true).when(mHidDeviceNativeInterface).disconnect();
417 
418         // Register app
419         doReturn(true).when(mHidDeviceNativeInterface)
420                 .registerApp(anyString(), anyString(), anyString(), anyByte(), any(byte[].class),
421                         isNull(), isNull());
422         mHidDeviceService.registerApp(mSettings, null, null, null);
423 
424         // App registered
425         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true);
426 
427         // Send a connect request
428         Assert.assertTrue("Connect failed", mHidDeviceService.connect(mTestDevice));
429 
430         mHidDeviceService.onConnectStateChangedFromNative(mTestDevice,
431                 HidDeviceService.HAL_CONN_STATE_CONNECTING);
432         // Verify the connection state broadcast
433         verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_CONNECTING,
434                 BluetoothProfile.STATE_DISCONNECTED);
435         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
436                 mHidDeviceService.getConnectionState(mTestDevice));
437 
438         mHidDeviceService.onConnectStateChangedFromNative(mTestDevice,
439                 HidDeviceService.HAL_CONN_STATE_CONNECTED);
440         // Verify the connection state broadcast
441         verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_CONNECTED,
442                 BluetoothProfile.STATE_CONNECTING);
443         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
444                 mHidDeviceService.getConnectionState(mTestDevice));
445 
446         // Verify the list of connected devices
447         Assert.assertTrue(mHidDeviceService.getDevicesMatchingConnectionStates(
448                 new int[]{BluetoothProfile.STATE_CONNECTED}).contains(mTestDevice));
449 
450         // Send a disconnect request
451         Assert.assertTrue("Disconnect failed", mHidDeviceService.disconnect(mTestDevice));
452 
453         mHidDeviceService.onConnectStateChangedFromNative(mTestDevice,
454                 HidDeviceService.HAL_CONN_STATE_DISCONNECTING);
455         // Verify the connection state broadcast
456         verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_DISCONNECTING,
457                 BluetoothProfile.STATE_CONNECTED);
458         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING,
459                 mHidDeviceService.getConnectionState(mTestDevice));
460 
461         mHidDeviceService.onConnectStateChangedFromNative(mTestDevice,
462                 HidDeviceService.HAL_CONN_STATE_DISCONNECTED);
463         // Verify the connection state broadcast
464         verifyConnectionStateIntent(TIMEOUT_MS, mTestDevice, BluetoothProfile.STATE_DISCONNECTED,
465                 BluetoothProfile.STATE_DISCONNECTING);
466         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
467                 mHidDeviceService.getConnectionState(mTestDevice));
468 
469         // Verify the list of connected devices
470         Assert.assertFalse(mHidDeviceService.getDevicesMatchingConnectionStates(
471                 new int[]{BluetoothProfile.STATE_CONNECTED}).contains(mTestDevice));
472 
473         // Unregister app
474         doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
475         Assert.assertEquals(true, mHidDeviceService.unregisterApp());
476     }
477 
478     /**
479      * Test the logic in callback functions from native stack: onGetReport, onSetReport,
480      * onSetProtocol, onInterruptData, onVirtualCableUnplug. The HID Device server should send the
481      * callback to the user app.
482      */
483     @Test
testCallbacks()484     public void testCallbacks() {
485         doReturn(true).when(mHidDeviceNativeInterface)
486                 .registerApp(anyString(), anyString(), anyString(), anyByte(), any(byte[].class),
487                         isNull(), isNull());
488 
489         verify(mHidDeviceNativeInterface, never()).registerApp(anyString(), anyString(),
490                 anyString(), anyByte(), any(byte[].class), isNull(), isNull());
491 
492         // Register app
493         BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper();
494         Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper));
495 
496         verify(mHidDeviceNativeInterface).registerApp(anyString(), anyString(), anyString(),
497                 anyByte(), any(byte[].class), isNull(), isNull());
498 
499         // App registered
500         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true);
501         verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
502 
503         // Received callback: onGetReport
504         mHidDeviceService.onGetReportFromNative(SAMPLE_REPORT_TYPE, SAMPLE_REPORT_ID,
505                 SAMPLE_BUFFER_SIZE);
506         verifyCallback(TIMEOUT_MS, CALLBACK_ON_GET_REPORT, mCallbackQueue);
507 
508         // Received callback: onSetReport
509         mHidDeviceService.onSetReportFromNative(SAMPLE_REPORT_TYPE, SAMPLE_REPORT_ID,
510                 SAMPLE_HID_REPORT);
511         verifyCallback(TIMEOUT_MS, CALLBACK_ON_SET_REPORT, mCallbackQueue);
512 
513         // Received callback: onSetProtocol
514         mHidDeviceService.onSetProtocolFromNative(BluetoothHidDevice.PROTOCOL_BOOT_MODE);
515         verifyCallback(TIMEOUT_MS, CALLBACK_ON_SET_PROTOCOL, mCallbackQueue);
516 
517         // Received callback: onInterruptData
518         mHidDeviceService.onInterruptDataFromNative(SAMPLE_REPORT_ID, SAMPLE_HID_REPORT);
519         verifyCallback(TIMEOUT_MS, CALLBACK_ON_INTR_DATA, mCallbackQueue);
520 
521         // Received callback: onVirtualCableUnplug
522         mHidDeviceService.onVirtualCableUnplugFromNative();
523         verifyCallback(TIMEOUT_MS, CALLBACK_ON_VIRTUAL_UNPLUG, mCallbackQueue);
524 
525         // Unregister app
526         doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
527         Assert.assertEquals(true, mHidDeviceService.unregisterApp());
528 
529         verify(mHidDeviceNativeInterface).unregisterApp();
530 
531         mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, false);
532         verifyCallback(TIMEOUT_MS, CALLBACK_APP_UNREGISTERED, mCallbackQueue);
533     }
534 }
535