1 /*
2  * Copyright (C) 2016 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 android.annotation.RequiresPermission;
22 import android.app.ActivityManager;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothHidDevice;
25 import android.bluetooth.BluetoothHidDeviceAppQosSettings;
26 import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
27 import android.bluetooth.BluetoothProfile;
28 import android.bluetooth.IBluetoothHidDevice;
29 import android.bluetooth.IBluetoothHidDeviceCallback;
30 import android.content.AttributionSource;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.os.Binder;
34 import android.os.Handler;
35 import android.os.IBinder;
36 import android.os.Looper;
37 import android.os.Message;
38 import android.os.Process;
39 import android.os.RemoteException;
40 import android.sysprop.BluetoothProperties;
41 import android.util.Log;
42 
43 import com.android.bluetooth.BluetoothMetricsProto;
44 import com.android.bluetooth.Utils;
45 import com.android.bluetooth.btservice.AdapterService;
46 import com.android.bluetooth.btservice.MetricsLogger;
47 import com.android.bluetooth.btservice.ProfileService;
48 import com.android.bluetooth.btservice.storage.DatabaseManager;
49 import com.android.internal.annotations.VisibleForTesting;
50 
51 import java.nio.ByteBuffer;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collections;
55 import java.util.List;
56 import java.util.NoSuchElementException;
57 import java.util.Objects;
58 
59 public class HidDeviceService extends ProfileService {
60     private static final String TAG = HidDeviceService.class.getSimpleName();
61 
62     private static final int MESSAGE_APPLICATION_STATE_CHANGED = 1;
63     private static final int MESSAGE_CONNECT_STATE_CHANGED = 2;
64     private static final int MESSAGE_GET_REPORT = 3;
65     private static final int MESSAGE_SET_REPORT = 4;
66     private static final int MESSAGE_SET_PROTOCOL = 5;
67     private static final int MESSAGE_INTR_DATA = 6;
68     private static final int MESSAGE_VC_UNPLUG = 7;
69     private static final int MESSAGE_IMPORTANCE_CHANGE = 8;
70 
71     private static final int FOREGROUND_IMPORTANCE_CUTOFF =
72             ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
73 
74     private static HidDeviceService sHidDeviceService;
75 
76     private DatabaseManager mDatabaseManager;
77     private HidDeviceNativeInterface mHidDeviceNativeInterface;
78 
79     private boolean mNativeAvailable = false;
80     private BluetoothDevice mHidDevice;
81     private int mHidDeviceState = BluetoothHidDevice.STATE_DISCONNECTED;
82     private int mUserUid = 0;
83     private IBluetoothHidDeviceCallback mCallback;
84     private BluetoothHidDeviceDeathRecipient mDeathRcpt;
85     private ActivityManager mActivityManager;
86 
87     private HidDeviceServiceHandler mHandler;
88 
HidDeviceService(Context ctx)89     public HidDeviceService(Context ctx) {
90         super(ctx);
91     }
92 
isEnabled()93     public static boolean isEnabled() {
94         return BluetoothProperties.isProfileHidDeviceEnabled().orElse(false);
95     }
96 
97     private class HidDeviceServiceHandler extends Handler {
HidDeviceServiceHandler(Looper looper)98         HidDeviceServiceHandler(Looper looper) {
99             super(looper);
100         }
101 
102         @Override
handleMessage(Message msg)103         public void handleMessage(Message msg) {
104             Log.d(TAG, "handleMessage(): msg.what=" + msg.what);
105 
106             switch (msg.what) {
107                 case MESSAGE_APPLICATION_STATE_CHANGED:
108                     {
109                         BluetoothDevice device = msg.obj != null ? (BluetoothDevice) msg.obj : null;
110                         boolean success = (msg.arg1 != 0);
111 
112                         if (success) {
113                             Log.d(TAG, "App registered, set device to: " + device);
114                             mHidDevice = device;
115                         } else {
116                             mHidDevice = null;
117                         }
118 
119                         try {
120                             if (mCallback != null) {
121                                 mCallback.onAppStatusChanged(device, success);
122                             } else {
123                                 break;
124                             }
125                         } catch (RemoteException e) {
126                             Log.e(TAG, "e=" + e.toString());
127                             e.printStackTrace();
128                         }
129 
130                         if (success) {
131                             mDeathRcpt =
132                                     new BluetoothHidDeviceDeathRecipient(HidDeviceService.this);
133                             if (mCallback != null) {
134                                 IBinder binder = mCallback.asBinder();
135                                 try {
136                                     binder.linkToDeath(mDeathRcpt, 0);
137                                     Log.i(TAG, "IBinder.linkToDeath() ok");
138                                 } catch (RemoteException e) {
139                                     e.printStackTrace();
140                                 }
141                             }
142                         } else if (mDeathRcpt != null) {
143                             if (mCallback != null) {
144                                 IBinder binder = mCallback.asBinder();
145                                 try {
146                                     binder.unlinkToDeath(mDeathRcpt, 0);
147                                     Log.i(TAG, "IBinder.unlinkToDeath() ok");
148                                 } catch (NoSuchElementException e) {
149                                     e.printStackTrace();
150                                 }
151                                 mDeathRcpt.cleanup();
152                                 mDeathRcpt = null;
153                             }
154                         }
155 
156                         if (!success) {
157                             mCallback = null;
158                         }
159 
160                         break;
161                     }
162 
163                 case MESSAGE_CONNECT_STATE_CHANGED:
164                     {
165                         BluetoothDevice device = (BluetoothDevice) msg.obj;
166                         int halState = msg.arg1;
167                         int state = convertHalState(halState);
168 
169                         if (state != BluetoothHidDevice.STATE_DISCONNECTED) {
170                             mHidDevice = device;
171                         }
172 
173                         setAndBroadcastConnectionState(device, state);
174 
175                         try {
176                             if (mCallback != null) {
177                                 mCallback.onConnectionStateChanged(device, state);
178                             }
179                         } catch (RemoteException e) {
180                             e.printStackTrace();
181                         }
182                         break;
183                     }
184 
185                 case MESSAGE_GET_REPORT:
186                     byte type = (byte) msg.arg1;
187                     byte id = (byte) msg.arg2;
188                     int bufferSize = msg.obj == null ? 0 : ((Integer) msg.obj).intValue();
189 
190                     try {
191                         if (mCallback != null) {
192                             mCallback.onGetReport(mHidDevice, type, id, bufferSize);
193                         }
194                     } catch (RemoteException e) {
195                         e.printStackTrace();
196                     }
197                     break;
198 
199                 case MESSAGE_SET_REPORT:
200                     {
201                         byte reportType = (byte) msg.arg1;
202                         byte reportId = (byte) msg.arg2;
203                         byte[] data = ((ByteBuffer) msg.obj).array();
204 
205                         try {
206                             if (mCallback != null) {
207                                 mCallback.onSetReport(mHidDevice, reportType, reportId, data);
208                             }
209                         } catch (RemoteException e) {
210                             e.printStackTrace();
211                         }
212                         break;
213                     }
214 
215                 case MESSAGE_SET_PROTOCOL:
216                     byte protocol = (byte) msg.arg1;
217 
218                     try {
219                         if (mCallback != null) {
220                             mCallback.onSetProtocol(mHidDevice, protocol);
221                         }
222                     } catch (RemoteException e) {
223                         e.printStackTrace();
224                     }
225                     break;
226 
227                 case MESSAGE_INTR_DATA:
228                     byte reportId = (byte) msg.arg1;
229                     byte[] data = ((ByteBuffer) msg.obj).array();
230 
231                     try {
232                         if (mCallback != null) {
233                             mCallback.onInterruptData(mHidDevice, reportId, data);
234                         }
235                     } catch (RemoteException e) {
236                         e.printStackTrace();
237                     }
238                     break;
239 
240                 case MESSAGE_VC_UNPLUG:
241                     try {
242                         if (mCallback != null) {
243                             mCallback.onVirtualCableUnplug(mHidDevice);
244                         }
245                     } catch (RemoteException e) {
246                         e.printStackTrace();
247                     }
248                     mHidDevice = null;
249                     break;
250 
251                 case MESSAGE_IMPORTANCE_CHANGE:
252                     int importance = msg.arg1;
253                     int uid = msg.arg2;
254                     if (importance > FOREGROUND_IMPORTANCE_CUTOFF
255                             && uid >= Process.FIRST_APPLICATION_UID) {
256                         unregisterAppUid(uid);
257                     }
258                     break;
259             }
260         }
261     }
262 
263     private static class BluetoothHidDeviceDeathRecipient implements IBinder.DeathRecipient {
264         private HidDeviceService mService;
265 
BluetoothHidDeviceDeathRecipient(HidDeviceService service)266         BluetoothHidDeviceDeathRecipient(HidDeviceService service) {
267             mService = service;
268         }
269 
270         @Override
binderDied()271         public void binderDied() {
272             Log.w(TAG, "Binder died, need to unregister app :(");
273             mService.unregisterApp();
274         }
275 
cleanup()276         public void cleanup() {
277             mService.unregisterApp();
278             mService = null;
279         }
280     }
281 
282     private ActivityManager.OnUidImportanceListener mUidImportanceListener =
283             new ActivityManager.OnUidImportanceListener() {
284                 @Override
285                 public void onUidImportance(final int uid, final int importance) {
286                     Message message = mHandler.obtainMessage(MESSAGE_IMPORTANCE_CHANGE);
287                     message.arg1 = importance;
288                     message.arg2 = uid;
289                     mHandler.sendMessage(message);
290                 }
291             };
292 
293     @VisibleForTesting
294     static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub
295             implements IProfileServiceBinder {
296 
297         private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName();
298 
299         private HidDeviceService mService;
300 
BluetoothHidDeviceBinder(HidDeviceService service)301         BluetoothHidDeviceBinder(HidDeviceService service) {
302             mService = service;
303         }
304 
305         @VisibleForTesting
getServiceForTesting()306         HidDeviceService getServiceForTesting() {
307             if (mService != null && mService.isAvailable()) {
308                 return mService;
309             }
310             return null;
311         }
312 
313         @Override
cleanup()314         public void cleanup() {
315             mService = null;
316         }
317 
318         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)319         private HidDeviceService getService(AttributionSource source) {
320             if (Utils.isInstrumentationTestMode()) {
321                 return mService;
322             }
323             if (!Utils.checkServiceAvailable(mService, TAG)
324                     || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)
325                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
326                 return null;
327             }
328             return mService;
329         }
330 
331         @Override
registerApp( BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, IBluetoothHidDeviceCallback callback, AttributionSource source)332         public boolean registerApp(
333                 BluetoothHidDeviceAppSdpSettings sdp,
334                 BluetoothHidDeviceAppQosSettings inQos,
335                 BluetoothHidDeviceAppQosSettings outQos,
336                 IBluetoothHidDeviceCallback callback,
337                 AttributionSource source) {
338             Log.d(TAG, "registerApp()");
339 
340             HidDeviceService service = getService(source);
341             if (service == null) {
342                 return false;
343             }
344 
345             return service.registerApp(sdp, inQos, outQos, callback);
346         }
347 
348         @Override
unregisterApp(AttributionSource source)349         public boolean unregisterApp(AttributionSource source) {
350             Log.d(TAG, "unregisterApp()");
351 
352             HidDeviceService service = getService(source);
353             if (service == null) {
354                 return false;
355             }
356 
357             return service.unregisterApp();
358         }
359 
360         @Override
sendReport( BluetoothDevice device, int id, byte[] data, AttributionSource source)361         public boolean sendReport(
362                 BluetoothDevice device, int id, byte[] data, AttributionSource source) {
363             Log.d(TAG, "sendReport(): device=" + device + "  id=" + id);
364 
365             HidDeviceService service = getService(source);
366             if (service == null) {
367                 return false;
368             }
369 
370             return service.sendReport(device, id, data);
371         }
372 
373         @Override
replyReport( BluetoothDevice device, byte type, byte id, byte[] data, AttributionSource source)374         public boolean replyReport(
375                 BluetoothDevice device, byte type, byte id, byte[] data, AttributionSource source) {
376             Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
377 
378             HidDeviceService service = getService(source);
379             if (service == null) {
380                 return false;
381             }
382 
383             return service.replyReport(device, type, id, data);
384         }
385 
386         @Override
unplug(BluetoothDevice device, AttributionSource source)387         public boolean unplug(BluetoothDevice device, AttributionSource source) {
388             Log.d(TAG, "unplug(): device=" + device);
389 
390             HidDeviceService service = getService(source);
391             if (service == null) {
392                 return false;
393             }
394 
395             return service.unplug(device);
396         }
397 
398         @Override
connect(BluetoothDevice device, AttributionSource source)399         public boolean connect(BluetoothDevice device, AttributionSource source) {
400             Log.d(TAG, "connect(): device=" + device);
401 
402             HidDeviceService service = getService(source);
403             if (service == null) {
404                 return false;
405             }
406 
407             return service.connect(device);
408         }
409 
410         @Override
disconnect(BluetoothDevice device, AttributionSource source)411         public boolean disconnect(BluetoothDevice device, AttributionSource source) {
412             Log.d(TAG, "disconnect(): device=" + device);
413 
414             HidDeviceService service = getService(source);
415             if (service == null) {
416                 return false;
417             }
418 
419             return service.disconnect(device);
420         }
421 
422         @Override
setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)423         public boolean setConnectionPolicy(
424                 BluetoothDevice device, int connectionPolicy, AttributionSource source) {
425             Log.d(
426                     TAG,
427                     "setConnectionPolicy():"
428                             + (" device=" + device)
429                             + (" connectionPolicy=" + connectionPolicy));
430 
431             HidDeviceService service = getService(source);
432             if (service == null) {
433                 return false;
434             }
435 
436             return service.setConnectionPolicy(device, connectionPolicy);
437         }
438 
439         @Override
reportError(BluetoothDevice device, byte error, AttributionSource source)440         public boolean reportError(BluetoothDevice device, byte error, AttributionSource source) {
441             Log.d(TAG, "reportError(): device=" + device + " error=" + error);
442 
443             HidDeviceService service = getService(source);
444             if (service == null) {
445                 return false;
446             }
447 
448             return service.reportError(device, error);
449         }
450 
451         @Override
getConnectionState(BluetoothDevice device, AttributionSource source)452         public int getConnectionState(BluetoothDevice device, AttributionSource source) {
453             Log.d(TAG, "getConnectionState(): device=" + device);
454 
455             HidDeviceService service = getService(source);
456             if (service == null) {
457                 return BluetoothHidDevice.STATE_DISCONNECTED;
458             }
459 
460             return service.getConnectionState(device);
461         }
462 
463         @Override
getConnectedDevices(AttributionSource source)464         public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
465             Log.d(TAG, "getConnectedDevices()");
466 
467             return getDevicesMatchingConnectionStates(
468                     new int[] {BluetoothProfile.STATE_CONNECTED}, source);
469         }
470 
471         @Override
getDevicesMatchingConnectionStates( int[] states, AttributionSource source)472         public List<BluetoothDevice> getDevicesMatchingConnectionStates(
473                 int[] states, AttributionSource source) {
474             Log.d(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
475 
476             HidDeviceService service = getService(source);
477             if (service == null) {
478                 return Collections.emptyList();
479             }
480 
481             return service.getDevicesMatchingConnectionStates(states);
482         }
483 
484         @Override
getUserAppName(AttributionSource source)485         public String getUserAppName(AttributionSource source) {
486             HidDeviceService service = getService(source);
487             if (service == null) {
488                 return "";
489             }
490             return service.getUserAppName();
491         }
492     }
493 
494     @Override
initBinder()495     protected IProfileServiceBinder initBinder() {
496         return new BluetoothHidDeviceBinder(this);
497     }
498 
checkDevice(BluetoothDevice device)499     private boolean checkDevice(BluetoothDevice device) {
500         if (mHidDevice == null || !mHidDevice.equals(device)) {
501             Log.w(TAG, "Unknown device: " + device);
502             return false;
503         }
504         return true;
505     }
506 
checkCallingUid()507     private boolean checkCallingUid() {
508         int callingUid = Binder.getCallingUid();
509         if (callingUid != mUserUid) {
510             Log.w(TAG, "checkCallingUid(): caller UID doesn't match registered user UID");
511             return false;
512         }
513         return true;
514     }
515 
registerApp( BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, IBluetoothHidDeviceCallback callback)516     synchronized boolean registerApp(
517             BluetoothHidDeviceAppSdpSettings sdp,
518             BluetoothHidDeviceAppQosSettings inQos,
519             BluetoothHidDeviceAppQosSettings outQos,
520             IBluetoothHidDeviceCallback callback) {
521         if (mUserUid != 0) {
522             Log.w(TAG, "registerApp(): failed because another app is registered");
523             return false;
524         }
525 
526         int callingUid = Binder.getCallingUid();
527         Log.d(TAG, "registerApp(): calling uid=" + callingUid);
528         if (callingUid >= Process.FIRST_APPLICATION_UID
529                 && mActivityManager.getUidImportance(callingUid) > FOREGROUND_IMPORTANCE_CUTOFF) {
530             Log.w(TAG, "registerApp(): failed because the app is not foreground");
531             return false;
532         }
533         mUserUid = callingUid;
534         mCallback = callback;
535 
536         return mHidDeviceNativeInterface.registerApp(
537                 sdp.getName(),
538                 sdp.getDescription(),
539                 sdp.getProvider(),
540                 sdp.getSubclass(),
541                 sdp.getDescriptors(),
542                 inQos == null
543                         ? null
544                         : new int[] {
545                             inQos.getServiceType(),
546                             inQos.getTokenRate(),
547                             inQos.getTokenBucketSize(),
548                             inQos.getPeakBandwidth(),
549                             inQos.getLatency(),
550                             inQos.getDelayVariation()
551                         },
552                 outQos == null
553                         ? null
554                         : new int[] {
555                             outQos.getServiceType(),
556                             outQos.getTokenRate(),
557                             outQos.getTokenBucketSize(),
558                             outQos.getPeakBandwidth(),
559                             outQos.getLatency(),
560                             outQos.getDelayVariation()
561                         });
562     }
563 
unregisterApp()564     synchronized boolean unregisterApp() {
565         Log.d(TAG, "unregisterApp()");
566 
567         int callingUid = Binder.getCallingUid();
568         return unregisterAppUid(callingUid);
569     }
570 
unregisterAppUid(int uid)571     private synchronized boolean unregisterAppUid(int uid) {
572         Log.d(TAG, "unregisterAppUid(): uid=" + uid);
573 
574         if (mUserUid != 0 && (uid == mUserUid || uid < Process.FIRST_APPLICATION_UID)) {
575             mUserUid = 0;
576             return mHidDeviceNativeInterface.unregisterApp();
577         }
578         Log.d(TAG, "unregisterAppUid(): caller UID doesn't match user UID");
579         return false;
580     }
581 
sendReport(BluetoothDevice device, int id, byte[] data)582     synchronized boolean sendReport(BluetoothDevice device, int id, byte[] data) {
583         Log.d(TAG, "sendReport(): device=" + device + " id=" + id);
584 
585         return checkDevice(device)
586                 && checkCallingUid()
587                 && mHidDeviceNativeInterface.sendReport(id, data);
588     }
589 
replyReport(BluetoothDevice device, byte type, byte id, byte[] data)590     synchronized boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
591         Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
592 
593         return checkDevice(device)
594                 && checkCallingUid()
595                 && mHidDeviceNativeInterface.replyReport(type, id, data);
596     }
597 
unplug(BluetoothDevice device)598     synchronized boolean unplug(BluetoothDevice device) {
599         Log.d(TAG, "unplug(): device=" + device);
600 
601         return checkDevice(device) && checkCallingUid() && mHidDeviceNativeInterface.unplug();
602     }
603 
604     /**
605      * Connects the Hid device profile for the remote bluetooth device
606      *
607      * @param device is the device with which we would like to connect the hid device profile
608      * @return true if the connection is successful, false otherwise
609      */
connect(BluetoothDevice device)610     public synchronized boolean connect(BluetoothDevice device) {
611         Log.d(TAG, "connect(): device=" + device);
612 
613         return checkCallingUid() && mHidDeviceNativeInterface.connect(device);
614     }
615 
616     /**
617      * Disconnects the hid device profile for the remote bluetooth device
618      *
619      * @param device is the device with which we would like to disconnect the hid device profile
620      * @return true if the disconnection is successful, false otherwise
621      */
disconnect(BluetoothDevice device)622     public synchronized boolean disconnect(BluetoothDevice device) {
623         Log.d(TAG, "disconnect(): device=" + device);
624 
625         int callingUid = Binder.getCallingUid();
626         if (callingUid != mUserUid && callingUid >= Process.FIRST_APPLICATION_UID) {
627             Log.w(TAG, "disconnect(): caller UID doesn't match user UID");
628             return false;
629         }
630         return checkDevice(device) && mHidDeviceNativeInterface.disconnect();
631     }
632 
633     /**
634      * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}
635      * and disconnects Hid device if connectionPolicy is {@link
636      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}.
637      *
638      * <p>The device should already be paired. Connection policy can be one of: {@link
639      * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link
640      * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
641      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
642      *
643      * @param device Paired bluetooth device
644      * @param connectionPolicy determines whether hid device should be connected or disconnected
645      * @return true if hid device is connected or disconnected, false otherwise
646      */
647     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)648     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
649         enforceCallingOrSelfPermission(
650                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
651         Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
652 
653         if (!mDatabaseManager.setProfileConnectionPolicy(
654                 device, BluetoothProfile.HID_DEVICE, connectionPolicy)) {
655             return false;
656         }
657         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
658             disconnect(device);
659         }
660         return true;
661     }
662 
663     /**
664      * Get the connection policy of the profile.
665      *
666      * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
667      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link
668      * BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
669      *
670      * @param device Bluetooth device
671      * @return connection policy of the device
672      */
673     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getConnectionPolicy(BluetoothDevice device)674     public int getConnectionPolicy(BluetoothDevice device) {
675         if (device == null) {
676             throw new IllegalArgumentException("Null device");
677         }
678         enforceCallingOrSelfPermission(
679                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
680         return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HID_DEVICE);
681     }
682 
reportError(BluetoothDevice device, byte error)683     synchronized boolean reportError(BluetoothDevice device, byte error) {
684         Log.d(TAG, "reportError(): device=" + device + " error=" + error);
685 
686         return checkDevice(device)
687                 && checkCallingUid()
688                 && mHidDeviceNativeInterface.reportError(error);
689     }
690 
getUserAppName()691     synchronized String getUserAppName() {
692         if (mUserUid < Process.FIRST_APPLICATION_UID) {
693             return "";
694         }
695         String appName = getPackageManager().getNameForUid(mUserUid);
696         return appName != null ? appName : "";
697     }
698 
699     @Override
start()700     public void start() {
701         Log.d(TAG, "start()");
702 
703         mDatabaseManager =
704                 Objects.requireNonNull(
705                         AdapterService.getAdapterService().getDatabase(),
706                         "DatabaseManager cannot be null when HidDeviceService starts");
707 
708         mHandler = new HidDeviceServiceHandler(Looper.getMainLooper());
709         mHidDeviceNativeInterface = HidDeviceNativeInterface.getInstance();
710         mHidDeviceNativeInterface.init();
711         mNativeAvailable = true;
712         mActivityManager = getSystemService(ActivityManager.class);
713         mActivityManager.addOnUidImportanceListener(
714                 mUidImportanceListener, FOREGROUND_IMPORTANCE_CUTOFF);
715         setHidDeviceService(this);
716     }
717 
718     @Override
stop()719     public void stop() {
720         Log.d(TAG, "stop()");
721 
722         if (sHidDeviceService == null) {
723             Log.w(TAG, "stop() called before start()");
724             return;
725         }
726 
727         setHidDeviceService(null);
728         if (mNativeAvailable) {
729             mHidDeviceNativeInterface.cleanup();
730             mNativeAvailable = false;
731         }
732         mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
733     }
734 
735     /**
736      * Get the HID Device Service instance
737      *
738      * @return HID Device Service instance
739      */
getHidDeviceService()740     public static synchronized HidDeviceService getHidDeviceService() {
741         if (sHidDeviceService == null) {
742             Log.d(TAG, "getHidDeviceService(): service is NULL");
743             return null;
744         }
745         if (!sHidDeviceService.isAvailable()) {
746             Log.d(TAG, "getHidDeviceService(): service is not available");
747             return null;
748         }
749         return sHidDeviceService;
750     }
751 
752     @VisibleForTesting
setHidDeviceService(HidDeviceService instance)753     static synchronized void setHidDeviceService(HidDeviceService instance) {
754         Log.d(TAG, "setHidDeviceService(): set to: " + instance);
755         sHidDeviceService = instance;
756     }
757 
758     /**
759      * Gets the connections state for the hid device profile for the passed in device
760      *
761      * @param device is the device whose conenction state we want to verify
762      * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED}, {@link
763      *     BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or {@link
764      *     BluetoothProfile#STATE_DISCONNECTING}
765      */
getConnectionState(BluetoothDevice device)766     public int getConnectionState(BluetoothDevice device) {
767         if (mHidDevice != null && mHidDevice.equals(device)) {
768             return mHidDeviceState;
769         }
770         return BluetoothHidDevice.STATE_DISCONNECTED;
771     }
772 
getDevicesMatchingConnectionStates(int[] states)773     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
774         List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
775 
776         if (mHidDevice != null) {
777             for (int state : states) {
778                 if (state == mHidDeviceState) {
779                     inputDevices.add(mHidDevice);
780                     break;
781                 }
782             }
783         }
784         return inputDevices;
785     }
786 
onApplicationStateChangedFromNative( BluetoothDevice device, boolean registered)787     synchronized void onApplicationStateChangedFromNative(
788             BluetoothDevice device, boolean registered) {
789         Log.d(TAG, "onApplicationStateChanged(): registered=" + registered);
790 
791         Message msg = mHandler.obtainMessage(MESSAGE_APPLICATION_STATE_CHANGED);
792         msg.obj = device;
793         msg.arg1 = registered ? 1 : 0;
794         mHandler.sendMessage(msg);
795     }
796 
onConnectStateChangedFromNative(BluetoothDevice device, int state)797     synchronized void onConnectStateChangedFromNative(BluetoothDevice device, int state) {
798         Log.d(TAG, "onConnectStateChanged(): device=" + device + " state=" + state);
799 
800         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
801         msg.obj = device;
802         msg.arg1 = state;
803         mHandler.sendMessage(msg);
804     }
805 
onGetReportFromNative(byte type, byte id, short bufferSize)806     synchronized void onGetReportFromNative(byte type, byte id, short bufferSize) {
807         Log.d(TAG, "onGetReport(): type=" + type + " id=" + id + " bufferSize=" + bufferSize);
808 
809         Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
810         msg.obj = bufferSize > 0 ? Integer.valueOf(bufferSize) : null;
811         msg.arg1 = type;
812         msg.arg2 = id;
813         mHandler.sendMessage(msg);
814     }
815 
onSetReportFromNative(byte reportType, byte reportId, byte[] data)816     synchronized void onSetReportFromNative(byte reportType, byte reportId, byte[] data) {
817         Log.d(TAG, "onSetReport(): reportType=" + reportType + " reportId=" + reportId);
818 
819         ByteBuffer bb = ByteBuffer.wrap(data);
820 
821         Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
822         msg.arg1 = reportType;
823         msg.arg2 = reportId;
824         msg.obj = bb;
825         mHandler.sendMessage(msg);
826     }
827 
onSetProtocolFromNative(byte protocol)828     synchronized void onSetProtocolFromNative(byte protocol) {
829         Log.d(TAG, "onSetProtocol(): protocol=" + protocol);
830 
831         Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL);
832         msg.arg1 = protocol;
833         mHandler.sendMessage(msg);
834     }
835 
onInterruptDataFromNative(byte reportId, byte[] data)836     synchronized void onInterruptDataFromNative(byte reportId, byte[] data) {
837         Log.d(TAG, "onInterruptData(): reportId=" + reportId);
838 
839         ByteBuffer bb = ByteBuffer.wrap(data);
840 
841         Message msg = mHandler.obtainMessage(MESSAGE_INTR_DATA);
842         msg.arg1 = reportId;
843         msg.obj = bb;
844         mHandler.sendMessage(msg);
845     }
846 
onVirtualCableUnplugFromNative()847     synchronized void onVirtualCableUnplugFromNative() {
848         Log.d(TAG, "onVirtualCableUnplug()");
849 
850         Message msg = mHandler.obtainMessage(MESSAGE_VC_UNPLUG);
851         mHandler.sendMessage(msg);
852     }
853 
setAndBroadcastConnectionState(BluetoothDevice device, int newState)854     private void setAndBroadcastConnectionState(BluetoothDevice device, int newState) {
855         Log.d(
856                 TAG,
857                 "setAndBroadcastConnectionState(): device="
858                         + device
859                         + " oldState="
860                         + mHidDeviceState
861                         + " newState="
862                         + newState);
863 
864         if (mHidDevice != null && !mHidDevice.equals(device)) {
865             Log.w(TAG, "Connection state changed for unknown device, ignoring");
866             return;
867         }
868 
869         int prevState = mHidDeviceState;
870         mHidDeviceState = newState;
871 
872         if (prevState == newState) {
873             Log.w(TAG, "Connection state is unchanged, ignoring");
874             return;
875         }
876 
877         AdapterService adapterService = AdapterService.getAdapterService();
878         if (adapterService != null) {
879             adapterService.updateProfileConnectionAdapterProperties(
880                     device, BluetoothProfile.HID_DEVICE, newState, prevState);
881         }
882 
883         if (newState == BluetoothProfile.STATE_CONNECTED) {
884             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_DEVICE);
885         }
886 
887         Intent intent = new Intent(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
888         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
889         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
890         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
891         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
892         sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
893     }
894 
convertHalState(int halState)895     private static int convertHalState(int halState) {
896         switch (halState) {
897             case HAL_CONN_STATE_CONNECTED:
898                 return BluetoothProfile.STATE_CONNECTED;
899             case HAL_CONN_STATE_CONNECTING:
900                 return BluetoothProfile.STATE_CONNECTING;
901             case HAL_CONN_STATE_DISCONNECTED:
902                 return BluetoothProfile.STATE_DISCONNECTED;
903             case HAL_CONN_STATE_DISCONNECTING:
904                 return BluetoothProfile.STATE_DISCONNECTING;
905             default:
906                 return BluetoothProfile.STATE_DISCONNECTED;
907         }
908     }
909 
910     static final int HAL_CONN_STATE_CONNECTED = 0;
911     static final int HAL_CONN_STATE_CONNECTING = 1;
912     static final int HAL_CONN_STATE_DISCONNECTED = 2;
913     static final int HAL_CONN_STATE_DISCONNECTING = 3;
914 }
915