• 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.util.Log;
29 
30 import androidx.annotation.VisibleForTesting;
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                         virtualUnPlugNative(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         if (DBG) {
527             Log.d(TAG, "Saved priority " + device + " = " + priority);
528         }
529         AdapterService.getAdapterService().getDatabase()
530                 .setProfilePriority(device, BluetoothProfile.HID_HOST, priority);
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         return AdapterService.getAdapterService().getDatabase()
540                 .getProfilePriority(device, BluetoothProfile.HID_HOST);
541     }
542 
543     /* The following APIs regarding test app for compliance */
getProtocolMode(BluetoothDevice device)544     boolean getProtocolMode(BluetoothDevice device) {
545         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
546         if (DBG) {
547             Log.d(TAG, "getProtocolMode: " + device.getAddress());
548         }
549         int state = this.getConnectionState(device);
550         if (state != BluetoothHidHost.STATE_CONNECTED) {
551             return false;
552         }
553         Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE, device);
554         mHandler.sendMessage(msg);
555         return true;
556         /* String objectPath = getObjectPathFromAddress(device.getAddress());
557             return getProtocolModeInputDeviceNative(objectPath);*/
558     }
559 
virtualUnplug(BluetoothDevice device)560     boolean virtualUnplug(BluetoothDevice device) {
561         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
562         if (DBG) {
563             Log.d(TAG, "virtualUnplug: " + device.getAddress());
564         }
565         int state = this.getConnectionState(device);
566         if (state != BluetoothHidHost.STATE_CONNECTED) {
567             return false;
568         }
569         Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG, device);
570         mHandler.sendMessage(msg);
571         return true;
572     }
573 
setProtocolMode(BluetoothDevice device, int protocolMode)574     boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
575         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
576         if (DBG) {
577             Log.d(TAG, "setProtocolMode: " + device.getAddress());
578         }
579         int state = this.getConnectionState(device);
580         if (state != BluetoothHidHost.STATE_CONNECTED) {
581             return false;
582         }
583         Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
584         msg.obj = device;
585         msg.arg1 = protocolMode;
586         mHandler.sendMessage(msg);
587         return true;
588     }
589 
getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize)590     boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
591         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
592         if (DBG) {
593             Log.d(TAG, "getReport: " + device.getAddress());
594         }
595         int state = this.getConnectionState(device);
596         if (state != BluetoothHidHost.STATE_CONNECTED) {
597             return false;
598         }
599         Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
600         msg.obj = device;
601         Bundle data = new Bundle();
602         data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType);
603         data.putByte(BluetoothHidHost.EXTRA_REPORT_ID, reportId);
604         data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
605         msg.setData(data);
606         mHandler.sendMessage(msg);
607         return true;
608     }
609 
setReport(BluetoothDevice device, byte reportType, String report)610     boolean setReport(BluetoothDevice device, byte reportType, String report) {
611         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
612         if (DBG) {
613             Log.d(TAG, "setReport: " + device.getAddress());
614         }
615         int state = this.getConnectionState(device);
616         if (state != BluetoothHidHost.STATE_CONNECTED) {
617             return false;
618         }
619         Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
620         msg.obj = device;
621         Bundle data = new Bundle();
622         data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType);
623         data.putString(BluetoothHidHost.EXTRA_REPORT, report);
624         msg.setData(data);
625         mHandler.sendMessage(msg);
626         return true;
627 
628     }
629 
sendData(BluetoothDevice device, String report)630     boolean sendData(BluetoothDevice device, String report) {
631         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
632         if (DBG) {
633             Log.d(TAG, "sendData: " + device.getAddress());
634         }
635         int state = this.getConnectionState(device);
636         if (state != BluetoothHidHost.STATE_CONNECTED) {
637             return false;
638         }
639 
640         return sendDataNative(Utils.getByteAddress(device), report);
641     }
642 
getIdleTime(BluetoothDevice device)643     boolean getIdleTime(BluetoothDevice device) {
644         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
645         if (DBG) Log.d(TAG, "getIdleTime: " + device.getAddress());
646         int state = this.getConnectionState(device);
647         if (state != BluetoothHidHost.STATE_CONNECTED) {
648             return false;
649         }
650         Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device);
651         mHandler.sendMessage(msg);
652         return true;
653     }
654 
setIdleTime(BluetoothDevice device, byte idleTime)655     boolean setIdleTime(BluetoothDevice device, byte idleTime) {
656         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
657         if (DBG) Log.d(TAG, "setIdleTime: " + device.getAddress());
658         int state = this.getConnectionState(device);
659         if (state != BluetoothHidHost.STATE_CONNECTED) {
660             return false;
661         }
662         Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME);
663         msg.obj = device;
664         Bundle data = new Bundle();
665         data.putByte(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime);
666         msg.setData(data);
667         mHandler.sendMessage(msg);
668         return true;
669     }
670 
onGetProtocolMode(byte[] address, int mode)671     private void onGetProtocolMode(byte[] address, int mode) {
672         if (DBG) Log.d(TAG, "onGetProtocolMode()");
673         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
674         msg.obj = address;
675         msg.arg1 = mode;
676         mHandler.sendMessage(msg);
677     }
678 
onGetIdleTime(byte[] address, int idleTime)679     private void onGetIdleTime(byte[] address, int idleTime) {
680         if (DBG) Log.d(TAG, "onGetIdleTime()");
681         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME);
682         msg.obj = address;
683         msg.arg1 = idleTime;
684         mHandler.sendMessage(msg);
685     }
686 
onGetReport(byte[] address, byte[] report, int rptSize)687     private void onGetReport(byte[] address, byte[] report, int rptSize) {
688         if (DBG) Log.d(TAG, "onGetReport()");
689         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT);
690         msg.obj = address;
691         Bundle data = new Bundle();
692         data.putByteArray(BluetoothHidHost.EXTRA_REPORT, report);
693         data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize);
694         msg.setData(data);
695         mHandler.sendMessage(msg);
696     }
697 
onHandshake(byte[] address, int status)698     private void onHandshake(byte[] address, int status) {
699         if (DBG) Log.d(TAG, "onHandshake: status=" + status);
700         Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE);
701         msg.obj = address;
702         msg.arg1 = status;
703         mHandler.sendMessage(msg);
704     }
705 
onVirtualUnplug(byte[] address, int status)706     private void onVirtualUnplug(byte[] address, int status) {
707         if (DBG) Log.d(TAG, "onVirtualUnplug: status=" + status);
708         Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
709         msg.obj = address;
710         msg.arg1 = status;
711         mHandler.sendMessage(msg);
712     }
713 
onConnectStateChanged(byte[] address, int state)714     private void onConnectStateChanged(byte[] address, int state) {
715         if (DBG) Log.d(TAG, "onConnectStateChanged: state=" + state);
716         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
717         msg.obj = address;
718         msg.arg1 = state;
719         mHandler.sendMessage(msg);
720     }
721 
722     // This method does not check for error conditon (newState == prevState)
broadcastConnectionState(BluetoothDevice device, int newState)723     private void broadcastConnectionState(BluetoothDevice device, int newState) {
724         Integer prevStateInteger = mInputDevices.get(device);
725         int prevState = (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED
726                 : prevStateInteger;
727         if (prevState == newState) {
728             Log.w(TAG, "no state change: " + newState);
729             return;
730         }
731         if (newState == BluetoothProfile.STATE_CONNECTED) {
732             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_HOST);
733         }
734         mInputDevices.put(device, newState);
735 
736         /* Notifying the connection state change of the profile before sending the intent for
737            connection state change, as it was causing a race condition, with the UI not being
738            updated with the correct connection state. */
739         Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState);
740         Intent intent = new Intent(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
741         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
742         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
743         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
744         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
745         sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
746     }
747 
broadcastHandshake(BluetoothDevice device, int status)748     private void broadcastHandshake(BluetoothDevice device, int status) {
749         Intent intent = new Intent(BluetoothHidHost.ACTION_HANDSHAKE);
750         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
751         intent.putExtra(BluetoothHidHost.EXTRA_STATUS, status);
752         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
753         sendBroadcast(intent, BLUETOOTH_PERM);
754     }
755 
broadcastProtocolMode(BluetoothDevice device, int protocolMode)756     private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
757         Intent intent = new Intent(BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED);
758         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
759         intent.putExtra(BluetoothHidHost.EXTRA_PROTOCOL_MODE, protocolMode);
760         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
761         sendBroadcast(intent, BLUETOOTH_PERM);
762         if (DBG) {
763             Log.d(TAG, "Protocol Mode (" + device + "): " + protocolMode);
764         }
765     }
766 
broadcastReport(BluetoothDevice device, byte[] report, int rptSize)767     private void broadcastReport(BluetoothDevice device, byte[] report, int rptSize) {
768         Intent intent = new Intent(BluetoothHidHost.ACTION_REPORT);
769         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
770         intent.putExtra(BluetoothHidHost.EXTRA_REPORT, report);
771         intent.putExtra(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize);
772         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
773         sendBroadcast(intent, BLUETOOTH_PERM);
774     }
775 
broadcastVirtualUnplugStatus(BluetoothDevice device, int status)776     private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
777         Intent intent = new Intent(BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS);
778         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
779         intent.putExtra(BluetoothHidHost.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
780         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
781         sendBroadcast(intent, BLUETOOTH_PERM);
782     }
783 
broadcastIdleTime(BluetoothDevice device, int idleTime)784     private void broadcastIdleTime(BluetoothDevice device, int idleTime) {
785         Intent intent = new Intent(BluetoothHidHost.ACTION_IDLE_TIME_CHANGED);
786         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
787         intent.putExtra(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime);
788         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
789         sendBroadcast(intent, BLUETOOTH_PERM);
790         if (DBG) {
791             Log.d(TAG, "Idle time (" + device + "): " + idleTime);
792         }
793     }
794 
795     /**
796      * Check whether can connect to a peer device.
797      * The check considers a number of factors during the evaluation.
798      *
799      * @param device the peer device to connect to
800      * @return true if connection is allowed, otherwise false
801      */
802     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
okToConnect(BluetoothDevice device)803     public boolean okToConnect(BluetoothDevice device) {
804         AdapterService adapterService = AdapterService.getAdapterService();
805         // Check if adapter service is null.
806         if (adapterService == null) {
807             Log.w(TAG, "okToConnect: adapter service is null");
808             return false;
809         }
810         // Check if this is an incoming connection in Quiet mode.
811         if (adapterService.isQuietModeEnabled() && mTargetDevice == null) {
812             Log.w(TAG, "okToConnect: return false as quiet mode enabled");
813             return false;
814         }
815         // Check priority and accept or reject the connection.
816         int priority = getPriority(device);
817         int bondState = adapterService.getBondState(device);
818         // Allow this connection only if the device is bonded. Any attempt to connect while
819         // bonding would potentially lead to an unauthorized connection.
820         if (bondState != BluetoothDevice.BOND_BONDED) {
821             Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
822             return false;
823         } else if (priority != BluetoothProfile.PRIORITY_UNDEFINED
824                 && priority != BluetoothProfile.PRIORITY_ON
825                 && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) {
826             // Otherwise, reject the connection if priority is not valid.
827             Log.w(TAG, "okToConnect: return false, priority=" + priority);
828             return false;
829         }
830         return true;
831     }
832 
convertHalState(int halState)833     private static int convertHalState(int halState) {
834         switch (halState) {
835             case CONN_STATE_CONNECTED:
836                 return BluetoothProfile.STATE_CONNECTED;
837             case CONN_STATE_CONNECTING:
838                 return BluetoothProfile.STATE_CONNECTING;
839             case CONN_STATE_DISCONNECTED:
840                 return BluetoothProfile.STATE_DISCONNECTED;
841             case CONN_STATE_DISCONNECTING:
842                 return BluetoothProfile.STATE_DISCONNECTING;
843             default:
844                 Log.e(TAG, "bad hid connection state: " + halState);
845                 return BluetoothProfile.STATE_DISCONNECTED;
846         }
847     }
848 
849     @Override
dump(StringBuilder sb)850     public void dump(StringBuilder sb) {
851         super.dump(sb);
852         println(sb, "mTargetDevice: " + mTargetDevice);
853         println(sb, "mInputDevices:");
854         for (BluetoothDevice device : mInputDevices.keySet()) {
855             println(sb, "  " + device + " : " + mInputDevices.get(device));
856         }
857     }
858 
859     // Constants matching Hal header file bt_hh.h
860     // bthh_connection_state_t
861     private static final int CONN_STATE_CONNECTED = 0;
862     private static final int CONN_STATE_CONNECTING = 1;
863     private static final int CONN_STATE_DISCONNECTED = 2;
864     private static final int CONN_STATE_DISCONNECTING = 3;
865 
classInitNative()866     private static native void classInitNative();
867 
initializeNative()868     private native void initializeNative();
869 
cleanupNative()870     private native void cleanupNative();
871 
connectHidNative(byte[] btAddress)872     private native boolean connectHidNative(byte[] btAddress);
873 
disconnectHidNative(byte[] btAddress)874     private native boolean disconnectHidNative(byte[] btAddress);
875 
getProtocolModeNative(byte[] btAddress)876     private native boolean getProtocolModeNative(byte[] btAddress);
877 
virtualUnPlugNative(byte[] btAddress)878     private native boolean virtualUnPlugNative(byte[] btAddress);
879 
setProtocolModeNative(byte[] btAddress, byte protocolMode)880     private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
881 
getReportNative(byte[] btAddress, byte reportType, byte reportId, int bufferSize)882     private native boolean getReportNative(byte[] btAddress, byte reportType, byte reportId,
883             int bufferSize);
884 
setReportNative(byte[] btAddress, byte reportType, String report)885     private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
886 
sendDataNative(byte[] btAddress, String report)887     private native boolean sendDataNative(byte[] btAddress, String report);
888 
setIdleTimeNative(byte[] btAddress, byte idleTime)889     private native boolean setIdleTimeNative(byte[] btAddress, byte idleTime);
890 
getIdleTimeNative(byte[] btAddress)891     private native boolean getIdleTimeNative(byte[] btAddress);
892 }
893