Home | History | Annotate | Download | only in hid
      1 /*
      2  * Copyright (C) 2012 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 android.bluetooth.BluetoothDevice;
     20 import android.bluetooth.BluetoothInputDevice;
     21 import android.bluetooth.BluetoothProfile;
     22 import android.bluetooth.IBluetoothInputDevice;
     23 import android.content.Intent;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.os.Message;
     27 import android.provider.Settings;
     28 import android.util.Log;
     29 
     30 import com.android.bluetooth.btservice.AdapterService;
     31 import com.android.bluetooth.btservice.ProfileService;
     32 import com.android.bluetooth.Utils;
     33 
     34 import java.util.ArrayList;
     35 import java.util.Collections;
     36 import java.util.HashMap;
     37 import java.util.List;
     38 import java.util.Map;
     39 
     40 /**
     41  * Provides Bluetooth Hid Host profile, as a service in
     42  * the Bluetooth application.
     43  * @hide
     44  */
     45 public class HidService extends ProfileService {
     46     private static final boolean DBG = true;
     47     private static final String TAG = "HidService";
     48 
     49     private Map<BluetoothDevice, Integer> mInputDevices;
     50     private boolean mNativeAvailable;
     51     private static HidService sHidService;
     52     private BluetoothDevice mTargetDevice = null;
     53 
     54     private static final int MESSAGE_CONNECT = 1;
     55     private static final int MESSAGE_DISCONNECT = 2;
     56     private static final int MESSAGE_CONNECT_STATE_CHANGED = 3;
     57     private static final int MESSAGE_GET_PROTOCOL_MODE = 4;
     58     private static final int MESSAGE_VIRTUAL_UNPLUG = 5;
     59     private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6;
     60     private static final int MESSAGE_SET_PROTOCOL_MODE = 7;
     61     private static final int MESSAGE_GET_REPORT = 8;
     62     private static final int MESSAGE_ON_GET_REPORT = 9;
     63     private static final int MESSAGE_SET_REPORT = 10;
     64     private static final int MESSAGE_SEND_DATA = 11;
     65     private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12;
     66     private static final int MESSAGE_ON_HANDSHAKE = 13;
     67     private static final int MESSAGE_GET_IDLE_TIME = 14;
     68     private static final int MESSAGE_ON_GET_IDLE_TIME = 15;
     69     private static final int MESSAGE_SET_IDLE_TIME = 16;
     70 
     71     static {
     72         classInitNative();
     73     }
     74 
     75     public String getName() {
     76         return TAG;
     77     }
     78 
     79     public IProfileServiceBinder initBinder() {
     80         return new BluetoothInputDeviceBinder(this);
     81     }
     82 
     83     protected boolean start() {
     84         mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
     85         initializeNative();
     86         mNativeAvailable=true;
     87         setHidService(this);
     88         return true;
     89     }
     90 
     91     protected boolean stop() {
     92         if (DBG) log("Stopping Bluetooth HidService");
     93         return true;
     94     }
     95 
     96     protected boolean cleanup() {
     97         if (mNativeAvailable) {
     98             cleanupNative();
     99             mNativeAvailable=false;
    100         }
    101 
    102         if(mInputDevices != null) {
    103             for (BluetoothDevice device : mInputDevices.keySet()) {
    104                 int inputDeviceState = getConnectionState(device);
    105                 if (inputDeviceState != BluetoothProfile.STATE_DISCONNECTED) {
    106                     broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
    107                 }
    108             }
    109             mInputDevices.clear();
    110         }
    111         clearHidService();
    112         return true;
    113     }
    114 
    115     public static synchronized HidService getHidService(){
    116         if (sHidService != null && sHidService.isAvailable()) {
    117             if (DBG) Log.d(TAG, "getHidService(): returning " + sHidService);
    118             return sHidService;
    119         }
    120         if (DBG)  {
    121             if (sHidService == null) {
    122                 Log.d(TAG, "getHidService(): service is NULL");
    123             } else if (!(sHidService.isAvailable())) {
    124                 Log.d(TAG,"getHidService(): service is not available");
    125             }
    126         }
    127         return null;
    128     }
    129 
    130     private static synchronized void setHidService(HidService instance) {
    131         if (instance != null && instance.isAvailable()) {
    132             if (DBG) Log.d(TAG, "setHidService(): set to: " + sHidService);
    133             sHidService = instance;
    134         } else {
    135             if (DBG)  {
    136                 if (sHidService == null) {
    137                     Log.d(TAG, "setHidService(): service not available");
    138                 } else if (!sHidService.isAvailable()) {
    139                     Log.d(TAG,"setHidService(): service is cleaning up");
    140                 }
    141             }
    142         }
    143     }
    144 
    145     private static synchronized void clearHidService() {
    146         sHidService = null;
    147     }
    148 
    149 
    150     private final Handler mHandler = new Handler() {
    151 
    152         @Override
    153         public void handleMessage(Message msg) {
    154             switch (msg.what) {
    155                 case MESSAGE_CONNECT:
    156                 {
    157                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    158                     if (!connectHidNative(Utils.getByteAddress(device)) ) {
    159                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
    160                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
    161                         break;
    162                     }
    163                     mTargetDevice = device;
    164                 }
    165                     break;
    166                 case MESSAGE_DISCONNECT:
    167                 {
    168                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    169                     if (!disconnectHidNative(Utils.getByteAddress(device)) ) {
    170                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
    171                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
    172                         break;
    173                     }
    174                 }
    175                     break;
    176                 case MESSAGE_CONNECT_STATE_CHANGED:
    177                 {
    178                     BluetoothDevice device = getDevice((byte[]) msg.obj);
    179                     int halState = msg.arg1;
    180                     Integer prevStateInteger = mInputDevices.get(device);
    181                     int prevState = (prevStateInteger == null) ?
    182                         BluetoothInputDevice.STATE_DISCONNECTED :prevStateInteger;
    183                     if(DBG) Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:"+
    184                         convertHalState(halState)+", prevState:"+prevState);
    185                     if(halState == CONN_STATE_CONNECTED &&
    186                        prevState == BluetoothInputDevice.STATE_DISCONNECTED &&
    187                        (!okToConnect(device))) {
    188                         if (DBG) Log.d(TAG,"Incoming HID connection rejected");
    189                         disconnectHidNative(Utils.getByteAddress(device));
    190                     } else {
    191                         broadcastConnectionState(device, convertHalState(halState));
    192                     }
    193                     if (halState == CONN_STATE_CONNECTED &&
    194                         (mTargetDevice != null && mTargetDevice.equals(device))) {
    195                         mTargetDevice = null;
    196                         // local device originated connection to hid device, move out
    197                         // of quiet mode
    198                         AdapterService adapterService = AdapterService.getAdapterService();
    199                         adapterService.enable(false);
    200                     }
    201                 }
    202                     break;
    203                 case MESSAGE_GET_PROTOCOL_MODE:
    204                 {
    205                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    206                     if(!getProtocolModeNative(Utils.getByteAddress(device)) ) {
    207                         Log.e(TAG, "Error: get protocol mode native returns false");
    208                     }
    209                 }
    210                 break;
    211 
    212                 case MESSAGE_ON_GET_PROTOCOL_MODE:
    213                 {
    214                     BluetoothDevice device = getDevice((byte[]) msg.obj);
    215                     int protocolMode = msg.arg1;
    216                     broadcastProtocolMode(device, protocolMode);
    217                 }
    218                 break;
    219                 case MESSAGE_VIRTUAL_UNPLUG:
    220                 {
    221                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    222                     if(!virtualUnPlugNative(Utils.getByteAddress(device))) {
    223                         Log.e(TAG, "Error: virtual unplug native returns false");
    224                     }
    225                 }
    226                 break;
    227                 case MESSAGE_SET_PROTOCOL_MODE:
    228                 {
    229                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    230                     byte protocolMode = (byte) msg.arg1;
    231                     log("sending set protocol mode(" + protocolMode + ")");
    232                     if(!setProtocolModeNative(Utils.getByteAddress(device), protocolMode)) {
    233                         Log.e(TAG, "Error: set protocol mode native returns false");
    234                     }
    235                 }
    236                 break;
    237                 case MESSAGE_GET_REPORT:
    238                 {
    239                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    240                     Bundle data = msg.getData();
    241                     byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
    242                     byte reportId = data.getByte(BluetoothInputDevice.EXTRA_REPORT_ID);
    243                     int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
    244                     if(!getReportNative(Utils.getByteAddress(device), reportType, reportId, bufferSize)) {
    245                         Log.e(TAG, "Error: get report native returns false");
    246                     }
    247                 }
    248                 break;
    249                 case MESSAGE_ON_GET_REPORT:
    250                 {
    251                     BluetoothDevice device = getDevice((byte[])msg.obj);
    252                     Bundle data = msg.getData();
    253                     byte[] report = data.getByteArray(BluetoothInputDevice.EXTRA_REPORT);
    254                     int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
    255                     broadcastReport(device, report, bufferSize);
    256                 }
    257                 break;
    258                 case MESSAGE_ON_HANDSHAKE:
    259                 {
    260                     BluetoothDevice device = getDevice((byte[])msg.obj);
    261                     int status = msg.arg1;
    262                     broadcastHandshake(device, status);
    263                 }
    264                 break;
    265                 case MESSAGE_SET_REPORT:
    266                 {
    267                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    268                     Bundle data = msg.getData();
    269                     byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
    270                     String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
    271                     if(!setReportNative(Utils.getByteAddress(device), reportType, report)) {
    272                         Log.e(TAG, "Error: set report native returns false");
    273                     }
    274                 }
    275                 break;
    276                 case MESSAGE_SEND_DATA:
    277                 {
    278                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    279                     Bundle data = msg.getData();
    280                     String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
    281                     if(!sendDataNative(Utils.getByteAddress(device), report)) {
    282                         Log.e(TAG, "Error: send data native returns false");
    283                     }
    284                 }
    285                 break;
    286                 case MESSAGE_ON_VIRTUAL_UNPLUG:
    287                 {
    288                     BluetoothDevice device = getDevice((byte[]) msg.obj);
    289                     int status = msg.arg1;
    290                     broadcastVirtualUnplugStatus(device, status);
    291                 }
    292                 break;
    293                 case MESSAGE_GET_IDLE_TIME: {
    294                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    295                     if (!getIdleTimeNative(Utils.getByteAddress(device))) {
    296                         Log.e(TAG, "Error: get idle time native returns false");
    297                     }
    298                 } break;
    299                 case MESSAGE_ON_GET_IDLE_TIME: {
    300                     BluetoothDevice device = getDevice((byte[]) msg.obj);
    301                     int idleTime = msg.arg1;
    302                     broadcastIdleTime(device, idleTime);
    303                 } break;
    304                 case MESSAGE_SET_IDLE_TIME: {
    305                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    306                     Bundle data = msg.getData();
    307                     byte idleTime = data.getByte(BluetoothInputDevice.EXTRA_IDLE_TIME);
    308                     if (!setIdleTimeNative(Utils.getByteAddress(device), idleTime)) {
    309                         Log.e(TAG, "Error: get idle time native returns false");
    310                     }
    311                 } break;
    312             }
    313         }
    314     };
    315 
    316     /**
    317      * Handlers for incoming service calls
    318      */
    319     private static class BluetoothInputDeviceBinder extends IBluetoothInputDevice.Stub implements IProfileServiceBinder{
    320         private HidService mService;
    321         public BluetoothInputDeviceBinder(HidService svc) {
    322             mService = svc;
    323         }
    324 
    325         public boolean cleanup() {
    326             mService = null;
    327             return true;
    328         }
    329 
    330         private HidService getService() {
    331             if (!Utils.checkCaller()) {
    332                 Log.w(TAG,"InputDevice call not allowed for non-active user");
    333                 return null;
    334             }
    335 
    336             if (mService  != null && mService.isAvailable()) {
    337                 return mService;
    338             }
    339             return null;
    340         }
    341 
    342         public boolean connect(BluetoothDevice device) {
    343             HidService service = getService();
    344             if (service == null) return false;
    345             return service.connect(device);
    346         }
    347 
    348         public boolean disconnect(BluetoothDevice device) {
    349             HidService service = getService();
    350             if (service == null) return false;
    351             return service.disconnect(device);
    352         }
    353 
    354         public int getConnectionState(BluetoothDevice device) {
    355             HidService service = getService();
    356             if (service == null) return BluetoothInputDevice.STATE_DISCONNECTED;
    357             return service.getConnectionState(device);
    358         }
    359 
    360         public List<BluetoothDevice> getConnectedDevices() {
    361             return getDevicesMatchingConnectionStates(
    362                     new int[] {BluetoothProfile.STATE_CONNECTED});
    363         }
    364 
    365         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    366             HidService service = getService();
    367             if (service == null) return new ArrayList<BluetoothDevice>(0);
    368             return service.getDevicesMatchingConnectionStates(states);
    369         }
    370 
    371         public boolean setPriority(BluetoothDevice device, int priority) {
    372             HidService service = getService();
    373             if (service == null) return false;
    374             return service.setPriority(device, priority);
    375         }
    376 
    377         public int getPriority(BluetoothDevice device) {
    378             HidService service = getService();
    379             if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
    380             return service.getPriority(device);
    381         }
    382 
    383         /* The following APIs regarding test app for compliance */
    384         public boolean getProtocolMode(BluetoothDevice device) {
    385             HidService service = getService();
    386             if (service == null) return false;
    387             return service.getProtocolMode(device);
    388         }
    389 
    390         public boolean virtualUnplug(BluetoothDevice device) {
    391             HidService service = getService();
    392             if (service == null) return false;
    393             return service.virtualUnplug(device);
    394         }
    395 
    396         public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
    397             HidService service = getService();
    398             if (service == null) return false;
    399             return service.setProtocolMode(device, protocolMode);
    400         }
    401 
    402         public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
    403             HidService service = getService();
    404             if (service == null) return false;
    405             return service.getReport(device, reportType, reportId, bufferSize) ;
    406         }
    407 
    408         public boolean setReport(BluetoothDevice device, byte reportType, String report) {
    409             HidService service = getService();
    410             if (service == null) return false;
    411             return service.setReport(device, reportType, report);
    412         }
    413 
    414         public boolean sendData(BluetoothDevice device, String report) {
    415             HidService service = getService();
    416             if (service == null) return false;
    417             return service.sendData(device, report);
    418         }
    419 
    420         public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
    421             HidService service = getService();
    422             if (service == null) return false;
    423             return service.setIdleTime(device, idleTime);
    424         }
    425 
    426         public boolean getIdleTime(BluetoothDevice device) {
    427             HidService service = getService();
    428             if (service == null) return false;
    429             return service.getIdleTime(device);
    430         }
    431     };
    432 
    433     //APIs
    434     boolean connect(BluetoothDevice device) {
    435         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    436         if (getConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED) {
    437             Log.e(TAG, "Hid Device not disconnected: " + device);
    438             return false;
    439         }
    440         if (getPriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
    441             Log.e(TAG, "Hid Device PRIORITY_OFF: " + device);
    442             return false;
    443         }
    444 
    445         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device);
    446         mHandler.sendMessage(msg);
    447         return true;
    448     }
    449 
    450     boolean disconnect(BluetoothDevice device) {
    451         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    452         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device);
    453         mHandler.sendMessage(msg);
    454         return true;
    455     }
    456 
    457     int getConnectionState(BluetoothDevice device) {
    458         if (mInputDevices.get(device) == null) {
    459             return BluetoothInputDevice.STATE_DISCONNECTED;
    460         }
    461         return mInputDevices.get(device);
    462     }
    463 
    464     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    465         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    466         List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
    467 
    468         for (BluetoothDevice device: mInputDevices.keySet()) {
    469             int inputDeviceState = getConnectionState(device);
    470             for (int state : states) {
    471                 if (state == inputDeviceState) {
    472                     inputDevices.add(device);
    473                     break;
    474                 }
    475             }
    476         }
    477         return inputDevices;
    478     }
    479 
    480     public boolean setPriority(BluetoothDevice device, int priority) {
    481         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    482                                        "Need BLUETOOTH_ADMIN permission");
    483         Settings.Global.putInt(getContentResolver(),
    484             Settings.Global.getBluetoothInputDevicePriorityKey(device.getAddress()),
    485             priority);
    486         if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority);
    487         return true;
    488     }
    489 
    490     public  int getPriority(BluetoothDevice device) {
    491         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    492                                        "Need BLUETOOTH_ADMIN permission");
    493         int priority = Settings.Global.getInt(getContentResolver(),
    494             Settings.Global.getBluetoothInputDevicePriorityKey(device.getAddress()),
    495             BluetoothProfile.PRIORITY_UNDEFINED);
    496         return priority;
    497     }
    498 
    499     /* The following APIs regarding test app for compliance */
    500     boolean getProtocolMode(BluetoothDevice device) {
    501         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    502                                        "Need BLUETOOTH_ADMIN permission");
    503         int state = this.getConnectionState(device);
    504         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    505             return false;
    506         }
    507         Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE,device);
    508         mHandler.sendMessage(msg);
    509         return true;
    510         /* String objectPath = getObjectPathFromAddress(device.getAddress());
    511             return getProtocolModeInputDeviceNative(objectPath);*/
    512     }
    513 
    514     boolean virtualUnplug(BluetoothDevice device) {
    515         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    516                                        "Need BLUETOOTH_ADMIN permission");
    517         int state = this.getConnectionState(device);
    518         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    519             return false;
    520         }
    521         Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG,device);
    522         mHandler.sendMessage(msg);
    523         return true;
    524     }
    525 
    526     boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
    527         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    528                                        "Need BLUETOOTH_ADMIN permission");
    529         int state = this.getConnectionState(device);
    530         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    531             return false;
    532         }
    533         Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
    534         msg.obj = device;
    535         msg.arg1 = protocolMode;
    536         mHandler.sendMessage(msg);
    537         return true ;
    538     }
    539 
    540     boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
    541         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    542                                        "Need BLUETOOTH_ADMIN permission");
    543         int state = this.getConnectionState(device);
    544         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    545             return false;
    546         }
    547         Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
    548         msg.obj = device;
    549         Bundle data = new Bundle();
    550         data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
    551         data.putByte(BluetoothInputDevice.EXTRA_REPORT_ID, reportId);
    552         data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
    553         msg.setData(data);
    554         mHandler.sendMessage(msg);
    555         return true ;
    556     }
    557 
    558     boolean setReport(BluetoothDevice device, byte reportType, String report) {
    559         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    560                                                    "Need BLUETOOTH_ADMIN permission");
    561         int state = this.getConnectionState(device);
    562         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    563             return false;
    564         }
    565         Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
    566         msg.obj = device;
    567         Bundle data = new Bundle();
    568         data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
    569         data.putString(BluetoothInputDevice.EXTRA_REPORT, report);
    570         msg.setData(data);
    571         mHandler.sendMessage(msg);
    572         return true ;
    573 
    574     }
    575 
    576     boolean sendData(BluetoothDevice device, String report) {
    577         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    578                                                    "Need BLUETOOTH_ADMIN permission");
    579         int state = this.getConnectionState(device);
    580         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    581             return false;
    582         }
    583 
    584         return sendDataNative(Utils.getByteAddress(device), report);
    585     }
    586 
    587     boolean getIdleTime(BluetoothDevice device) {
    588         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    589         int state = this.getConnectionState(device);
    590         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    591             return false;
    592         }
    593         Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device);
    594         mHandler.sendMessage(msg);
    595         return true;
    596     }
    597 
    598     boolean setIdleTime(BluetoothDevice device, byte idleTime) {
    599         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    600         int state = this.getConnectionState(device);
    601         if (state != BluetoothInputDevice.STATE_CONNECTED) {
    602             return false;
    603         }
    604         Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME);
    605         msg.obj = device;
    606         Bundle data = new Bundle();
    607         data.putByte(BluetoothInputDevice.EXTRA_IDLE_TIME, idleTime);
    608         msg.setData(data);
    609         mHandler.sendMessage(msg);
    610         return true;
    611     }
    612 
    613     private void onGetProtocolMode(byte[] address, int mode) {
    614         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
    615         msg.obj = address;
    616         msg.arg1 = mode;
    617         mHandler.sendMessage(msg);
    618     }
    619 
    620     private void onGetIdleTime(byte[] address, int idleTime) {
    621         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME);
    622         msg.obj = address;
    623         msg.arg1 = idleTime;
    624         mHandler.sendMessage(msg);
    625     }
    626 
    627     private void onGetReport(byte[] address, byte[] report, int rpt_size) {
    628         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT);
    629         msg.obj = address;
    630         Bundle data = new Bundle();
    631         data.putByteArray(BluetoothInputDevice.EXTRA_REPORT, report);
    632         data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, rpt_size);
    633         msg.setData(data);
    634         mHandler.sendMessage(msg);
    635     }
    636 
    637     private void onHandshake(byte[] address, int status) {
    638         Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE);
    639         msg.obj = address;
    640         msg.arg1 = status;
    641         mHandler.sendMessage(msg);
    642     }
    643 
    644     private void onVirtualUnplug(byte[] address, int status) {
    645         Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
    646         msg.obj = address;
    647         msg.arg1 = status;
    648         mHandler.sendMessage(msg);
    649     }
    650 
    651     private void onConnectStateChanged(byte[] address, int state) {
    652         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
    653         msg.obj = address;
    654         msg.arg1 = state;
    655         mHandler.sendMessage(msg);
    656     }
    657 
    658     // This method does not check for error conditon (newState == prevState)
    659     private void broadcastConnectionState(BluetoothDevice device, int newState) {
    660         Integer prevStateInteger = mInputDevices.get(device);
    661         int prevState = (prevStateInteger == null) ? BluetoothInputDevice.STATE_DISCONNECTED :
    662                                                      prevStateInteger;
    663         if (prevState == newState) {
    664             Log.w(TAG, "no state change: " + newState);
    665             return;
    666         }
    667         mInputDevices.put(device, newState);
    668 
    669         /* Notifying the connection state change of the profile before sending the intent for
    670            connection state change, as it was causing a race condition, with the UI not being
    671            updated with the correct connection state. */
    672         log("Connection state " + device + ": " + prevState + "->" + newState);
    673         Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
    674         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    675         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
    676         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    677         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    678         sendBroadcast(intent, BLUETOOTH_PERM);
    679     }
    680 
    681     private void broadcastHandshake(BluetoothDevice device, int status) {
    682         Intent intent = new Intent(BluetoothInputDevice.ACTION_HANDSHAKE);
    683         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    684         intent.putExtra(BluetoothInputDevice.EXTRA_STATUS, status);
    685         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    686         sendBroadcast(intent, BLUETOOTH_PERM);
    687     }
    688 
    689     private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
    690         Intent intent = new Intent(BluetoothInputDevice.ACTION_PROTOCOL_MODE_CHANGED);
    691         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    692         intent.putExtra(BluetoothInputDevice.EXTRA_PROTOCOL_MODE, protocolMode);
    693         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    694         sendBroadcast(intent, BLUETOOTH_PERM);
    695         if (DBG) log("Protocol Mode (" + device + "): " + protocolMode);
    696     }
    697 
    698     private void broadcastReport(BluetoothDevice device, byte[] report, int rpt_size) {
    699         Intent intent = new Intent(BluetoothInputDevice.ACTION_REPORT);
    700         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    701         intent.putExtra(BluetoothInputDevice.EXTRA_REPORT, report);
    702         intent.putExtra(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, rpt_size);
    703         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    704         sendBroadcast(intent, BLUETOOTH_PERM);
    705     }
    706 
    707     private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
    708         Intent intent = new Intent(BluetoothInputDevice.ACTION_VIRTUAL_UNPLUG_STATUS);
    709         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    710         intent.putExtra(BluetoothInputDevice.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
    711         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    712         sendBroadcast(intent, BLUETOOTH_PERM);
    713     }
    714 
    715     private void broadcastIdleTime(BluetoothDevice device, int idleTime) {
    716         Intent intent = new Intent(BluetoothInputDevice.ACTION_IDLE_TIME_CHANGED);
    717         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    718         intent.putExtra(BluetoothInputDevice.EXTRA_IDLE_TIME, idleTime);
    719         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    720         sendBroadcast(intent, BLUETOOTH_PERM);
    721         if (DBG) log("Idle time (" + device + "): " + idleTime);
    722     }
    723 
    724     private boolean okToConnect(BluetoothDevice device) {
    725         AdapterService adapterService = AdapterService.getAdapterService();
    726         //check if it is inbound connection in Quiet mode, priority and Bond status
    727         //to decide if its ok to allow this connection
    728         if((adapterService == null)||
    729            ((adapterService.isQuietModeEnabled()) &&(mTargetDevice == null)) ||
    730            (BluetoothProfile.PRIORITY_OFF == getPriority(device)) ||
    731            (device.getBondState() == BluetoothDevice.BOND_NONE))
    732             return false;
    733 
    734         return true;
    735     }
    736     private static int convertHalState(int halState) {
    737         switch (halState) {
    738             case CONN_STATE_CONNECTED:
    739                 return BluetoothProfile.STATE_CONNECTED;
    740             case CONN_STATE_CONNECTING:
    741                 return BluetoothProfile.STATE_CONNECTING;
    742             case CONN_STATE_DISCONNECTED:
    743                 return BluetoothProfile.STATE_DISCONNECTED;
    744             case CONN_STATE_DISCONNECTING:
    745                 return BluetoothProfile.STATE_DISCONNECTING;
    746             default:
    747                 Log.e(TAG, "bad hid connection state: " + halState);
    748                 return BluetoothProfile.STATE_DISCONNECTED;
    749         }
    750     }
    751 
    752     @Override
    753     public void dump(StringBuilder sb) {
    754         super.dump(sb);
    755         println(sb, "mTargetDevice: " + mTargetDevice);
    756         println(sb, "mInputDevices:");
    757         for (BluetoothDevice device : mInputDevices.keySet()) {
    758             println(sb, "  " + device + " : " + mInputDevices.get(device));
    759         }
    760     }
    761 
    762     // Constants matching Hal header file bt_hh.h
    763     // bthh_connection_state_t
    764     private final static int CONN_STATE_CONNECTED = 0;
    765     private final static int CONN_STATE_CONNECTING = 1;
    766     private final static int CONN_STATE_DISCONNECTED = 2;
    767     private final static int CONN_STATE_DISCONNECTING = 3;
    768 
    769     private native static void classInitNative();
    770     private native void initializeNative();
    771     private native void cleanupNative();
    772     private native boolean connectHidNative(byte[] btAddress);
    773     private native boolean disconnectHidNative(byte[] btAddress);
    774     private native boolean getProtocolModeNative(byte[] btAddress);
    775     private native boolean virtualUnPlugNative(byte[] btAddress);
    776     private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
    777     private native boolean getReportNative(byte[]btAddress, byte reportType, byte reportId, int bufferSize);
    778     private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
    779     private native boolean sendDataNative(byte[] btAddress, String report);
    780     private native boolean setIdleTimeNative(byte[] btAddress, byte idleTime);
    781     private native boolean getIdleTimeNative(byte[] btAddress);
    782 }
    783