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