Home | History | Annotate | Download | only in hid
      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 
     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
     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
    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
    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 
    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 
    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 
    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 {
    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 
    216         public void onConnectionStateChanged(BluetoothDevice device, int state) {
    217 
    218         }
    219 
    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 
    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 
    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 
    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 
    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
    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
    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
    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
    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
    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
    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
    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