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