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