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