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 android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothHidDeviceAppConfiguration;
21 import android.bluetooth.BluetoothHidDeviceAppQosSettings;
22 import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
23 import android.bluetooth.BluetoothInputHost;
24 import android.bluetooth.BluetoothProfile;
25 import android.bluetooth.IBluetoothHidDeviceCallback;
26 import android.bluetooth.IBluetoothInputHost;
27 import android.content.Intent;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.util.Log;
33 
34 import com.android.bluetooth.Utils;
35 import com.android.bluetooth.btservice.ProfileService;
36 
37 import java.nio.ByteBuffer;
38 import java.util.Arrays;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.NoSuchElementException;
42 
43 /** @hide */
44 public class HidDevService extends ProfileService {
45     private static final boolean DBG = false;
46 
47     private static final String TAG = HidDevService.class.getSimpleName();
48 
49     private static final int MESSAGE_APPLICATION_STATE_CHANGED = 1;
50     private static final int MESSAGE_CONNECT_STATE_CHANGED = 2;
51     private static final int MESSAGE_GET_REPORT = 3;
52     private static final int MESSAGE_SET_REPORT = 4;
53     private static final int MESSAGE_SET_PROTOCOL = 5;
54     private static final int MESSAGE_INTR_DATA = 6;
55     private static final int MESSAGE_VC_UNPLUG = 7;
56 
57     private boolean mNativeAvailable = false;
58 
59     private BluetoothDevice mHidDevice = null;
60 
61     private int mHidDeviceState = BluetoothInputHost.STATE_DISCONNECTED;
62 
63     private BluetoothHidDeviceAppConfiguration mAppConfig = null;
64 
65     private IBluetoothHidDeviceCallback mCallback = null;
66 
67     private BluetoothHidDeviceDeathRecipient mDeathRcpt;
68 
69     static {
classInitNative()70         classInitNative();
71     }
72 
73     private final Handler mHandler = new Handler() {
74 
75         @Override
76         public void handleMessage(Message msg) {
77             if (DBG) Log.v(TAG, "handleMessage(): msg.what=" + msg.what);
78 
79             switch (msg.what) {
80                 case MESSAGE_APPLICATION_STATE_CHANGED: {
81                     BluetoothDevice device = msg.obj != null ? getDevice((byte[]) msg.obj) : null;
82                     boolean success = (msg.arg1 != 0);
83 
84                     if (success) {
85                         mHidDevice = device;
86                     } else {
87                         mHidDevice = null;
88                     }
89 
90                     try {
91                         if (mCallback != null)
92                             mCallback.onAppStatusChanged(device, mAppConfig, success);
93                         else
94                             break;
95                     } catch (RemoteException e) {
96                         Log.e(TAG, "e=" + e.toString());
97                         e.printStackTrace();
98                     }
99 
100                     if (success) {
101                         mDeathRcpt = new BluetoothHidDeviceDeathRecipient(
102                                 HidDevService.this, mAppConfig);
103                         if (mCallback != null) {
104                             IBinder binder = mCallback.asBinder();
105                             try {
106                                 binder.linkToDeath(mDeathRcpt, 0);
107                                 Log.i(TAG, "IBinder.linkToDeath() ok");
108                             } catch (RemoteException e) {
109                                 e.printStackTrace();
110                             }
111                         }
112                     } else if (mDeathRcpt != null) {
113                         if (mCallback != null) {
114                             IBinder binder = mCallback.asBinder();
115                             try {
116                                 binder.unlinkToDeath(mDeathRcpt, 0);
117                                 Log.i(TAG, "IBinder.unlinkToDeath() ok");
118                             } catch (NoSuchElementException e) {
119                                 e.printStackTrace();
120                             }
121                             mDeathRcpt.cleanup();
122                             mDeathRcpt = null;
123                         }
124                     }
125 
126                     if (!success) {
127                         mAppConfig = null;
128                         mCallback = null;
129                     }
130 
131                     break;
132                 }
133 
134                 case MESSAGE_CONNECT_STATE_CHANGED: {
135                     BluetoothDevice device = getDevice((byte[]) msg.obj);
136                     int halState = msg.arg1;
137                     int state = convertHalState(halState);
138 
139                     if (state != BluetoothInputHost.STATE_DISCONNECTED) {
140                         mHidDevice = device;
141                     }
142 
143                     broadcastConnectionState(device, state);
144 
145                     try {
146                         if (mCallback != null) mCallback.onConnectionStateChanged(device, state);
147                     } catch (RemoteException e) {
148                         e.printStackTrace();
149                     }
150                     break;
151                 }
152 
153                 case MESSAGE_GET_REPORT:
154                     byte type = (byte) msg.arg1;
155                     byte id = (byte) msg.arg2;
156                     int bufferSize = msg.obj == null ? 0 : ((Integer) msg.obj).intValue();
157 
158                     try {
159                         if (mCallback != null)
160                             mCallback.onGetReport(mHidDevice, type, id, bufferSize);
161                     } catch (RemoteException e) {
162                         e.printStackTrace();
163                     }
164                     break;
165 
166                 case MESSAGE_SET_REPORT: {
167                     byte reportType = (byte) msg.arg1;
168                     byte reportId = (byte) msg.arg2;
169                     byte[] data = ((ByteBuffer) msg.obj).array();
170 
171                     try {
172                         if (mCallback != null)
173                             mCallback.onSetReport(mHidDevice, reportType, reportId, data);
174                     } catch (RemoteException e) {
175                         e.printStackTrace();
176                     }
177                     break;
178                 }
179 
180                 case MESSAGE_SET_PROTOCOL:
181                     byte protocol = (byte) msg.arg1;
182 
183                     try {
184                         if (mCallback != null) mCallback.onSetProtocol(mHidDevice, protocol);
185                     } catch (RemoteException e) {
186                         e.printStackTrace();
187                     }
188                     break;
189 
190                 case MESSAGE_INTR_DATA:
191                     byte reportId = (byte) msg.arg1;
192                     byte[] data = ((ByteBuffer) msg.obj).array();
193 
194                     try {
195                         if (mCallback != null) mCallback.onIntrData(mHidDevice, reportId, data);
196                     } catch (RemoteException e) {
197                         e.printStackTrace();
198                     }
199                     break;
200 
201                 case MESSAGE_VC_UNPLUG:
202                     try {
203                         if (mCallback != null) mCallback.onVirtualCableUnplug(mHidDevice);
204                     } catch (RemoteException e) {
205                         e.printStackTrace();
206                     }
207                     mHidDevice = null;
208                     break;
209             }
210         }
211     };
212 
213     private static class BluetoothHidDeviceDeathRecipient implements IBinder.DeathRecipient {
214         private HidDevService mService;
215         private BluetoothHidDeviceAppConfiguration mAppConfig;
216 
BluetoothHidDeviceDeathRecipient( HidDevService service, BluetoothHidDeviceAppConfiguration config)217         public BluetoothHidDeviceDeathRecipient(
218                 HidDevService service, BluetoothHidDeviceAppConfiguration config) {
219             mService = service;
220             mAppConfig = config;
221         }
222 
223         @Override
binderDied()224         public void binderDied() {
225             Log.w(TAG, "Binder died, need to unregister app :(");
226             mService.unregisterApp(mAppConfig);
227         }
228 
cleanup()229         public void cleanup() {
230             mService = null;
231             mAppConfig = null;
232         }
233   }
234 
235   private static class BluetoothHidDeviceBinder
236       extends IBluetoothInputHost.Stub implements IProfileServiceBinder {
237 
238     private static final String TAG =
239         BluetoothHidDeviceBinder.class.getSimpleName();
240 
241     private HidDevService mService;
242 
BluetoothHidDeviceBinder(HidDevService service)243     public BluetoothHidDeviceBinder(HidDevService service) {
244       mService = service;
245     }
246 
247     @Override
cleanup()248     public boolean cleanup() {
249       mService = null;
250       return true;
251     }
252 
getService()253     private HidDevService getService() {
254       if (!Utils.checkCaller()) {
255         Log.w(TAG, "HidDevice call not allowed for non-active user");
256         return null;
257       }
258 
259       if (mService != null && mService.isAvailable()) {
260         return mService;
261       }
262 
263       return null;
264     }
265 
266     @Override
registerApp(BluetoothHidDeviceAppConfiguration config, BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, IBluetoothHidDeviceCallback callback)267     public boolean registerApp(BluetoothHidDeviceAppConfiguration config,
268                                BluetoothHidDeviceAppSdpSettings sdp,
269                                BluetoothHidDeviceAppQosSettings inQos,
270                                BluetoothHidDeviceAppQosSettings outQos,
271                                IBluetoothHidDeviceCallback callback) {
272       if (DBG)
273         Log.v(TAG, "registerApp()");
274 
275       HidDevService service = getService();
276       if (service == null) {
277         return false;
278       }
279 
280       return service.registerApp(config, sdp, inQos, outQos, callback);
281     }
282 
283     @Override
unregisterApp(BluetoothHidDeviceAppConfiguration config)284     public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) {
285       if (DBG)
286         Log.v(TAG, "unregisterApp()");
287 
288       HidDevService service = getService();
289       if (service == null) {
290         return false;
291       }
292 
293       return service.unregisterApp(config);
294     }
295 
296     @Override
sendReport(BluetoothDevice device, int id, byte[] data)297     public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
298         if (DBG) Log.v(TAG, "sendReport(): device=" + device + "  id=" + id);
299 
300         HidDevService service = getService();
301         if (service == null) {
302             return false;
303         }
304 
305         return service.sendReport(device, id, data);
306     }
307 
308     @Override
replyReport(BluetoothDevice device, byte type, byte id, byte[] data)309     public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
310         if (DBG) Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
311 
312         HidDevService service = getService();
313         if (service == null) {
314             return false;
315         }
316 
317         return service.replyReport(device, type, id, data);
318     }
319 
320     @Override
unplug(BluetoothDevice device)321     public boolean unplug(BluetoothDevice device) {
322         if (DBG) Log.v(TAG, "unplug(): device=" + device);
323 
324         HidDevService service = getService();
325         if (service == null) {
326             return false;
327         }
328 
329         return service.unplug(device);
330     }
331 
332     @Override
connect(BluetoothDevice device)333     public boolean connect(BluetoothDevice device) {
334         if (DBG) Log.v(TAG, "connect(): device=" + device);
335 
336         HidDevService service = getService();
337         if (service == null) {
338             return false;
339         }
340 
341         return service.connect(device);
342     }
343 
344     @Override
disconnect(BluetoothDevice device)345     public boolean disconnect(BluetoothDevice device) {
346         if (DBG) Log.v(TAG, "disconnect(): device=" + device);
347 
348         HidDevService service = getService();
349         if (service == null) {
350             return false;
351         }
352 
353         return service.disconnect(device);
354     }
355 
356     @Override
reportError(BluetoothDevice device, byte error)357     public boolean reportError(BluetoothDevice device, byte error) {
358         if (DBG) Log.v(TAG, "reportError(): device=" + device + " error=" + error);
359 
360         HidDevService service = getService();
361         if (service == null) {
362             return false;
363         }
364 
365         return service.reportError(device, error);
366     }
367 
368     @Override
getConnectionState(BluetoothDevice device)369     public int getConnectionState(BluetoothDevice device) {
370         if (DBG) Log.v(TAG, "getConnectionState(): device=" + device);
371 
372         HidDevService service = getService();
373         if (service == null) {
374             return BluetoothInputHost.STATE_DISCONNECTED;
375         }
376 
377         return service.getConnectionState(device);
378     }
379 
380     @Override
getConnectedDevices()381     public List<BluetoothDevice> getConnectedDevices() {
382         if (DBG) Log.v(TAG, "getConnectedDevices()");
383 
384         return getDevicesMatchingConnectionStates(new int[] {BluetoothProfile.STATE_CONNECTED});
385     }
386 
387     @Override
getDevicesMatchingConnectionStates(int[] states)388     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
389         if (DBG)
390             Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
391 
392         HidDevService service = getService();
393         if (service == null) {
394             return new ArrayList<BluetoothDevice>(0);
395         }
396 
397         return service.getDevicesMatchingConnectionStates(states);
398     }
399   }
400 
401   @Override
initBinder()402   protected IProfileServiceBinder initBinder() {
403     return new BluetoothHidDeviceBinder(this);
404   }
405 
checkDevice(BluetoothDevice device)406   private boolean checkDevice(BluetoothDevice device) {
407       if (mHidDevice == null || !mHidDevice.equals(device)) {
408           Log.w(TAG, "Unknown device: " + device);
409           return false;
410       }
411       return true;
412   }
413 
registerApp(BluetoothHidDeviceAppConfiguration config, BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, IBluetoothHidDeviceCallback callback)414   synchronized boolean registerApp(BluetoothHidDeviceAppConfiguration config,
415                                    BluetoothHidDeviceAppSdpSettings sdp,
416                                    BluetoothHidDeviceAppQosSettings inQos,
417                                    BluetoothHidDeviceAppQosSettings outQos,
418                                    IBluetoothHidDeviceCallback callback) {
419     if (DBG)
420       Log.v(TAG, "registerApp()");
421 
422     if (mAppConfig != null) {
423       return false;
424     }
425 
426     mAppConfig = config;
427     mCallback = callback;
428 
429     return registerAppNative(sdp.name, sdp.description, sdp.provider,
430                              sdp.subclass, sdp.descriptors,
431                              inQos == null ? null : inQos.toArray(),
432                              outQos == null ? null : outQos.toArray());
433   }
434 
435   synchronized boolean
unregisterApp(BluetoothHidDeviceAppConfiguration config)436   unregisterApp(BluetoothHidDeviceAppConfiguration config) {
437     if (DBG)
438       Log.v(TAG, "unregisterApp()");
439 
440     if (mAppConfig == null || config == null || !config.equals(mAppConfig)) {
441       return false;
442     }
443 
444     return unregisterAppNative();
445   }
446 
sendReport(BluetoothDevice device, int id, byte[] data)447   synchronized boolean sendReport(BluetoothDevice device, int id, byte[] data) {
448       if (DBG) Log.v(TAG, "sendReport(): device=" + device + " id=" + id);
449 
450       if (!checkDevice(device)) {
451           return false;
452       }
453 
454       return sendReportNative(id, data);
455   }
456 
replyReport(BluetoothDevice device, byte type, byte id, byte[] data)457   synchronized boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
458       if (DBG) Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
459 
460       if (!checkDevice(device)) {
461           return false;
462       }
463 
464       return replyReportNative(type, id, data);
465   }
466 
unplug(BluetoothDevice device)467   synchronized boolean unplug(BluetoothDevice device) {
468       if (DBG) Log.v(TAG, "unplug(): device=" + device);
469 
470       if (!checkDevice(device)) {
471           return false;
472       }
473 
474       return unplugNative();
475   }
476 
connect(BluetoothDevice device)477   synchronized boolean connect(BluetoothDevice device) {
478       if (DBG) Log.v(TAG, "connect(): device=" + device);
479 
480       return connectNative(Utils.getByteAddress(device));
481   }
482 
disconnect(BluetoothDevice device)483   synchronized boolean disconnect(BluetoothDevice device) {
484       if (DBG) Log.v(TAG, "disconnect(): device=" + device);
485 
486       if (!checkDevice(device)) {
487           return false;
488       }
489 
490       return disconnectNative();
491   }
492 
reportError(BluetoothDevice device, byte error)493   synchronized boolean reportError(BluetoothDevice device, byte error) {
494       if (DBG) Log.v(TAG, "reportError(): device=" + device + " error=" + error);
495 
496       if (!checkDevice(device)) {
497           return false;
498       }
499 
500       return reportErrorNative(error);
501   }
502 
503   @Override
start()504   protected boolean start() {
505     if (DBG)
506       Log.d(TAG, "start()");
507 
508     initNative();
509     mNativeAvailable = true;
510 
511     return true;
512   }
513 
514   @Override
stop()515   protected boolean stop() {
516     if (DBG)
517       Log.d(TAG, "stop()");
518 
519     return true;
520   }
521 
522   @Override
cleanup()523   protected boolean cleanup() {
524     if (DBG)
525       Log.d(TAG, "cleanup()");
526 
527     if (mNativeAvailable) {
528       cleanupNative();
529       mNativeAvailable = false;
530     }
531 
532     return true;
533   }
534 
getConnectionState(BluetoothDevice device)535   int getConnectionState(BluetoothDevice device) {
536       if (mHidDevice != null && mHidDevice.equals(device)) {
537           return mHidDeviceState;
538       }
539       return BluetoothInputHost.STATE_DISCONNECTED;
540   }
541 
getDevicesMatchingConnectionStates(int[] states)542   List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
543       enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
544       List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
545 
546       if (mHidDevice != null) {
547           for (int state : states) {
548               if (state == mHidDeviceState) {
549                   inputDevices.add(mHidDevice);
550                   break;
551               }
552           }
553       }
554       return inputDevices;
555   }
556 
onApplicationStateChanged(byte[] address, boolean registered)557   private synchronized void onApplicationStateChanged(byte[] address,
558                                                       boolean registered) {
559     if (DBG)
560       Log.v(TAG, "onApplicationStateChanged(): registered=" + registered);
561 
562     Message msg = mHandler.obtainMessage(MESSAGE_APPLICATION_STATE_CHANGED);
563     msg.obj = address;
564     msg.arg1 = registered ? 1 : 0;
565     mHandler.sendMessage(msg);
566   }
567 
onConnectStateChanged(byte[] address, int state)568   private synchronized void onConnectStateChanged(byte[] address, int state) {
569     if (DBG)
570       Log.v(TAG, "onConnectStateChanged(): address=" +
571                      Arrays.toString(address) + " state=" + state);
572 
573     Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
574     msg.obj = address;
575     msg.arg1 = state;
576     mHandler.sendMessage(msg);
577   }
578 
onGetReport(byte type, byte id, short bufferSize)579   private synchronized void onGetReport(byte type, byte id, short bufferSize) {
580     if (DBG)
581       Log.v(TAG, "onGetReport(): type=" + type + " id=" + id + " bufferSize=" +
582                      bufferSize);
583 
584     Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
585     msg.obj = bufferSize > 0 ? new Integer(bufferSize) : null;
586     msg.arg1 = type;
587     msg.arg2 = id;
588     mHandler.sendMessage(msg);
589   }
590 
onSetReport(byte reportType, byte reportId, byte[] data)591   private synchronized void onSetReport(byte reportType, byte reportId,
592                                         byte[] data) {
593     if (DBG)
594       Log.v(TAG, "onSetReport(): reportType=" + reportType + " reportId=" +
595                      reportId);
596 
597     ByteBuffer bb = ByteBuffer.wrap(data);
598 
599     Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
600     msg.arg1 = reportType;
601     msg.arg2 = reportId;
602     msg.obj = bb;
603     mHandler.sendMessage(msg);
604   }
605 
onSetProtocol(byte protocol)606   private synchronized void onSetProtocol(byte protocol) {
607     if (DBG)
608       Log.v(TAG, "onSetProtocol(): protocol=" + protocol);
609 
610     Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL);
611     msg.arg1 = protocol;
612     mHandler.sendMessage(msg);
613   }
614 
onIntrData(byte reportId, byte[] data)615   private synchronized void onIntrData(byte reportId, byte[] data) {
616     if (DBG)
617       Log.v(TAG, "onIntrData(): reportId=" + reportId);
618 
619     ByteBuffer bb = ByteBuffer.wrap(data);
620 
621     Message msg = mHandler.obtainMessage(MESSAGE_INTR_DATA);
622     msg.arg1 = reportId;
623     msg.obj = bb;
624     mHandler.sendMessage(msg);
625   }
626 
onVirtualCableUnplug()627   private synchronized void onVirtualCableUnplug() {
628     if (DBG)
629       Log.v(TAG, "onVirtualCableUnplug()");
630 
631     Message msg = mHandler.obtainMessage(MESSAGE_VC_UNPLUG);
632     mHandler.sendMessage(msg);
633   }
634 
broadcastConnectionState(BluetoothDevice device, int newState)635   private void broadcastConnectionState(BluetoothDevice device, int newState) {
636     if (DBG)
637       Log.v(TAG, "broadcastConnectionState(): device=" + device.getAddress() +
638                      " newState=" + newState);
639 
640     if (mHidDevice != null && !mHidDevice.equals(device)) {
641         Log.w(TAG, "Connection state changed for unknown device, ignoring");
642         return;
643     }
644 
645     int prevState = mHidDeviceState;
646     mHidDeviceState = newState;
647 
648     Log.i(TAG, "connection state for " + device.getAddress() + ": " +
649                    prevState + " -> " + newState);
650 
651     if (prevState == newState) {
652       return;
653     }
654 
655     Intent intent =
656         new Intent(BluetoothInputHost.ACTION_CONNECTION_STATE_CHANGED);
657     intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
658     intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
659     intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
660     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
661     sendBroadcast(intent, BLUETOOTH_PERM);
662   }
663 
convertHalState(int halState)664   private static int convertHalState(int halState) {
665     switch (halState) {
666     case CONN_STATE_CONNECTED:
667       return BluetoothProfile.STATE_CONNECTED;
668     case CONN_STATE_CONNECTING:
669       return BluetoothProfile.STATE_CONNECTING;
670     case CONN_STATE_DISCONNECTED:
671       return BluetoothProfile.STATE_DISCONNECTED;
672     case CONN_STATE_DISCONNECTING:
673       return BluetoothProfile.STATE_DISCONNECTING;
674     default:
675       return BluetoothProfile.STATE_DISCONNECTED;
676     }
677   }
678 
679   private final static int CONN_STATE_CONNECTED = 0;
680   private final static int CONN_STATE_CONNECTING = 1;
681   private final static int CONN_STATE_DISCONNECTED = 2;
682   private final static int CONN_STATE_DISCONNECTING = 3;
683 
classInitNative()684   private native static void classInitNative();
initNative()685   private native void initNative();
cleanupNative()686   private native void cleanupNative();
registerAppNative(String name, String description, String provider, byte subclass, byte[] descriptors, int[] inQos, int[] outQos)687   private native boolean registerAppNative(String name, String description,
688                                            String provider, byte subclass,
689                                            byte[] descriptors, int[] inQos,
690                                            int[] outQos);
unregisterAppNative()691   private native boolean unregisterAppNative();
sendReportNative(int id, byte[] data)692   private native boolean sendReportNative(int id, byte[] data);
replyReportNative(byte type, byte id, byte[] data)693   private native boolean replyReportNative(byte type, byte id, byte[] data);
unplugNative()694   private native boolean unplugNative();
connectNative(byte[] btAddress)695   private native boolean connectNative(byte[] btAddress);
disconnectNative()696   private native boolean disconnectNative();
reportErrorNative(byte error)697   private native boolean reportErrorNative(byte error);
698 }
699