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