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