• 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 static java.util.Objects.requireNonNull;
24 
25 import android.annotation.RequiresPermission;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothHidHost;
28 import android.bluetooth.BluetoothProfile;
29 import android.bluetooth.BluetoothProtoEnums;
30 import android.bluetooth.BluetoothUuid;
31 import android.bluetooth.IBluetoothHidHost;
32 import android.content.AttributionSource;
33 import android.content.Intent;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.Message;
37 import android.os.ParcelUuid;
38 import android.os.UserHandle;
39 import android.sysprop.BluetoothProperties;
40 import android.util.Log;
41 
42 import androidx.annotation.VisibleForTesting;
43 
44 import com.android.bluetooth.BluetoothMetricsProto;
45 import com.android.bluetooth.Utils;
46 import com.android.bluetooth.btservice.AdapterService;
47 import com.android.bluetooth.btservice.MetricsLogger;
48 import com.android.bluetooth.btservice.ProfileService;
49 import com.android.bluetooth.btservice.storage.DatabaseManager;
50 import com.android.bluetooth.flags.Flags;
51 
52 import com.google.common.primitives.Ints;
53 
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.stream.Collectors;
59 
60 /** Provides Bluetooth Hid Host profile, as a service in the Bluetooth application. */
61 public class HidHostService extends ProfileService {
62     private static final String TAG = HidHostService.class.getSimpleName();
63 
64     public static final String ANDROID_HEADTRACKER_UUID_STR =
65             "109b862f-50e3-45cc-8ea1-ac62de4846d1";
66 
67     public static final ParcelUuid ANDROID_HEADTRACKER_UUID =
68             ParcelUuid.fromString(ANDROID_HEADTRACKER_UUID_STR);
69 
70     private static class InputDevice {
71         int mSelectedTransport = BluetoothDevice.TRANSPORT_AUTO;
72         private int mHidState = BluetoothProfile.STATE_DISCONNECTED;
73         private int mHogpState = BluetoothProfile.STATE_DISCONNECTED;
74 
getState(int transport)75         int getState(int transport) {
76             return (transport == BluetoothDevice.TRANSPORT_LE) ? mHogpState : mHidState;
77         }
78 
getState()79         int getState() {
80             return getState(mSelectedTransport);
81         }
82 
setState(int transport, int state)83         void setState(int transport, int state) {
84             if (transport == BluetoothDevice.TRANSPORT_LE) {
85                 mHogpState = state;
86             } else {
87                 mHidState = state;
88             }
89         }
90 
setState(int state)91         void setState(int state) {
92             setState(mSelectedTransport, state);
93         }
94 
95         @Override
toString()96         public String toString() {
97             return ("Selected transport=" + mSelectedTransport)
98                     + (" HID connection state=" + mHidState)
99                     + (" HOGP connection state=" + mHogpState);
100         }
101     }
102 
103     private static HidHostService sHidHostService;
104 
105     private final Map<BluetoothDevice, InputDevice> mInputDevices =
106             Collections.synchronizedMap(new HashMap<>());
107 
108     private final AdapterService mAdapterService;
109     private final DatabaseManager mDatabaseManager;
110     private final HidHostNativeInterface mNativeInterface;
111 
112     private boolean mNativeAvailable;
113     private BluetoothDevice mTargetDevice = null;
114 
115     private static final int MESSAGE_CONNECT = 1;
116     private static final int MESSAGE_DISCONNECT = 2;
117     private static final int MESSAGE_CONNECT_STATE_CHANGED = 3;
118     private static final int MESSAGE_GET_PROTOCOL_MODE = 4;
119     private static final int MESSAGE_VIRTUAL_UNPLUG = 5;
120     private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6;
121     private static final int MESSAGE_SET_PROTOCOL_MODE = 7;
122     private static final int MESSAGE_GET_REPORT = 8;
123     private static final int MESSAGE_ON_GET_REPORT = 9;
124     private static final int MESSAGE_SET_REPORT = 10;
125     private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12;
126     private static final int MESSAGE_ON_HANDSHAKE = 13;
127     private static final int MESSAGE_GET_IDLE_TIME = 14;
128     private static final int MESSAGE_ON_GET_IDLE_TIME = 15;
129     private static final int MESSAGE_SET_IDLE_TIME = 16;
130     private static final int MESSAGE_SET_PREFERRED_TRANSPORT = 17;
131     private static final int MESSAGE_SEND_DATA = 18;
132 
133     public static final int STATE_DISCONNECTED = BluetoothProfile.STATE_DISCONNECTED;
134     public static final int STATE_CONNECTING = BluetoothProfile.STATE_CONNECTING;
135     public static final int STATE_CONNECTED = BluetoothProfile.STATE_CONNECTED;
136     public static final int STATE_DISCONNECTING = BluetoothProfile.STATE_DISCONNECTING;
137     public static final int STATE_ACCEPTING = BluetoothProfile.STATE_DISCONNECTING + 1;
138 
HidHostService(AdapterService adapterService)139     public HidHostService(AdapterService adapterService) {
140         super(adapterService);
141 
142         mAdapterService = requireNonNull(adapterService);
143         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
144         mNativeInterface = requireNonNull(HidHostNativeInterface.getInstance());
145     }
146 
isEnabled()147     public static boolean isEnabled() {
148         return BluetoothProperties.isProfileHidHostEnabled().orElse(false);
149     }
150 
151     @Override
initBinder()152     public IProfileServiceBinder initBinder() {
153         return new BluetoothHidHostBinder(this);
154     }
155 
156     @Override
start()157     public void start() {
158         mNativeInterface.init(this);
159         mNativeAvailable = true;
160         setHidHostService(this);
161     }
162 
163     @Override
stop()164     public void stop() {
165         Log.d(TAG, "Stop");
166     }
167 
168     @Override
cleanup()169     public void cleanup() {
170         Log.d(TAG, "Cleanup");
171         if (mNativeAvailable) {
172             mNativeInterface.cleanup();
173             mNativeAvailable = false;
174         }
175 
176         if (mInputDevices != null) {
177             for (BluetoothDevice device : mInputDevices.keySet()) {
178                 // Set both HID and HOGP connection states to disconnected
179                 updateConnectionState(
180                         device, BluetoothDevice.TRANSPORT_LE, BluetoothProfile.STATE_DISCONNECTED);
181                 updateConnectionState(
182                         device,
183                         BluetoothDevice.TRANSPORT_BREDR,
184                         BluetoothProfile.STATE_DISCONNECTED);
185             }
186             mInputDevices.clear();
187         }
188         // TODO(b/72948646): this should be moved to stop()
189         setHidHostService(null);
190     }
191 
getIdentityAddress(BluetoothDevice device)192     private byte[] getIdentityAddress(BluetoothDevice device) {
193         if (Flags.identityAddressNullIfUnknown()) {
194             return Utils.getByteBrEdrAddress(mAdapterService, device);
195         } else {
196             return mAdapterService.getByteIdentityAddress(device);
197         }
198     }
199 
getByteAddress(BluetoothDevice device, int transport)200     private byte[] getByteAddress(BluetoothDevice device, int transport) {
201         ParcelUuid[] uuids = mAdapterService.getRemoteUuids(device);
202 
203         if (!Flags.allowSwitchingHidAndHogp()) {
204             boolean hogpSupported = Utils.arrayContains(uuids, BluetoothUuid.HOGP);
205             boolean headtrackerSupported =
206                     Flags.androidHeadtrackerService()
207                             && Utils.arrayContains(uuids, HidHostService.ANDROID_HEADTRACKER_UUID);
208 
209             if (hogpSupported || headtrackerSupported) {
210                 // Use pseudo address when HOGP is available
211                 return Utils.getByteAddress(device);
212             } else {
213                 // Otherwise use identity address
214                 return getIdentityAddress(device);
215             }
216         }
217 
218         if (transport == BluetoothDevice.TRANSPORT_LE) {
219             // Use pseudo address when HOGP is to be used
220             return Utils.getByteAddress(device);
221         } else if (transport == BluetoothDevice.TRANSPORT_BREDR) {
222             // Use identity address if HID is to be used
223             return getIdentityAddress(device);
224         } else { // BluetoothDevice.TRANSPORT_AUTO
225             boolean hidSupported = Utils.arrayContains(uuids, BluetoothUuid.HID);
226             // Prefer HID over HOGP
227             if (hidSupported) {
228                 // Use identity address if HID is available
229                 return getIdentityAddress(device);
230             } else {
231                 // Otherwise use pseudo address
232                 return Utils.getByteAddress(device);
233             }
234         }
235     }
236 
getByteAddress(BluetoothDevice device)237     private byte[] getByteAddress(BluetoothDevice device) {
238         return getByteAddress(device, getTransport(device));
239     }
240 
241     /**
242      * Retrieves device address type
243      *
244      * @param device remote device
245      * @return address type
246      */
getAddressType(BluetoothDevice device)247     private int getAddressType(BluetoothDevice device) {
248         if (Flags.getAddressTypeApi()) {
249             return device.getAddressType();
250         }
251 
252         return BluetoothDevice.ADDRESS_TYPE_PUBLIC;
253     }
254 
255     /**
256      * Retrieves preferred transport for the device
257      *
258      * @param device remote device
259      * @return transport
260      */
getTransport(BluetoothDevice device)261     private int getTransport(BluetoothDevice device) {
262         InputDevice inputDevice = mInputDevices.get(device);
263         if (inputDevice != null) {
264             return inputDevice.mSelectedTransport;
265         }
266 
267         return BluetoothDevice.TRANSPORT_AUTO;
268     }
269 
270     /**
271      * Saves the preferred transport for the input device. Adds an input device entry if not present
272      *
273      * @param device remote device
274      * @param transport preferred transport
275      */
setTransport(BluetoothDevice device, int transport)276     private void setTransport(BluetoothDevice device, int transport) {
277         InputDevice inputDevice = getOrCreateInputDevice(device);
278         if (inputDevice.mSelectedTransport != transport) {
279             inputDevice.mSelectedTransport = transport;
280         }
281     }
282 
283     /**
284      * Retrieves the input device object. Creates a new one if it does not exist
285      *
286      * @param device remote device
287      * @return input device object
288      */
getOrCreateInputDevice(BluetoothDevice device)289     private InputDevice getOrCreateInputDevice(BluetoothDevice device) {
290         return mInputDevices.computeIfAbsent(device, k -> new InputDevice());
291     }
292 
293     /**
294      * Retrieves the connection state
295      *
296      * @param device remote device
297      * @param transport transport
298      * @return connection state
299      */
getState(BluetoothDevice device, int transport)300     private int getState(BluetoothDevice device, int transport) {
301         InputDevice inputDevice = mInputDevices.get(device);
302         if (inputDevice != null) {
303             return inputDevice.getState(transport);
304         }
305 
306         return BluetoothProfile.STATE_DISCONNECTED;
307     }
308 
getHidHostService()309     public static synchronized HidHostService getHidHostService() {
310         if (sHidHostService == null) {
311             Log.w(TAG, "getHidHostService(): service is null");
312             return null;
313         }
314         if (!sHidHostService.isAvailable()) {
315             Log.w(TAG, "getHidHostService(): service is not available ");
316             return null;
317         }
318         return sHidHostService;
319     }
320 
setHidHostService(HidHostService instance)321     private static synchronized void setHidHostService(HidHostService instance) {
322         Log.d(TAG, "setHidHostService(): set to: " + instance);
323         sHidHostService = instance;
324     }
325 
326     /**
327      * Requests the native stack to start HID connection
328      *
329      * @param device remote device
330      * @param transport transport to be used
331      * @return true if successfully requested, else false
332      */
nativeConnect(BluetoothDevice device, int transport)333     private boolean nativeConnect(BluetoothDevice device, int transport) {
334         if (!mNativeInterface.connectHid(
335                 getByteAddress(device, transport), getAddressType(device), transport)) {
336             Log.w(
337                     TAG,
338                     "nativeConnect: Connection attempt failed."
339                             + (" device=" + device)
340                             + (" transport=" + transport));
341 
342             if (!Flags.allowSwitchingHidAndHogp()) {
343                 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTING);
344                 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTED);
345             }
346             return false;
347         }
348         return true;
349     }
350 
351     /**
352      * Requests the native stack to start HID disconnection
353      *
354      * @param device remote device
355      * @param transport transport
356      * @param reconnectAllowed true if remote device is allowed to initiate reconnections, else
357      *     false
358      * @return true if successfully requested, else false
359      */
nativeDisconnect( BluetoothDevice device, int transport, boolean reconnectAllowed)360     private boolean nativeDisconnect(
361             BluetoothDevice device, int transport, boolean reconnectAllowed) {
362         if (!mNativeInterface.disconnectHid(
363                 getByteAddress(device, transport),
364                 getAddressType(device),
365                 transport,
366                 reconnectAllowed)) {
367             Log.w(
368                     TAG,
369                     "nativeDisconnect: Disconnection attempt failed."
370                             + (" device=" + device)
371                             + (" transport=" + transport));
372             if (!Flags.allowSwitchingHidAndHogp()) {
373                 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTING);
374                 updateConnectionState(device, transport, BluetoothProfile.STATE_DISCONNECTED);
375             }
376             return false;
377         }
378         return true;
379     }
380 
381     private final Handler mHandler =
382             new Handler() {
383                 @Override
384                 public void handleMessage(Message msg) {
385                     Log.v(TAG, "handleMessage(): msg.what=" + msg.what);
386 
387                     switch (msg.what) {
388                         case MESSAGE_CONNECT:
389                             handleMessageConnect(msg);
390                             break;
391                         case MESSAGE_DISCONNECT:
392                             handleMessageDisconnect(msg);
393                             break;
394                         case MESSAGE_CONNECT_STATE_CHANGED:
395                             handleMessageConnectStateChanged(msg);
396                             break;
397                         case MESSAGE_GET_PROTOCOL_MODE:
398                             handleMessageGetProtocolMode(msg);
399                             break;
400                         case MESSAGE_ON_GET_PROTOCOL_MODE:
401                             handleMessageOnGetProtocolMode(msg);
402                             break;
403                         case MESSAGE_VIRTUAL_UNPLUG:
404                             handleMessageVirtualUnplug(msg);
405                             break;
406                         case MESSAGE_SET_PROTOCOL_MODE:
407                             handleMessageSetProtocolMode(msg);
408                             break;
409                         case MESSAGE_GET_REPORT:
410                             handleMessageGetReport(msg);
411                             break;
412                         case MESSAGE_ON_GET_REPORT:
413                             handleMessageOnGetReport(msg);
414                             break;
415                         case MESSAGE_ON_HANDSHAKE:
416                             handleMessageOnHandshake(msg);
417                             break;
418                         case MESSAGE_SET_REPORT:
419                             handleMessageSetReport(msg);
420                             break;
421                         case MESSAGE_ON_VIRTUAL_UNPLUG:
422                             handleMessageOnVirtualUnplug(msg);
423                             break;
424                         case MESSAGE_GET_IDLE_TIME:
425                             handleMessageGetIdleTime(msg);
426                             break;
427                         case MESSAGE_ON_GET_IDLE_TIME:
428                             handleMessageOnGetIdleTime(msg);
429                             break;
430                         case MESSAGE_SET_IDLE_TIME:
431                             handleMessageSetIdleTime(msg);
432                             break;
433                         case MESSAGE_SET_PREFERRED_TRANSPORT:
434                             handleMessageSetPreferredTransport(msg);
435                             break;
436                         case MESSAGE_SEND_DATA:
437                             handleMessageSendData(msg);
438                             break;
439                     }
440                 }
441             };
442 
handleMessageSendData(Message msg)443     private void handleMessageSendData(Message msg) {
444         if (!Flags.allowSwitchingHidAndHogp()) {
445             return;
446         }
447         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
448 
449         Bundle data = msg.getData();
450         String report = data.getString(BluetoothHidHost.EXTRA_REPORT);
451 
452         if (!mNativeInterface.sendData(
453                 getByteAddress(device), getAddressType(device), getTransport(device), report)) {
454             Log.e(TAG, "handleMessageSendData: Failed to send data");
455         }
456     }
457 
handleMessageSetPreferredTransport(Message msg)458     private void handleMessageSetPreferredTransport(Message msg) {
459         BluetoothDevice device = (BluetoothDevice) msg.obj;
460         int transport = msg.arg1;
461 
462         int prevTransport = getTransport(device);
463         Log.i(
464                 TAG,
465                 "handleMessageSetPreferredTransport: Transport changed"
466                         + (" device=" + device)
467                         + (" transport: prev=" + prevTransport + " -> new=" + transport));
468 
469         // Save the preferred transport
470         InputDevice inputDevice = getOrCreateInputDevice(device);
471         inputDevice.mSelectedTransport = transport;
472 
473         /* If connections are allowed, ensure that the previous transport is disconnected and the
474         new transport is connected */
475         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
476             if (prevTransport != transport) {
477                 Log.i(
478                         TAG,
479                         "handleMessageSetPreferredTransport: Connection switch"
480                                 + (" device=" + device)
481                                 + (" transport: prev=" + prevTransport + " -> new=" + transport));
482                 // Disconnect the other transport and disallow reconnections
483                 nativeDisconnect(device, prevTransport, false);
484 
485                 // Request to connect the preferred transport
486                 nativeConnect(device, transport);
487             }
488         }
489     }
490 
handleMessageSetIdleTime(Message msg)491     private void handleMessageSetIdleTime(Message msg) {
492         BluetoothDevice device = (BluetoothDevice) msg.obj;
493         Bundle data = msg.getData();
494         byte idleTime = data.getByte(BluetoothHidHost.EXTRA_IDLE_TIME);
495         if (!mNativeInterface.setIdleTime(
496                 getByteAddress(device), getAddressType(device), getTransport(device), idleTime)) {
497             Log.e(TAG, "Error: get idle time native returns false");
498         }
499     }
500 
handleMessageOnGetIdleTime(Message msg)501     private void handleMessageOnGetIdleTime(Message msg) {
502         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
503         int transport = msg.arg1;
504 
505         if (!checkTransport(device, transport, msg.what)) {
506             return;
507         }
508 
509         int idleTime = msg.arg2;
510         broadcastIdleTime(device, idleTime);
511     }
512 
handleMessageGetIdleTime(Message msg)513     private void handleMessageGetIdleTime(Message msg) {
514         BluetoothDevice device = (BluetoothDevice) msg.obj;
515         if (!mNativeInterface.getIdleTime(
516                 getByteAddress(device), getAddressType(device), getTransport(device))) {
517             Log.e(TAG, "Error: get idle time native returns false");
518         }
519     }
520 
handleMessageOnVirtualUnplug(Message msg)521     private void handleMessageOnVirtualUnplug(Message msg) {
522         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
523         int transport = msg.arg1;
524         if (!checkTransport(device, transport, msg.what)) {
525             return;
526         }
527         int status = msg.arg2;
528         broadcastVirtualUnplugStatus(device, status);
529     }
530 
handleMessageSetReport(Message msg)531     private void handleMessageSetReport(Message msg) {
532         BluetoothDevice device = (BluetoothDevice) msg.obj;
533         Bundle data = msg.getData();
534         byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
535         String report = data.getString(BluetoothHidHost.EXTRA_REPORT);
536         if (!mNativeInterface.setReport(
537                 getByteAddress(device),
538                 getAddressType(device),
539                 getTransport(device),
540                 reportType,
541                 report)) {
542             Log.e(TAG, "Error: set report native returns false");
543         }
544     }
545 
handleMessageOnHandshake(Message msg)546     private void handleMessageOnHandshake(Message msg) {
547         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
548         int transport = msg.arg1;
549         if (!checkTransport(device, transport, msg.what)) {
550             return;
551         }
552 
553         int status = msg.arg2;
554         broadcastHandshake(device, status);
555     }
556 
handleMessageOnGetReport(Message msg)557     private void handleMessageOnGetReport(Message msg) {
558         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
559         int transport = msg.arg1;
560         if (!checkTransport(device, transport, msg.what)) {
561             return;
562         }
563 
564         Bundle data = msg.getData();
565         byte[] report = data.getByteArray(BluetoothHidHost.EXTRA_REPORT);
566         int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
567         broadcastReport(device, report, bufferSize);
568     }
569 
handleMessageGetReport(Message msg)570     private void handleMessageGetReport(Message msg) {
571         BluetoothDevice device = (BluetoothDevice) msg.obj;
572         Bundle data = msg.getData();
573         byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
574         byte reportId = data.getByte(BluetoothHidHost.EXTRA_REPORT_ID);
575         int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
576         if (!mNativeInterface.getReport(
577                 getByteAddress(device),
578                 getAddressType(device),
579                 getTransport(device),
580                 reportType,
581                 reportId,
582                 bufferSize)) {
583             Log.e(TAG, "Error: get report native returns false");
584         }
585     }
586 
handleMessageSetProtocolMode(Message msg)587     private void handleMessageSetProtocolMode(Message msg) {
588         BluetoothDevice device = (BluetoothDevice) msg.obj;
589         byte protocolMode = (byte) msg.arg1;
590         Log.d(TAG, "sending set protocol mode(" + protocolMode + ")");
591         if (!mNativeInterface.setProtocolMode(
592                 getByteAddress(device),
593                 getAddressType(device),
594                 getTransport(device),
595                 protocolMode)) {
596             Log.e(TAG, "Error: set protocol mode native returns false");
597         }
598     }
599 
handleMessageVirtualUnplug(Message msg)600     private void handleMessageVirtualUnplug(Message msg) {
601         BluetoothDevice device = (BluetoothDevice) msg.obj;
602         if (!mNativeInterface.virtualUnPlug(
603                 getByteAddress(device), getAddressType(device), getTransport(device))) {
604             Log.e(TAG, "Error: virtual unplug native returns false");
605         }
606     }
607 
handleMessageOnGetProtocolMode(Message msg)608     private void handleMessageOnGetProtocolMode(Message msg) {
609         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
610         int transport = msg.arg1;
611         int protocolMode = msg.arg2;
612 
613         if (!checkTransport(device, transport, msg.what)) {
614             return;
615         }
616 
617         broadcastProtocolMode(device, protocolMode);
618     }
619 
handleMessageGetProtocolMode(Message msg)620     private void handleMessageGetProtocolMode(Message msg) {
621         BluetoothDevice device = (BluetoothDevice) msg.obj;
622         if (!mNativeInterface.getProtocolMode(
623                 getByteAddress(device), getAddressType(device), getTransport(device))) {
624             Log.e(TAG, "Error: get protocol mode native returns false");
625         }
626     }
627 
handleMessageConnectStateChanged(Message msg)628     private void handleMessageConnectStateChanged(Message msg) {
629         BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
630         int transport = msg.arg1;
631         int state = msg.arg2;
632         int prevState = getState(device, transport);
633 
634         if (Flags.allowSwitchingHidAndHogp()) {
635             InputDevice inputDevice = mInputDevices.get(device);
636             if (inputDevice != null) {
637                 // Update transport if it was not resolved already
638                 if (inputDevice.mSelectedTransport == BluetoothDevice.TRANSPORT_AUTO) {
639                     inputDevice.mSelectedTransport = transport;
640                     setTransport(device, transport);
641                 }
642             } else {
643                 // ACCEPTING state for unknown device indicates that this device
644                 // was loaded from storage. Add it in the record.
645                 if (state == STATE_ACCEPTING) {
646                     setTransport(device, transport);
647                 } else {
648                     Log.e(
649                             TAG,
650                             "handleMessageConnectStateChanged: Disconnect and unknown inputDevice"
651                                     + (" device=" + device)
652                                     + (" state=" + state));
653                     nativeDisconnect(device, transport, false);
654                     return;
655                 }
656             }
657 
658             if (transport != getTransport(device)) {
659                 Log.w(
660                         TAG,
661                         "handleMessageConnectStateChanged: Not preferred transport in message"
662                                 + (" device=" + device)
663                                 + (" transport=" + transport)
664                                 + (" newState=" + state)
665                                 + (" prevState=" + prevState));
666             }
667         } else {
668             // Only TRANSPORT_AUTO should be used when allowSwitchingHidAndHogp is disabled
669             transport = BluetoothDevice.TRANSPORT_AUTO;
670             setTransport(device, BluetoothDevice.TRANSPORT_AUTO);
671         }
672 
673         Log.d(
674                 TAG,
675                 "handleMessageConnectStateChanged:"
676                         + (" device=" + device)
677                         + (" newState=" + state)
678                         + (" prevState=" + prevState));
679 
680         boolean connectionAllowed = true;
681         // Process connection
682         if (prevState == BluetoothProfile.STATE_DISCONNECTED
683                 && state == BluetoothProfile.STATE_CONNECTED) {
684             connectionAllowed = processConnection(device, transport);
685         }
686 
687         // ACCEPTING state has to be treated as DISCONNECTED state
688         int reportedState = state;
689         if (state == STATE_ACCEPTING) {
690             reportedState = BluetoothProfile.STATE_DISCONNECTED;
691         }
692 
693         if (Flags.allowSwitchingHidAndHogp() || connectionAllowed) {
694             updateConnectionState(device, transport, reportedState);
695         }
696         updateQuietMode(device, reportedState);
697     }
698 
handleMessageDisconnect(Message msg)699     private void handleMessageDisconnect(Message msg) {
700         BluetoothDevice device = (BluetoothDevice) msg.obj;
701         int connectionPolicy = msg.arg1;
702 
703         boolean reconnectAllowed = true;
704         if (Flags.allowSwitchingHidAndHogp()) {
705             if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
706                 reconnectAllowed = false;
707             }
708         }
709 
710         nativeDisconnect(device, getTransport(device), reconnectAllowed);
711     }
712 
handleMessageConnect(Message msg)713     private void handleMessageConnect(Message msg) {
714         BluetoothDevice device = (BluetoothDevice) msg.obj;
715         InputDevice inputDevice = getOrCreateInputDevice(device);
716 
717         if (Flags.allowSwitchingHidAndHogp()) {
718             int connectionPolicy = getConnectionPolicy(device);
719             if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
720                 Log.e(
721                         TAG,
722                         "handleMessageConnect: Connection not allowed."
723                                 + (" device=" + device)
724                                 + (" connectionPolicy=" + connectionPolicy));
725 
726                 return;
727             }
728         }
729 
730         nativeConnect(device, inputDevice.mSelectedTransport);
731     }
732 
733     /**
734      * Checks if the reported transport does not match the selected transport
735      *
736      * @param device remote device
737      * @param transport reported transport
738      * @param message message ID for logging purpose
739      * @return true if transport matches, otherwise false
740      */
checkTransport(BluetoothDevice device, int transport, int message)741     private boolean checkTransport(BluetoothDevice device, int transport, int message) {
742         if (Flags.allowSwitchingHidAndHogp() && getTransport(device) != transport) {
743             Log.w(
744                     TAG,
745                     "checkTransport:"
746                             + (" message= " + message)
747                             + (" reported transport(" + transport + ")")
748                             + (" doesn't match selected transport(" + getTransport(device) + ")"));
749             return false;
750         }
751         return true;
752     }
753 
754     /**
755      * Handles connection complete
756      *
757      * @param device remote device
758      * @return true if the connection is being retained, otherwise false
759      */
processConnection(BluetoothDevice device, int transport)760     private boolean processConnection(BluetoothDevice device, int transport) {
761         if (!okToConnect(device)) {
762             Log.w(
763                     TAG,
764                     "processConnection: Incoming HID connection rejected."
765                             + (" device=" + device)
766                             + (" connectionPolicy=" + getConnectionPolicy(device)));
767 
768             if (Flags.allowSwitchingHidAndHogp()) {
769                 nativeDisconnect(device, transport, false);
770             } else {
771                 mNativeInterface.virtualUnPlug(
772                         getByteAddress(device), getAddressType(device), getTransport(device));
773             }
774             return false;
775         }
776         return true;
777     }
778 
779     /**
780      * Disables the quiet mode if target device gets connected
781      *
782      * @param device remote device
783      * @param state connection state
784      */
updateQuietMode(BluetoothDevice device, int state)785     private void updateQuietMode(BluetoothDevice device, int state) {
786         if (state == BluetoothProfile.STATE_CONNECTED
787                 && mTargetDevice != null
788                 && mTargetDevice.equals(device)) {
789             // Locally initiated connection, move out of quiet mode
790             Log.i(TAG, "updateQuietMode: Move out of quiet mode. device=" + device);
791             mTargetDevice = null;
792             mAdapterService.enable(false);
793         }
794     }
795 
796     @VisibleForTesting
797     static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub
798             implements IProfileServiceBinder {
799         private HidHostService mService;
800 
BluetoothHidHostBinder(HidHostService svc)801         BluetoothHidHostBinder(HidHostService svc) {
802             mService = svc;
803         }
804 
805         @Override
cleanup()806         public void cleanup() {
807             mService = null;
808         }
809 
810         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)811         private HidHostService getService(AttributionSource source) {
812             if (Utils.isInstrumentationTestMode()) {
813                 return mService;
814             }
815             if (!Utils.checkServiceAvailable(mService, TAG)
816                     || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
817                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
818                 return null;
819             }
820             return mService;
821         }
822 
823         @Override
connect(BluetoothDevice device, AttributionSource source)824         public boolean connect(BluetoothDevice device, AttributionSource source) {
825             HidHostService service = getService(source);
826             if (service == null) {
827                 return false;
828             }
829             enforceBluetoothPrivilegedPermission(service);
830             return service.connect(device);
831         }
832 
833         @Override
disconnect(BluetoothDevice device, AttributionSource source)834         public boolean disconnect(BluetoothDevice device, AttributionSource source) {
835             HidHostService service = getService(source);
836             if (service == null) {
837                 return false;
838             }
839             enforceBluetoothPrivilegedPermission(service);
840             return service.disconnect(device);
841         }
842 
843         @Override
getConnectionState(BluetoothDevice device, AttributionSource source)844         public int getConnectionState(BluetoothDevice device, AttributionSource source) {
845             HidHostService service = getService(source);
846             if (service == null) {
847                 return BluetoothProfile.STATE_DISCONNECTED;
848             }
849             return service.getConnectionState(device);
850         }
851 
852         @Override
getConnectedDevices(AttributionSource source)853         public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
854             return getDevicesMatchingConnectionStates(
855                     new int[] {BluetoothProfile.STATE_CONNECTED}, source);
856         }
857 
858         @Override
getDevicesMatchingConnectionStates( int[] states, AttributionSource source)859         public List<BluetoothDevice> getDevicesMatchingConnectionStates(
860                 int[] states, AttributionSource source) {
861             HidHostService service = getService(source);
862             if (service == null) {
863                 return Collections.emptyList();
864             }
865             return service.getDevicesMatchingConnectionStates(states);
866         }
867 
868         @Override
setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)869         public boolean setConnectionPolicy(
870                 BluetoothDevice device, int connectionPolicy, AttributionSource source) {
871             HidHostService service = getService(source);
872             if (service == null) {
873                 return false;
874             }
875             enforceBluetoothPrivilegedPermission(service);
876             return service.setConnectionPolicy(device, connectionPolicy);
877         }
878 
879         @Override
getConnectionPolicy(BluetoothDevice device, AttributionSource source)880         public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
881             HidHostService service = getService(source);
882             if (service == null) {
883                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
884             }
885             enforceBluetoothPrivilegedPermission(service);
886             return service.getConnectionPolicy(device);
887         }
888 
889         @Override
setPreferredTransport( BluetoothDevice device, int transport, AttributionSource source)890         public boolean setPreferredTransport(
891                 BluetoothDevice device, int transport, AttributionSource source) {
892             HidHostService service = getService(source);
893             if (service == null) {
894                 return false;
895             }
896             enforceBluetoothPrivilegedPermission(service);
897             return service.setPreferredTransport(device, transport);
898         }
899 
900         @Override
getPreferredTransport(BluetoothDevice device, AttributionSource source)901         public int getPreferredTransport(BluetoothDevice device, AttributionSource source) {
902             HidHostService service = getService(source);
903             if (service == null) {
904                 return BluetoothDevice.TRANSPORT_AUTO;
905             }
906             enforceBluetoothPrivilegedPermission(service);
907             return service.getPreferredTransport(device);
908         }
909 
910         /* The following APIs regarding test app for compliance */
911         @Override
getProtocolMode(BluetoothDevice device, AttributionSource source)912         public boolean getProtocolMode(BluetoothDevice device, AttributionSource source) {
913             HidHostService service = getService(source);
914             if (service == null) {
915                 return false;
916             }
917             return service.getProtocolMode(device);
918         }
919 
920         @Override
virtualUnplug(BluetoothDevice device, AttributionSource source)921         public boolean virtualUnplug(BluetoothDevice device, AttributionSource source) {
922             HidHostService service = getService(source);
923             if (service == null) {
924                 return false;
925             }
926             return service.virtualUnplug(device);
927         }
928 
929         @Override
setProtocolMode( BluetoothDevice device, int protocolMode, AttributionSource source)930         public boolean setProtocolMode(
931                 BluetoothDevice device, int protocolMode, AttributionSource source) {
932             HidHostService service = getService(source);
933             if (service == null) {
934                 return false;
935             }
936             return service.setProtocolMode(device, protocolMode);
937         }
938 
939         @Override
getReport( BluetoothDevice device, byte reportType, byte reportId, int bufferSize, AttributionSource source)940         public boolean getReport(
941                 BluetoothDevice device,
942                 byte reportType,
943                 byte reportId,
944                 int bufferSize,
945                 AttributionSource source) {
946             HidHostService service = getService(source);
947             if (service == null) {
948                 return false;
949             }
950             return service.getReport(device, reportType, reportId, bufferSize);
951         }
952 
953         @Override
setReport( BluetoothDevice device, byte reportType, String report, AttributionSource source)954         public boolean setReport(
955                 BluetoothDevice device, byte reportType, String report, AttributionSource source) {
956             HidHostService service = getService(source);
957             if (service == null) {
958                 return false;
959             }
960             return service.setReport(device, reportType, report);
961         }
962 
963         @Override
sendData(BluetoothDevice device, String report, AttributionSource source)964         public boolean sendData(BluetoothDevice device, String report, AttributionSource source) {
965             HidHostService service = getService(source);
966             if (service == null) {
967                 return false;
968             }
969             return service.sendData(device, report);
970         }
971 
972         @Override
setIdleTime( BluetoothDevice device, byte idleTime, AttributionSource source)973         public boolean setIdleTime(
974                 BluetoothDevice device, byte idleTime, AttributionSource source) {
975             HidHostService service = getService(source);
976             if (service == null) {
977                 return false;
978             }
979             return service.setIdleTime(device, idleTime);
980         }
981 
982         @Override
getIdleTime(BluetoothDevice device, AttributionSource source)983         public boolean getIdleTime(BluetoothDevice device, AttributionSource source) {
984             HidHostService service = getService(source);
985             if (service == null) {
986                 return false;
987             }
988             return service.getIdleTime(device);
989         }
990     }
991     ;
992 
993     // APIs
994 
995     /**
996      * Connects the hid host profile for the passed in device
997      *
998      * @param device is the device with which to connect the hid host profile
999      * @return true if connection request is passed down to mHandler.
1000      */
connect(BluetoothDevice device)1001     public boolean connect(BluetoothDevice device) {
1002         Log.d(TAG, "connect: device=" + device);
1003         int state = getConnectionState(device);
1004         if (state != BluetoothProfile.STATE_DISCONNECTED) {
1005             Log.e(TAG, "Device " + device + " not disconnected. state=" + state);
1006             return false;
1007         }
1008         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
1009             Log.e(TAG, "Device " + device + " CONNECTION_POLICY_FORBIDDEN");
1010             return false;
1011         }
1012 
1013         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device);
1014         mHandler.sendMessage(msg);
1015         return true;
1016     }
1017 
1018     /**
1019      * Disconnects the hid host profile from the passed in device
1020      *
1021      * @param device is the device with which to disconnect the hid host profile
1022      * @return true
1023      */
disconnect(BluetoothDevice device, int connectionPolicy)1024     private boolean disconnect(BluetoothDevice device, int connectionPolicy) {
1025         Log.d(TAG, "disconnect: device=" + device + " connectionPolicy=" + connectionPolicy);
1026         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device);
1027         msg.arg1 = connectionPolicy;
1028         mHandler.sendMessage(msg);
1029         return true;
1030     }
1031 
1032     /**
1033      * Disconnects the hid host profile from the passed in device
1034      *
1035      * @param device is the device with which to disconnect the hid host profile
1036      * @return true
1037      */
disconnect(BluetoothDevice device)1038     public boolean disconnect(BluetoothDevice device) {
1039         disconnect(device, getConnectionPolicy(device));
1040         return true;
1041     }
1042 
1043     /**
1044      * Get the current connection state of the profile
1045      *
1046      * @param device is the remote bluetooth device
1047      * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, {@link
1048      *     BluetoothProfile#STATE_CONNECTING} if this profile is being connected, {@link
1049      *     BluetoothProfile#STATE_CONNECTED} if this profile is connected, or {@link
1050      *     BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected
1051      */
getConnectionState(BluetoothDevice device)1052     public int getConnectionState(BluetoothDevice device) {
1053         Log.d(TAG, "getConnectionState: device=" + device);
1054         InputDevice inputDevice = mInputDevices.get(device);
1055         if (inputDevice != null) {
1056             return inputDevice.getState();
1057         }
1058         return BluetoothProfile.STATE_DISCONNECTED;
1059     }
1060 
getDevicesMatchingConnectionStates(int[] states)1061     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1062         Log.d(TAG, "getDevicesMatchingConnectionStates()");
1063         return mInputDevices.entrySet().stream()
1064                 .filter(entry -> Ints.asList(states).contains(entry.getValue().getState()))
1065                 .map(Map.Entry::getKey)
1066                 .collect(Collectors.toList());
1067     }
1068 
1069     /**
1070      * Set connection policy of the profile and connects it if connectionPolicy is {@link
1071      * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link
1072      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
1073      *
1074      * <p>The device should already be paired. Connection policy can be one of: {@link
1075      * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link
1076      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
1077      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
1078      *
1079      * @param device Paired bluetooth device
1080      * @param connectionPolicy is the connection policy to set to for this profile
1081      * @return true if connectionPolicy is set, false on error
1082      */
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)1083     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
1084         Log.d(TAG, "setConnectionPolicy: device=" + device);
1085 
1086         if (!mDatabaseManager.setProfileConnectionPolicy(
1087                 device, BluetoothProfile.HID_HOST, connectionPolicy)) {
1088             return false;
1089         }
1090         Log.d(TAG, "Saved connectionPolicy=" + connectionPolicy + " for device=" + device);
1091         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
1092             connect(device);
1093         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
1094             disconnect(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
1095             MetricsLogger.getInstance()
1096                     .count(BluetoothProtoEnums.HIDH_COUNT_CONNECTION_POLICY_DISABLED, 1);
1097         }
1098         return true;
1099     }
1100 
1101     /**
1102      * @see BluetoothHidHost#setPreferredTransport
1103      */
setPreferredTransport(BluetoothDevice device, int transport)1104     boolean setPreferredTransport(BluetoothDevice device, int transport) {
1105         Log.i(TAG, "setPreferredTransport: device=" + device + " transport=" + transport);
1106 
1107         if (mAdapterService.getBondState(device) != BluetoothDevice.BOND_BONDED) {
1108             Log.w(TAG, "Device " + device + " not bonded");
1109             return false;
1110         }
1111 
1112         ParcelUuid[] uuids = mAdapterService.getRemoteUuids(device);
1113         boolean hidSupported = Utils.arrayContains(uuids, BluetoothUuid.HID);
1114         boolean hogpSupported = Utils.arrayContains(uuids, BluetoothUuid.HOGP);
1115         boolean headtrackerSupported =
1116                 Flags.androidHeadtrackerService()
1117                         && Utils.arrayContains(uuids, HidHostService.ANDROID_HEADTRACKER_UUID);
1118         if (transport == BluetoothDevice.TRANSPORT_BREDR && !hidSupported) {
1119             Log.w(TAG, "device " + device + " does not support HID");
1120             return false;
1121         } else if (transport == BluetoothDevice.TRANSPORT_LE
1122                 && !(hogpSupported || headtrackerSupported)) {
1123             Log.w(TAG, "device " + device + " does not support HOGP");
1124             return false;
1125         }
1126 
1127         Message msg = mHandler.obtainMessage(MESSAGE_SET_PREFERRED_TRANSPORT, device);
1128         msg.arg1 = transport;
1129         mHandler.sendMessage(msg);
1130 
1131         return true;
1132     }
1133 
1134     /**
1135      * Get the connection policy of the profile.
1136      *
1137      * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
1138      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
1139      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
1140      *
1141      * @param device Bluetooth device
1142      * @return connection policy of the device
1143      */
getConnectionPolicy(BluetoothDevice device)1144     public int getConnectionPolicy(BluetoothDevice device) {
1145         Log.d(TAG, "getConnectionPolicy: device=" + device);
1146         return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HID_HOST);
1147     }
1148 
1149     /**
1150      * @see BluetoothHidHost#getPreferredTransport
1151      */
getPreferredTransport(BluetoothDevice device)1152     int getPreferredTransport(BluetoothDevice device) {
1153         Log.d(TAG, "getPreferredTransport: device=" + device);
1154 
1155         // TODO: Access to mInputDevices should be protected in binder thread
1156         return getTransport(device);
1157     }
1158 
1159     /* The following APIs regarding test app for compliance */
getProtocolMode(BluetoothDevice device)1160     boolean getProtocolMode(BluetoothDevice device) {
1161         Log.d(TAG, "getProtocolMode: device=" + device);
1162         int state = this.getConnectionState(device);
1163         if (state != BluetoothProfile.STATE_CONNECTED) {
1164             return false;
1165         }
1166         Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE, device);
1167         mHandler.sendMessage(msg);
1168         return true;
1169     }
1170 
virtualUnplug(BluetoothDevice device)1171     boolean virtualUnplug(BluetoothDevice device) {
1172         Log.d(TAG, "virtualUnplug: device=" + device);
1173         int state = this.getConnectionState(device);
1174         if (state != BluetoothProfile.STATE_CONNECTED) {
1175             return false;
1176         }
1177         Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG, device);
1178         mHandler.sendMessage(msg);
1179         return true;
1180     }
1181 
setProtocolMode(BluetoothDevice device, int protocolMode)1182     boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
1183         Log.d(TAG, "setProtocolMode: device=" + device);
1184         int state = this.getConnectionState(device);
1185         if (state != BluetoothProfile.STATE_CONNECTED) {
1186             return false;
1187         }
1188         Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
1189         msg.obj = device;
1190         msg.arg1 = protocolMode;
1191         mHandler.sendMessage(msg);
1192         return true;
1193     }
1194 
getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize)1195     boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
1196         Log.d(TAG, "getReport: device=" + device);
1197         int state = this.getConnectionState(device);
1198         if (state != BluetoothProfile.STATE_CONNECTED) {
1199             return false;
1200         }
1201         Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
1202         msg.obj = device;
1203         Bundle data = new Bundle();
1204         data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType);
1205         data.putByte(BluetoothHidHost.EXTRA_REPORT_ID, reportId);
1206         data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
1207         msg.setData(data);
1208         mHandler.sendMessage(msg);
1209         return true;
1210     }
1211 
setReport(BluetoothDevice device, byte reportType, String report)1212     boolean setReport(BluetoothDevice device, byte reportType, String report) {
1213         Log.d(TAG, "setReport: device=" + device);
1214         int state = this.getConnectionState(device);
1215         if (state != BluetoothProfile.STATE_CONNECTED) {
1216             return false;
1217         }
1218         Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
1219         msg.obj = device;
1220         Bundle data = new Bundle();
1221         data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType);
1222         data.putString(BluetoothHidHost.EXTRA_REPORT, report);
1223         msg.setData(data);
1224         mHandler.sendMessage(msg);
1225         return true;
1226     }
1227 
sendData(BluetoothDevice device, String report)1228     boolean sendData(BluetoothDevice device, String report) {
1229         Log.d(TAG, "sendData: device=" + device);
1230         int state = this.getConnectionState(device);
1231         if (state != BluetoothProfile.STATE_CONNECTED) {
1232             return false;
1233         }
1234 
1235         if (!Flags.allowSwitchingHidAndHogp()) {
1236             return mNativeInterface.sendData(
1237                     getByteAddress(device), getAddressType(device), getTransport(device), report);
1238         }
1239 
1240         Message msg = mHandler.obtainMessage(MESSAGE_SEND_DATA, device);
1241         Bundle data = new Bundle();
1242         data.putString(BluetoothHidHost.EXTRA_REPORT, report);
1243         msg.setData(data);
1244         mHandler.sendMessage(msg);
1245         return true;
1246     }
1247 
getIdleTime(BluetoothDevice device)1248     boolean getIdleTime(BluetoothDevice device) {
1249         Log.d(TAG, "getIdleTime: device=" + device);
1250         int state = this.getConnectionState(device);
1251         if (state != BluetoothProfile.STATE_CONNECTED) {
1252             return false;
1253         }
1254         Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device);
1255         mHandler.sendMessage(msg);
1256         return true;
1257     }
1258 
setIdleTime(BluetoothDevice device, byte idleTime)1259     boolean setIdleTime(BluetoothDevice device, byte idleTime) {
1260         Log.d(TAG, "setIdleTime: device=" + device);
1261         int state = this.getConnectionState(device);
1262         if (state != BluetoothProfile.STATE_CONNECTED) {
1263             return false;
1264         }
1265         Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME);
1266         msg.obj = device;
1267         Bundle data = new Bundle();
1268         data.putByte(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime);
1269         msg.setData(data);
1270         mHandler.sendMessage(msg);
1271         return true;
1272     }
1273 
onGetProtocolMode(byte[] address, int addressType, int transport, int mode)1274     void onGetProtocolMode(byte[] address, int addressType, int transport, int mode) {
1275         Log.d(TAG, "onGetProtocolMode()");
1276         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
1277         msg.obj = address;
1278         msg.arg1 = transport;
1279         msg.arg2 = mode;
1280         mHandler.sendMessage(msg);
1281     }
1282 
onGetIdleTime(byte[] address, int addressType, int transport, int idleTime)1283     void onGetIdleTime(byte[] address, int addressType, int transport, int idleTime) {
1284         Log.d(TAG, "onGetIdleTime()");
1285         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME);
1286         msg.obj = address;
1287         msg.arg1 = transport;
1288         msg.arg2 = idleTime;
1289         mHandler.sendMessage(msg);
1290     }
1291 
onGetReport(byte[] address, int addressType, int transport, byte[] report, int rptSize)1292     void onGetReport(byte[] address, int addressType, int transport, byte[] report, int rptSize) {
1293         Log.d(TAG, "onGetReport()");
1294         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT);
1295         msg.obj = address;
1296         msg.arg1 = transport;
1297         Bundle data = new Bundle();
1298         data.putByteArray(BluetoothHidHost.EXTRA_REPORT, report);
1299         data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize);
1300         msg.setData(data);
1301         mHandler.sendMessage(msg);
1302     }
1303 
onHandshake(byte[] address, int addressType, int transport, int status)1304     void onHandshake(byte[] address, int addressType, int transport, int status) {
1305         Log.d(TAG, "onHandshake: status=" + status);
1306         Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE);
1307         msg.obj = address;
1308         msg.arg1 = transport;
1309         msg.arg2 = status;
1310         mHandler.sendMessage(msg);
1311     }
1312 
onVirtualUnplug(byte[] address, int addressType, int transport, int status)1313     void onVirtualUnplug(byte[] address, int addressType, int transport, int status) {
1314         Log.d(TAG, "onVirtualUnplug: status=" + status);
1315         Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
1316         msg.obj = address;
1317         msg.arg1 = transport;
1318         msg.arg2 = status;
1319         mHandler.sendMessage(msg);
1320     }
1321 
onConnectStateChanged(byte[] address, int addressType, int transport, int state)1322     void onConnectStateChanged(byte[] address, int addressType, int transport, int state) {
1323         Log.d(TAG, "onConnectStateChanged: state=" + state);
1324         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED, address);
1325         msg.arg1 = transport;
1326         msg.arg2 = state;
1327         mHandler.sendMessage(msg);
1328     }
1329 
1330     /**
1331      * Saves new connection state. Broadcasts any change from previous state
1332      *
1333      * @param device remote device
1334      * @param transport transport
1335      * @param newState new connection state
1336      */
updateConnectionState(BluetoothDevice device, int transport, int newState)1337     private void updateConnectionState(BluetoothDevice device, int transport, int newState) {
1338         InputDevice inputDevice = mInputDevices.get(device);
1339         int prevState = BluetoothProfile.STATE_DISCONNECTED;
1340 
1341         if (Flags.allowSwitchingHidAndHogp()) {
1342             if (inputDevice == null) {
1343                 Log.w(
1344                         TAG,
1345                         "updateConnectionState: requested on unknown inputDevice"
1346                                 + (" device=" + device)
1347                                 + (" newState=" + newState)
1348                                 + (" transport=" + transport));
1349                 return;
1350             }
1351 
1352             if (transport == BluetoothDevice.TRANSPORT_AUTO) {
1353                 Log.w(
1354                         TAG,
1355                         "updateConnectionState: requested with AUTO transport"
1356                                 + (" device=" + device)
1357                                 + (" newState=" + newState));
1358                 return;
1359             }
1360 
1361             prevState = inputDevice.getState(transport);
1362             inputDevice.setState(transport, newState);
1363         } else {
1364             if (inputDevice == null) {
1365                 inputDevice = getOrCreateInputDevice(device);
1366             }
1367             prevState = inputDevice.getState();
1368             setTransport(device, transport);
1369             inputDevice.setState(newState);
1370         }
1371 
1372         if (prevState == newState) {
1373             Log.d(
1374                     TAG,
1375                     "updateConnectionState: No state change for"
1376                             + (" device=" + device)
1377                             + (" newState=" + newState)
1378                             + (" transport=" + transport));
1379             return;
1380         }
1381 
1382         if (newState == BluetoothProfile.STATE_CONNECTED) {
1383             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_HOST);
1384         }
1385 
1386         mInputDevices.put(device, inputDevice);
1387 
1388         broadcastConnectionState(device, transport, prevState, newState);
1389     }
1390 
1391     // This method does not check for error condition (newState == prevState)
broadcastConnectionState( BluetoothDevice device, int transport, int prevState, int newState)1392     private void broadcastConnectionState(
1393             BluetoothDevice device, int transport, int prevState, int newState) {
1394         // Notifying the connection state change of the profile before sending the intent for
1395         // connection state change, as it was causing a race condition, with the UI not being
1396         // updated with the correct connection state.
1397         Log.i(
1398                 TAG,
1399                 "broadcastConnectionState:"
1400                         + (" device= " + device)
1401                         + (" transport= " + transport)
1402                         + (" prevState=" + prevState + " -> newState=" + newState));
1403 
1404         mAdapterService.updateProfileConnectionAdapterProperties(
1405                 device, BluetoothProfile.HID_HOST, newState, prevState);
1406 
1407         Intent intent = new Intent(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
1408         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
1409         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
1410         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1411         if (Flags.allowSwitchingHidAndHogp()) {
1412             intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transport);
1413         }
1414         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1415         sendBroadcastAsUser(
1416                 intent,
1417                 UserHandle.ALL,
1418                 BLUETOOTH_CONNECT,
1419                 Utils.getTempBroadcastOptions().toBundle());
1420     }
1421 
broadcastHandshake(BluetoothDevice device, int status)1422     private void broadcastHandshake(BluetoothDevice device, int status) {
1423         Intent intent = new Intent(BluetoothHidHost.ACTION_HANDSHAKE);
1424         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1425         intent.putExtra(BluetoothHidHost.EXTRA_STATUS, status);
1426         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1427         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1428     }
1429 
broadcastProtocolMode(BluetoothDevice device, int protocolMode)1430     private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
1431         Intent intent = new Intent(BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED);
1432         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1433         intent.putExtra(BluetoothHidHost.EXTRA_PROTOCOL_MODE, protocolMode);
1434         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1435         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1436         Log.d(TAG, "broadcastProtocolMode: device=" + device + " protocolMode=" + protocolMode);
1437     }
1438 
broadcastReport(BluetoothDevice device, byte[] report, int rptSize)1439     private void broadcastReport(BluetoothDevice device, byte[] report, int rptSize) {
1440         Intent intent = new Intent(BluetoothHidHost.ACTION_REPORT);
1441         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1442         intent.putExtra(BluetoothHidHost.EXTRA_REPORT, report);
1443         intent.putExtra(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize);
1444         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1445         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1446     }
1447 
broadcastVirtualUnplugStatus(BluetoothDevice device, int status)1448     private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
1449         Intent intent = new Intent(BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS);
1450         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1451         intent.putExtra(BluetoothHidHost.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
1452         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1453         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1454     }
1455 
broadcastIdleTime(BluetoothDevice device, int idleTime)1456     private void broadcastIdleTime(BluetoothDevice device, int idleTime) {
1457         Intent intent = new Intent(BluetoothHidHost.ACTION_IDLE_TIME_CHANGED);
1458         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1459         intent.putExtra(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime);
1460         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1461         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1462         Log.d(TAG, "broadcastIdleTime: device=" + device + " idleTime=" + idleTime);
1463     }
1464 
1465     /**
1466      * Check whether can connect to a peer device. The check considers a number of factors during
1467      * the evaluation.
1468      *
1469      * @param device the peer device to connect to
1470      * @return true if connection is allowed, otherwise false
1471      */
1472     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
okToConnect(BluetoothDevice device)1473     public boolean okToConnect(BluetoothDevice device) {
1474         // Check if this is an incoming connection in Quiet mode.
1475         if (mAdapterService.isQuietModeEnabled() && mTargetDevice == null) {
1476             Log.w(TAG, "okToConnect: return false because of quiet mode enabled. device=" + device);
1477             return false;
1478         }
1479         // Check connection policy and accept or reject the connection.
1480         int connectionPolicy = getConnectionPolicy(device);
1481         int bondState = mAdapterService.getBondState(device);
1482         // Allow this connection only if the device is bonded. Any attempt to connect while
1483         // bonding would potentially lead to an unauthorized connection.
1484         if (bondState != BluetoothDevice.BOND_BONDED) {
1485             Log.w(TAG, "okToConnect: return false, device=" + device + " bondState=" + bondState);
1486             return false;
1487         } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
1488                 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
1489             // Otherwise, reject the connection if connectionPolicy is not valid.
1490             Log.w(
1491                     TAG,
1492                     "okToConnect: return false,"
1493                             + (" device=" + device)
1494                             + (" connectionPolicy=" + connectionPolicy));
1495             return false;
1496         }
1497         return true;
1498     }
1499 
1500     @Override
dump(StringBuilder sb)1501     public void dump(StringBuilder sb) {
1502         super.dump(sb);
1503         println(sb, "mTargetDevice: " + mTargetDevice);
1504         println(sb, "mInputDevices:");
1505         mInputDevices.forEach(
1506                 (k, v) -> sb.append(" " + k.getAddressForLogging() + " : " + v + "\n"));
1507     }
1508 }
1509