Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2018 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.cts.verifier.bluetooth;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.bluetooth.BluetoothHidDevice;
     22 import android.bluetooth.BluetoothHidDeviceAppQosSettings;
     23 import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
     24 import android.bluetooth.BluetoothProfile;
     25 import android.content.Intent;
     26 import android.os.Bundle;
     27 
     28 import android.util.Log;
     29 import android.view.View;
     30 import android.view.View.OnClickListener;
     31 import android.widget.Button;
     32 import com.android.cts.verifier.PassFailButtons;
     33 import com.android.cts.verifier.R;
     34 
     35 import java.util.List;
     36 import java.util.concurrent.ExecutorService;
     37 import java.util.concurrent.Executors;
     38 
     39 public class HidDeviceActivity extends PassFailButtons.Activity {
     40     private static final String TAG = HidDeviceActivity.class.getSimpleName();
     41     private static final int MSG_APP_STATUS_CHANGED = 0;
     42     private static final String SDP_NAME = "CtsVerifier";
     43     private static final String SDP_DESCRIPTION = "CtsVerifier HID Device test";
     44     private static final String SDP_PROVIDER = "Android";
     45     private static final int QOS_TOKEN_RATE = 800; // 9 bytes * 1000000 us / 11250 us
     46     private static final int QOS_TOKEN_BUCKET_SIZE = 9;
     47     private static final int QOS_PEAK_BANDWIDTH = 0;
     48     private static final int QOS_LATENCY = 11250;
     49     static final String SAMPLE_INPUT = "bluetooth";
     50 
     51     private BluetoothAdapter mBluetoothAdapter;
     52     private BluetoothHidDevice mBluetoothHidDevice;
     53     private BluetoothDevice mHidHost;
     54     private ExecutorService mExecutor;
     55 
     56     private Button mRegisterAppButton;
     57     private Button mMakeDiscoverableButton;
     58     private Button mUnregisterAppButton;
     59     private Button mSendReportButton;
     60     private Button mReplyReportButton;
     61     private Button mReportErrorButton;
     62 
     63     private BluetoothProfile.ServiceListener mProfileListener =
     64             new BluetoothProfile.ServiceListener() {
     65         public void onServiceConnected(int profile, BluetoothProfile proxy) {
     66             if (profile == BluetoothProfile.HID_DEVICE) {
     67                 mBluetoothHidDevice = (BluetoothHidDevice) proxy;
     68             }
     69         }
     70 
     71         public void onServiceDisconnected(int profile) {
     72             if (profile == BluetoothProfile.HID_DEVICE) {
     73                 mBluetoothHidDevice = null;
     74             }
     75         }
     76     };
     77 
     78     private final BluetoothHidDeviceAppSdpSettings mSdpSettings =
     79             new BluetoothHidDeviceAppSdpSettings(
     80                     SDP_NAME,
     81                     SDP_DESCRIPTION,
     82                     SDP_PROVIDER,
     83                     BluetoothHidDevice.SUBCLASS1_COMBO,
     84                     HidConstants.HIDD_REPORT_DESC);
     85 
     86     private final BluetoothHidDeviceAppQosSettings mOutQos =
     87             new BluetoothHidDeviceAppQosSettings(
     88                     BluetoothHidDeviceAppQosSettings.SERVICE_BEST_EFFORT,
     89                     QOS_TOKEN_RATE,
     90                     QOS_TOKEN_BUCKET_SIZE,
     91                     QOS_PEAK_BANDWIDTH,
     92                     QOS_LATENCY,
     93                     BluetoothHidDeviceAppQosSettings.MAX);
     94 
     95     private BluetoothHidDevice.Callback mCallback = new BluetoothHidDevice.Callback() {
     96         @Override
     97         public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
     98             Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
     99                     + registered);
    100         }
    101     };
    102 
    103     @Override
    104     protected void onCreate(Bundle savedInstanceState) {
    105         super.onCreate(savedInstanceState);
    106         setContentView(R.layout.bt_hid_device);
    107         setPassFailButtonClickListeners();
    108         setInfoResources(R.string.bt_hid_device_test_name, R.string.bt_hid_device_test_info, -1);
    109 
    110         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    111         mBluetoothAdapter.getProfileProxy(getApplicationContext(), mProfileListener,
    112                 BluetoothProfile.HID_DEVICE);
    113         mExecutor = Executors.newSingleThreadExecutor();
    114 
    115         mRegisterAppButton = (Button) findViewById(R.id.bt_hid_device_register_button);
    116         mRegisterAppButton.setOnClickListener(new OnClickListener() {
    117             @Override
    118             public void onClick(View v) {
    119                 register();
    120             }
    121         });
    122 
    123         mUnregisterAppButton = (Button) findViewById(R.id.bt_hid_device_unregister_button);
    124         mUnregisterAppButton.setOnClickListener(new OnClickListener() {
    125             @Override
    126             public void onClick(View v) {
    127                 unregister();
    128             }
    129         });
    130 
    131         mMakeDiscoverableButton = (Button) findViewById(R.id.bt_hid_device_discoverable_button);
    132         mMakeDiscoverableButton.setOnClickListener(new OnClickListener() {
    133             @Override
    134             public void onClick(View v) {
    135                 makeDiscoverable();
    136             }
    137         });
    138 
    139         mSendReportButton = (Button) findViewById(R.id.bt_hid_device_send_report_button);
    140         mSendReportButton.setOnClickListener(new OnClickListener() {
    141             @Override
    142             public void onClick(View v) {
    143                 testSendReport();
    144             }
    145         });
    146 
    147         mReplyReportButton = (Button) findViewById(R.id.bt_hid_device_reply_report_button);
    148         mReplyReportButton.setEnabled(false);
    149         mReplyReportButton.setOnClickListener(new OnClickListener() {
    150             @Override
    151             public void onClick(View v) {
    152                 testReplyReport();
    153             }
    154         });
    155 
    156         mReportErrorButton = (Button) findViewById(R.id.bt_hid_device_report_error_button);
    157         mReportErrorButton.setEnabled(false);
    158         mReportErrorButton.setOnClickListener(new OnClickListener() {
    159             @Override
    160             public void onClick(View v) {
    161                 testReportError();
    162             }
    163         });
    164     }
    165 
    166     @Override
    167     protected void onDestroy() {
    168         super.onDestroy();
    169         unregister();
    170     }
    171 
    172     private boolean register() {
    173         return mBluetoothHidDevice != null
    174                 && mBluetoothHidDevice.registerApp(mSdpSettings, null, mOutQos, mExecutor,
    175                 mCallback);
    176     }
    177 
    178     private void makeDiscoverable() {
    179         if (mBluetoothAdapter.getScanMode() !=
    180                 BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
    181             Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    182             intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 30);
    183             startActivity(intent);
    184         }
    185     }
    186 
    187     private boolean unregister() {
    188         return mBluetoothHidDevice != null && mBluetoothHidDevice.unregisterApp();
    189     }
    190 
    191 
    192     private boolean getConnectedDevice() {
    193         if (mBluetoothHidDevice == null) {
    194             Log.w(TAG, "mBluetoothHidDevice is null");
    195             return false;
    196         }
    197 
    198         List<BluetoothDevice> connectedDevices = mBluetoothHidDevice.getConnectedDevices();
    199         if (connectedDevices.size() == 0) {
    200             return false;
    201         } else {
    202             return false;
    203         }
    204     }
    205 
    206     private void testSendReport() {
    207         if (mBluetoothHidDevice == null) {
    208             Log.w(TAG, "mBluetoothHidDevice is null");
    209             return;
    210         }
    211 
    212         if (mHidHost == null) {
    213             if (mBluetoothHidDevice.getConnectedDevices().size() == 0) {
    214                 Log.w(TAG, "HID host not connected");
    215                 return;
    216             } else {
    217                 mHidHost = mBluetoothHidDevice.getConnectedDevices().get(0);
    218                 Log.d(TAG, "connected to: " + mHidHost);
    219             }
    220         }
    221         for (char c : SAMPLE_INPUT.toCharArray()) {
    222             mBluetoothHidDevice.sendReport(mHidHost, BluetoothHidDevice.REPORT_TYPE_INPUT,
    223                     singleKeyHit(charToKeyCode(c)));
    224             mBluetoothHidDevice.sendReport(mHidHost, BluetoothHidDevice.REPORT_TYPE_INPUT,
    225                     singleKeyHit((byte) 0));
    226         }
    227         mReplyReportButton.setEnabled(true);
    228 
    229     }
    230 
    231     private void testReplyReport() {
    232         if (mBluetoothHidDevice == null) {
    233             Log.w(TAG, "mBluetoothHidDevice is null");
    234             return;
    235         }
    236 
    237         if (mHidHost == null) {
    238             if (mBluetoothHidDevice.getConnectedDevices().size() == 0) {
    239                 Log.w(TAG, "HID host not connected");
    240                 return;
    241             } else {
    242                 mHidHost = mBluetoothHidDevice.getConnectedDevices().get(0);
    243                 Log.d(TAG, "connected to: " + mHidHost);
    244             }
    245         }
    246         if (mBluetoothHidDevice.replyReport(mHidHost, (byte) 0, (byte) 0,
    247                 singleKeyHit((byte) 0))) {
    248             mReportErrorButton.setEnabled(true);
    249         }
    250     }
    251 
    252     private void testReportError() {
    253         if (mBluetoothHidDevice == null) {
    254             Log.w(TAG, "mBluetoothHidDevice is null");
    255             return;
    256         }
    257 
    258         if (mHidHost == null) {
    259             if (mBluetoothHidDevice.getConnectedDevices().size() == 0) {
    260                 Log.w(TAG, "HID host not connected");
    261                 return;
    262             } else {
    263                 mHidHost = mBluetoothHidDevice.getConnectedDevices().get(0);
    264                 Log.d(TAG, "connected to: " + mHidHost);
    265             }
    266         }
    267         if (mBluetoothHidDevice.reportError(mHidHost, (byte) 0)) {
    268             getPassButton().setEnabled(true);
    269         }
    270     }
    271 
    272 
    273     private byte[] singleKeyHit(byte code) {
    274         byte[] keyboardData = new byte[8];
    275         keyboardData[0] = 0;
    276         keyboardData[1] = 0;
    277         keyboardData[2] = code;
    278         keyboardData[3] = 0;
    279         keyboardData[4] = 0;
    280         keyboardData[5] = 0;
    281         keyboardData[6] = 0;
    282         keyboardData[7] = 0;
    283         return keyboardData;
    284     }
    285 
    286     private byte charToKeyCode(char c) {
    287         if (c < 'a' || c > 'z') {
    288             return 0;
    289         }
    290         return (byte) (c - 'a' + 0x04);
    291     }
    292 }
    293