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