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