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