Home | History | Annotate | Download | only in hid
      1 /*
      2  * Copyright (C) 2016 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.app.ActivityManager;
     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.bluetooth.IBluetoothHidDevice;
     26 import android.bluetooth.IBluetoothHidDeviceCallback;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.os.Binder;
     30 import android.os.Handler;
     31 import android.os.IBinder;
     32 import android.os.Message;
     33 import android.os.Process;
     34 import android.os.RemoteException;
     35 import android.util.Log;
     36 
     37 import com.android.bluetooth.BluetoothMetricsProto;
     38 import com.android.bluetooth.Utils;
     39 import com.android.bluetooth.btservice.MetricsLogger;
     40 import com.android.bluetooth.btservice.ProfileService;
     41 import com.android.internal.annotations.VisibleForTesting;
     42 
     43 import java.nio.ByteBuffer;
     44 import java.util.ArrayList;
     45 import java.util.Arrays;
     46 import java.util.List;
     47 import java.util.NoSuchElementException;
     48 
     49 /** @hide */
     50 public class HidDeviceService extends ProfileService {
     51     private static final boolean DBG = false;
     52     private static final String TAG = HidDeviceService.class.getSimpleName();
     53 
     54     private static final int MESSAGE_APPLICATION_STATE_CHANGED = 1;
     55     private static final int MESSAGE_CONNECT_STATE_CHANGED = 2;
     56     private static final int MESSAGE_GET_REPORT = 3;
     57     private static final int MESSAGE_SET_REPORT = 4;
     58     private static final int MESSAGE_SET_PROTOCOL = 5;
     59     private static final int MESSAGE_INTR_DATA = 6;
     60     private static final int MESSAGE_VC_UNPLUG = 7;
     61     private static final int MESSAGE_IMPORTANCE_CHANGE = 8;
     62 
     63     private static final int FOREGROUND_IMPORTANCE_CUTOFF =
     64             ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
     65 
     66     private static HidDeviceService sHidDeviceService;
     67 
     68     private HidDeviceNativeInterface mHidDeviceNativeInterface;
     69 
     70     private boolean mNativeAvailable = false;
     71     private BluetoothDevice mHidDevice;
     72     private int mHidDeviceState = BluetoothHidDevice.STATE_DISCONNECTED;
     73     private int mUserUid = 0;
     74     private IBluetoothHidDeviceCallback mCallback;
     75     private BluetoothHidDeviceDeathRecipient mDeathRcpt;
     76     private ActivityManager mActivityManager;
     77 
     78     private HidDeviceServiceHandler mHandler;
     79 
     80     private class HidDeviceServiceHandler extends Handler {
     81         @Override
     82         public void handleMessage(Message msg) {
     83             if (DBG) {
     84                 Log.d(TAG, "handleMessage(): msg.what=" + msg.what);
     85             }
     86 
     87             switch (msg.what) {
     88                 case MESSAGE_APPLICATION_STATE_CHANGED: {
     89                     BluetoothDevice device = msg.obj != null ? (BluetoothDevice) msg.obj : null;
     90                     boolean success = (msg.arg1 != 0);
     91 
     92                     if (success) {
     93                         Log.d(TAG, "App registered, set device to: " + device);
     94                         mHidDevice = device;
     95                     } else {
     96                         mHidDevice = null;
     97                     }
     98 
     99                     try {
    100                         if (mCallback != null) {
    101                             mCallback.onAppStatusChanged(device, success);
    102                         } else {
    103                             break;
    104                         }
    105                     } catch (RemoteException e) {
    106                         Log.e(TAG, "e=" + e.toString());
    107                         e.printStackTrace();
    108                     }
    109 
    110                     if (success) {
    111                         mDeathRcpt = new BluetoothHidDeviceDeathRecipient(HidDeviceService.this);
    112                         if (mCallback != null) {
    113                             IBinder binder = mCallback.asBinder();
    114                             try {
    115                                 binder.linkToDeath(mDeathRcpt, 0);
    116                                 Log.i(TAG, "IBinder.linkToDeath() ok");
    117                             } catch (RemoteException e) {
    118                                 e.printStackTrace();
    119                             }
    120                         }
    121                     } else if (mDeathRcpt != null) {
    122                         if (mCallback != null) {
    123                             IBinder binder = mCallback.asBinder();
    124                             try {
    125                                 binder.unlinkToDeath(mDeathRcpt, 0);
    126                                 Log.i(TAG, "IBinder.unlinkToDeath() ok");
    127                             } catch (NoSuchElementException e) {
    128                                 e.printStackTrace();
    129                             }
    130                             mDeathRcpt.cleanup();
    131                             mDeathRcpt = null;
    132                         }
    133                     }
    134 
    135                     if (!success) {
    136                         mCallback = null;
    137                     }
    138 
    139                     break;
    140                 }
    141 
    142                 case MESSAGE_CONNECT_STATE_CHANGED: {
    143                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    144                     int halState = msg.arg1;
    145                     int state = convertHalState(halState);
    146 
    147                     if (state != BluetoothHidDevice.STATE_DISCONNECTED) {
    148                         mHidDevice = device;
    149                     }
    150 
    151                     setAndBroadcastConnectionState(device, state);
    152 
    153                     try {
    154                         if (mCallback != null) {
    155                             mCallback.onConnectionStateChanged(device, state);
    156                         }
    157                     } catch (RemoteException e) {
    158                         e.printStackTrace();
    159                     }
    160                     break;
    161                 }
    162 
    163                 case MESSAGE_GET_REPORT:
    164                     byte type = (byte) msg.arg1;
    165                     byte id = (byte) msg.arg2;
    166                     int bufferSize = msg.obj == null ? 0 : ((Integer) msg.obj).intValue();
    167 
    168                     try {
    169                         if (mCallback != null) {
    170                             mCallback.onGetReport(mHidDevice, type, id, bufferSize);
    171                         }
    172                     } catch (RemoteException e) {
    173                         e.printStackTrace();
    174                     }
    175                     break;
    176 
    177                 case MESSAGE_SET_REPORT: {
    178                     byte reportType = (byte) msg.arg1;
    179                     byte reportId = (byte) msg.arg2;
    180                     byte[] data = ((ByteBuffer) msg.obj).array();
    181 
    182                     try {
    183                         if (mCallback != null) {
    184                             mCallback.onSetReport(mHidDevice, reportType, reportId, data);
    185                         }
    186                     } catch (RemoteException e) {
    187                         e.printStackTrace();
    188                     }
    189                     break;
    190                 }
    191 
    192                 case MESSAGE_SET_PROTOCOL:
    193                     byte protocol = (byte) msg.arg1;
    194 
    195                     try {
    196                         if (mCallback != null) {
    197                             mCallback.onSetProtocol(mHidDevice, protocol);
    198                         }
    199                     } catch (RemoteException e) {
    200                         e.printStackTrace();
    201                     }
    202                     break;
    203 
    204                 case MESSAGE_INTR_DATA:
    205                     byte reportId = (byte) msg.arg1;
    206                     byte[] data = ((ByteBuffer) msg.obj).array();
    207 
    208                     try {
    209                         if (mCallback != null) {
    210                             mCallback.onInterruptData(mHidDevice, reportId, data);
    211                         }
    212                     } catch (RemoteException e) {
    213                         e.printStackTrace();
    214                     }
    215                     break;
    216 
    217                 case MESSAGE_VC_UNPLUG:
    218                     try {
    219                         if (mCallback != null) {
    220                             mCallback.onVirtualCableUnplug(mHidDevice);
    221                         }
    222                     } catch (RemoteException e) {
    223                         e.printStackTrace();
    224                     }
    225                     mHidDevice = null;
    226                     break;
    227 
    228                 case MESSAGE_IMPORTANCE_CHANGE:
    229                     int importance = msg.arg1;
    230                     int uid = msg.arg2;
    231                     if (importance > FOREGROUND_IMPORTANCE_CUTOFF
    232                             && uid >= Process.FIRST_APPLICATION_UID) {
    233                         unregisterAppUid(uid);
    234                     }
    235                     break;
    236             }
    237         }
    238     }
    239 
    240     private static class BluetoothHidDeviceDeathRecipient implements IBinder.DeathRecipient {
    241         private HidDeviceService mService;
    242 
    243         BluetoothHidDeviceDeathRecipient(HidDeviceService service) {
    244             mService = service;
    245         }
    246 
    247         @Override
    248         public void binderDied() {
    249             Log.w(TAG, "Binder died, need to unregister app :(");
    250             mService.unregisterApp();
    251         }
    252 
    253         public void cleanup() {
    254             mService = null;
    255         }
    256     }
    257 
    258     private ActivityManager.OnUidImportanceListener mUidImportanceListener =
    259             new ActivityManager.OnUidImportanceListener() {
    260                 @Override
    261                 public void onUidImportance(final int uid, final int importance) {
    262                     Message message = mHandler.obtainMessage(MESSAGE_IMPORTANCE_CHANGE);
    263                     message.arg1 = importance;
    264                     message.arg2 = uid;
    265                     mHandler.sendMessage(message);
    266                 }
    267             };
    268 
    269     @VisibleForTesting
    270     static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub
    271             implements IProfileServiceBinder {
    272 
    273         private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName();
    274 
    275         private HidDeviceService mService;
    276 
    277         BluetoothHidDeviceBinder(HidDeviceService service) {
    278             mService = service;
    279         }
    280 
    281         @VisibleForTesting
    282         HidDeviceService getServiceForTesting() {
    283             if (mService != null && mService.isAvailable()) {
    284                 return mService;
    285             }
    286             return null;
    287         }
    288 
    289         @Override
    290         public void cleanup() {
    291             mService = null;
    292         }
    293 
    294         private HidDeviceService getService() {
    295             if (!Utils.checkCaller()) {
    296                 Log.w(TAG, "HidDevice call not allowed for non-active user");
    297                 return null;
    298             }
    299 
    300             if (mService != null && mService.isAvailable()) {
    301                 return mService;
    302             }
    303 
    304             return null;
    305         }
    306 
    307         @Override
    308         public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
    309                 BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
    310                 IBluetoothHidDeviceCallback callback) {
    311             if (DBG) {
    312                 Log.d(TAG, "registerApp()");
    313             }
    314 
    315             HidDeviceService service = getService();
    316             if (service == null) {
    317                 return false;
    318             }
    319 
    320             return service.registerApp(sdp, inQos, outQos, callback);
    321         }
    322 
    323         @Override
    324         public boolean unregisterApp() {
    325             if (DBG) {
    326                 Log.d(TAG, "unregisterApp()");
    327             }
    328 
    329             HidDeviceService service = getService();
    330             if (service == null) {
    331                 return false;
    332             }
    333 
    334             return service.unregisterApp();
    335         }
    336 
    337         @Override
    338         public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
    339             if (DBG) {
    340                 Log.d(TAG, "sendReport(): device=" + device + "  id=" + id);
    341             }
    342 
    343             HidDeviceService service = getService();
    344             if (service == null) {
    345                 return false;
    346             }
    347 
    348             return service.sendReport(device, id, data);
    349         }
    350 
    351         @Override
    352         public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
    353             if (DBG) {
    354                 Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
    355             }
    356 
    357             HidDeviceService service = getService();
    358             if (service == null) {
    359                 return false;
    360             }
    361 
    362             return service.replyReport(device, type, id, data);
    363         }
    364 
    365         @Override
    366         public boolean unplug(BluetoothDevice device) {
    367             if (DBG) {
    368                 Log.d(TAG, "unplug(): device=" + device);
    369             }
    370 
    371             HidDeviceService service = getService();
    372             if (service == null) {
    373                 return false;
    374             }
    375 
    376             return service.unplug(device);
    377         }
    378 
    379         @Override
    380         public boolean connect(BluetoothDevice device) {
    381             if (DBG) {
    382                 Log.d(TAG, "connect(): device=" + device);
    383             }
    384 
    385             HidDeviceService service = getService();
    386             if (service == null) {
    387                 return false;
    388             }
    389 
    390             return service.connect(device);
    391         }
    392 
    393         @Override
    394         public boolean disconnect(BluetoothDevice device) {
    395             if (DBG) {
    396                 Log.d(TAG, "disconnect(): device=" + device);
    397             }
    398 
    399             HidDeviceService service = getService();
    400             if (service == null) {
    401                 return false;
    402             }
    403 
    404             return service.disconnect(device);
    405         }
    406 
    407         @Override
    408         public boolean reportError(BluetoothDevice device, byte error) {
    409             if (DBG) {
    410                 Log.d(TAG, "reportError(): device=" + device + " error=" + error);
    411             }
    412 
    413             HidDeviceService service = getService();
    414             if (service == null) {
    415                 return false;
    416             }
    417 
    418             return service.reportError(device, error);
    419         }
    420 
    421         @Override
    422         public int getConnectionState(BluetoothDevice device) {
    423             if (DBG) {
    424                 Log.d(TAG, "getConnectionState(): device=" + device);
    425             }
    426 
    427             HidDeviceService service = getService();
    428             if (service == null) {
    429                 return BluetoothHidDevice.STATE_DISCONNECTED;
    430             }
    431 
    432             return service.getConnectionState(device);
    433         }
    434 
    435         @Override
    436         public List<BluetoothDevice> getConnectedDevices() {
    437             if (DBG) {
    438                 Log.d(TAG, "getConnectedDevices()");
    439             }
    440 
    441             return getDevicesMatchingConnectionStates(new int[]{BluetoothProfile.STATE_CONNECTED});
    442         }
    443 
    444         @Override
    445         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    446             if (DBG) {
    447                 Log.d(TAG,
    448                         "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
    449             }
    450 
    451             HidDeviceService service = getService();
    452             if (service == null) {
    453                 return new ArrayList<BluetoothDevice>(0);
    454             }
    455 
    456             return service.getDevicesMatchingConnectionStates(states);
    457         }
    458 
    459         @Override
    460         public String getUserAppName() {
    461             HidDeviceService service = getService();
    462             if (service == null) {
    463                 return "";
    464             }
    465             return service.getUserAppName();
    466         }
    467     }
    468 
    469     @Override
    470     protected IProfileServiceBinder initBinder() {
    471         return new BluetoothHidDeviceBinder(this);
    472     }
    473 
    474     private boolean checkDevice(BluetoothDevice device) {
    475         if (mHidDevice == null || !mHidDevice.equals(device)) {
    476             Log.w(TAG, "Unknown device: " + device);
    477             return false;
    478         }
    479         return true;
    480     }
    481 
    482     private boolean checkCallingUid() {
    483         int callingUid = Binder.getCallingUid();
    484         if (callingUid != mUserUid) {
    485             Log.w(TAG, "checkCallingUid(): caller UID doesn't match registered user UID");
    486             return false;
    487         }
    488         return true;
    489     }
    490 
    491     synchronized boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
    492             BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
    493             IBluetoothHidDeviceCallback callback) {
    494         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    495         if (mUserUid != 0) {
    496             Log.w(TAG, "registerApp(): failed because another app is registered");
    497             return false;
    498         }
    499 
    500         int callingUid = Binder.getCallingUid();
    501         if (DBG) {
    502             Log.d(TAG, "registerApp(): calling uid=" + callingUid);
    503         }
    504         if (callingUid >= Process.FIRST_APPLICATION_UID
    505                 && mActivityManager.getUidImportance(callingUid) > FOREGROUND_IMPORTANCE_CUTOFF) {
    506             Log.w(TAG, "registerApp(): failed because the app is not foreground");
    507             return false;
    508         }
    509         mUserUid = callingUid;
    510         mCallback = callback;
    511 
    512         return mHidDeviceNativeInterface.registerApp(
    513                 sdp.getName(),
    514                 sdp.getDescription(),
    515                 sdp.getProvider(),
    516                 sdp.getSubclass(),
    517                 sdp.getDescriptors(),
    518                 inQos == null
    519                         ? null
    520                         : new int[] {
    521                             inQos.getServiceType(),
    522                             inQos.getTokenRate(),
    523                             inQos.getTokenBucketSize(),
    524                             inQos.getPeakBandwidth(),
    525                             inQos.getLatency(),
    526                             inQos.getDelayVariation()
    527                         },
    528                 outQos == null
    529                         ? null
    530                         : new int[] {
    531                             outQos.getServiceType(),
    532                             outQos.getTokenRate(),
    533                             outQos.getTokenBucketSize(),
    534                             outQos.getPeakBandwidth(),
    535                             outQos.getLatency(),
    536                             outQos.getDelayVariation()
    537                         });
    538     }
    539 
    540     synchronized boolean unregisterApp() {
    541         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    542         if (DBG) {
    543             Log.d(TAG, "unregisterApp()");
    544         }
    545 
    546         int callingUid = Binder.getCallingUid();
    547         return unregisterAppUid(callingUid);
    548     }
    549 
    550     private synchronized boolean unregisterAppUid(int uid) {
    551         if (DBG) {
    552             Log.d(TAG, "unregisterAppUid(): uid=" + uid);
    553         }
    554 
    555         if (mUserUid != 0 && (uid == mUserUid || uid < Process.FIRST_APPLICATION_UID)) {
    556             mUserUid = 0;
    557             return mHidDeviceNativeInterface.unregisterApp();
    558         }
    559         if (DBG) {
    560             Log.d(TAG, "unregisterAppUid(): caller UID doesn't match user UID");
    561         }
    562         return false;
    563     }
    564 
    565     synchronized boolean sendReport(BluetoothDevice device, int id, byte[] data) {
    566         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    567         if (DBG) {
    568             Log.d(TAG, "sendReport(): device=" + device + " id=" + id);
    569         }
    570 
    571         return checkDevice(device) && checkCallingUid()
    572                 && mHidDeviceNativeInterface.sendReport(id, data);
    573     }
    574 
    575     synchronized boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
    576         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    577         if (DBG) {
    578             Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
    579         }
    580 
    581         return checkDevice(device) && checkCallingUid()
    582                 && mHidDeviceNativeInterface.replyReport(type, id, data);
    583     }
    584 
    585     synchronized boolean unplug(BluetoothDevice device) {
    586         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    587         if (DBG) {
    588             Log.d(TAG, "unplug(): device=" + device);
    589         }
    590 
    591         return checkDevice(device) && checkCallingUid()
    592                 && mHidDeviceNativeInterface.unplug();
    593     }
    594 
    595     synchronized boolean connect(BluetoothDevice device) {
    596         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    597         if (DBG) {
    598             Log.d(TAG, "connect(): device=" + device);
    599         }
    600 
    601         return checkCallingUid() && mHidDeviceNativeInterface.connect(device);
    602     }
    603 
    604     synchronized boolean disconnect(BluetoothDevice device) {
    605         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    606         if (DBG) {
    607             Log.d(TAG, "disconnect(): device=" + device);
    608         }
    609 
    610         int callingUid = Binder.getCallingUid();
    611         if (callingUid != mUserUid && callingUid >= Process.FIRST_APPLICATION_UID) {
    612             Log.w(TAG, "disconnect(): caller UID doesn't match user UID");
    613             return false;
    614         }
    615         return checkDevice(device) && mHidDeviceNativeInterface.disconnect();
    616     }
    617 
    618     synchronized boolean reportError(BluetoothDevice device, byte error) {
    619         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
    620         if (DBG) {
    621             Log.d(TAG, "reportError(): device=" + device + " error=" + error);
    622         }
    623 
    624         return checkDevice(device) && checkCallingUid()
    625                 && mHidDeviceNativeInterface.reportError(error);
    626     }
    627 
    628     synchronized String getUserAppName() {
    629         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    630         if (mUserUid < Process.FIRST_APPLICATION_UID) {
    631             return "";
    632         }
    633         String appName = getPackageManager().getNameForUid(mUserUid);
    634         return appName != null ? appName : "";
    635     }
    636 
    637     @Override
    638     protected boolean start() {
    639         if (DBG) {
    640             Log.d(TAG, "start()");
    641         }
    642 
    643         mHandler = new HidDeviceServiceHandler();
    644         mHidDeviceNativeInterface = HidDeviceNativeInterface.getInstance();
    645         mHidDeviceNativeInterface.init();
    646         mNativeAvailable = true;
    647         mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    648         mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
    649                 FOREGROUND_IMPORTANCE_CUTOFF);
    650         setHidDeviceService(this);
    651         return true;
    652     }
    653 
    654     @Override
    655     protected boolean stop() {
    656         if (DBG) {
    657             Log.d(TAG, "stop()");
    658         }
    659 
    660         if (sHidDeviceService == null) {
    661             Log.w(TAG, "stop() called before start()");
    662             return true;
    663         }
    664 
    665         setHidDeviceService(null);
    666         if (mNativeAvailable) {
    667             mHidDeviceNativeInterface.cleanup();
    668             mNativeAvailable = false;
    669         }
    670         mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
    671         return true;
    672     }
    673 
    674     @Override
    675     public boolean onUnbind(Intent intent) {
    676         Log.d(TAG, "Need to unregister app");
    677         unregisterApp();
    678         return super.onUnbind(intent);
    679     }
    680 
    681     /**
    682      * Get the HID Device Service instance
    683      * @return HID Device Service instance
    684      */
    685     public static synchronized HidDeviceService getHidDeviceService() {
    686         if (sHidDeviceService == null) {
    687             Log.d(TAG, "getHidDeviceService(): service is NULL");
    688             return null;
    689         }
    690         if (!sHidDeviceService.isAvailable()) {
    691             Log.d(TAG, "getHidDeviceService(): service is not available");
    692             return null;
    693         }
    694         return sHidDeviceService;
    695     }
    696 
    697     private static synchronized void setHidDeviceService(HidDeviceService instance) {
    698         if (DBG) {
    699             Log.d(TAG, "setHidDeviceService(): set to: " + instance);
    700         }
    701         sHidDeviceService = instance;
    702     }
    703 
    704     int getConnectionState(BluetoothDevice device) {
    705         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    706         if (mHidDevice != null && mHidDevice.equals(device)) {
    707             return mHidDeviceState;
    708         }
    709         return BluetoothHidDevice.STATE_DISCONNECTED;
    710     }
    711 
    712     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    713         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    714         List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
    715 
    716         if (mHidDevice != null) {
    717             for (int state : states) {
    718                 if (state == mHidDeviceState) {
    719                     inputDevices.add(mHidDevice);
    720                     break;
    721                 }
    722             }
    723         }
    724         return inputDevices;
    725     }
    726 
    727     synchronized void onApplicationStateChangedFromNative(BluetoothDevice device,
    728             boolean registered) {
    729         if (DBG) {
    730             Log.d(TAG, "onApplicationStateChanged(): registered=" + registered);
    731         }
    732 
    733         Message msg = mHandler.obtainMessage(MESSAGE_APPLICATION_STATE_CHANGED);
    734         msg.obj = device;
    735         msg.arg1 = registered ? 1 : 0;
    736         mHandler.sendMessage(msg);
    737     }
    738 
    739     synchronized void onConnectStateChangedFromNative(BluetoothDevice device, int state) {
    740         if (DBG) {
    741             Log.d(TAG, "onConnectStateChanged(): device="
    742                     + device + " state=" + state);
    743         }
    744 
    745         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
    746         msg.obj = device;
    747         msg.arg1 = state;
    748         mHandler.sendMessage(msg);
    749     }
    750 
    751     synchronized void onGetReportFromNative(byte type, byte id, short bufferSize) {
    752         if (DBG) {
    753             Log.d(TAG, "onGetReport(): type=" + type + " id=" + id + " bufferSize=" + bufferSize);
    754         }
    755 
    756         Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
    757         msg.obj = bufferSize > 0 ? new Integer(bufferSize) : null;
    758         msg.arg1 = type;
    759         msg.arg2 = id;
    760         mHandler.sendMessage(msg);
    761     }
    762 
    763     synchronized void onSetReportFromNative(byte reportType, byte reportId, byte[] data) {
    764         if (DBG) {
    765             Log.d(TAG, "onSetReport(): reportType=" + reportType + " reportId=" + reportId);
    766         }
    767 
    768         ByteBuffer bb = ByteBuffer.wrap(data);
    769 
    770         Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
    771         msg.arg1 = reportType;
    772         msg.arg2 = reportId;
    773         msg.obj = bb;
    774         mHandler.sendMessage(msg);
    775     }
    776 
    777     synchronized void onSetProtocolFromNative(byte protocol) {
    778         if (DBG) {
    779             Log.d(TAG, "onSetProtocol(): protocol=" + protocol);
    780         }
    781 
    782         Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL);
    783         msg.arg1 = protocol;
    784         mHandler.sendMessage(msg);
    785     }
    786 
    787     synchronized void onInterruptDataFromNative(byte reportId, byte[] data) {
    788         if (DBG) {
    789             Log.d(TAG, "onInterruptData(): reportId=" + reportId);
    790         }
    791 
    792         ByteBuffer bb = ByteBuffer.wrap(data);
    793 
    794         Message msg = mHandler.obtainMessage(MESSAGE_INTR_DATA);
    795         msg.arg1 = reportId;
    796         msg.obj = bb;
    797         mHandler.sendMessage(msg);
    798     }
    799 
    800     synchronized void onVirtualCableUnplugFromNative() {
    801         if (DBG) {
    802             Log.d(TAG, "onVirtualCableUnplug()");
    803         }
    804 
    805         Message msg = mHandler.obtainMessage(MESSAGE_VC_UNPLUG);
    806         mHandler.sendMessage(msg);
    807     }
    808 
    809     private void setAndBroadcastConnectionState(BluetoothDevice device, int newState) {
    810         if (DBG) {
    811             Log.d(TAG, "setAndBroadcastConnectionState(): device=" + device.getAddress()
    812                     + " oldState=" + mHidDeviceState + " newState=" + newState);
    813         }
    814 
    815         if (mHidDevice != null && !mHidDevice.equals(device)) {
    816             Log.w(TAG, "Connection state changed for unknown device, ignoring");
    817             return;
    818         }
    819 
    820         int prevState = mHidDeviceState;
    821         mHidDeviceState = newState;
    822 
    823         if (prevState == newState) {
    824             Log.w(TAG, "Connection state is unchanged, ignoring");
    825             return;
    826         }
    827 
    828         if (newState == BluetoothProfile.STATE_CONNECTED) {
    829             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_DEVICE);
    830         }
    831 
    832         Intent intent = new Intent(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
    833         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    834         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
    835         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    836         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    837         sendBroadcast(intent, BLUETOOTH_PERM);
    838     }
    839 
    840     private static int convertHalState(int halState) {
    841         switch (halState) {
    842             case HAL_CONN_STATE_CONNECTED:
    843                 return BluetoothProfile.STATE_CONNECTED;
    844             case HAL_CONN_STATE_CONNECTING:
    845                 return BluetoothProfile.STATE_CONNECTING;
    846             case HAL_CONN_STATE_DISCONNECTED:
    847                 return BluetoothProfile.STATE_DISCONNECTED;
    848             case HAL_CONN_STATE_DISCONNECTING:
    849                 return BluetoothProfile.STATE_DISCONNECTING;
    850             default:
    851                 return BluetoothProfile.STATE_DISCONNECTED;
    852         }
    853     }
    854 
    855     static final int HAL_CONN_STATE_CONNECTED = 0;
    856     static final int HAL_CONN_STATE_CONNECTING = 1;
    857     static final int HAL_CONN_STATE_DISCONNECTED = 2;
    858     static final int HAL_CONN_STATE_DISCONNECTING = 3;
    859 }
    860