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