1 /*
2  * Copyright 2017 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 /*
18  * Defines the native inteface that is used by HID Device service to
19  * send or receive messages from the native stack. This file is registered
20  * for the native methods in the corresponding JNI C++ file.
21  */
22 
23 package com.android.bluetooth.hid;
24 
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothDevice;
27 import android.util.Log;
28 
29 import com.android.bluetooth.Utils;
30 import com.android.internal.annotations.GuardedBy;
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 /**
34  * HID Device Native Interface to/from JNI.
35  */
36 public class HidDeviceNativeInterface {
37     private static final String TAG = "HidDeviceNativeInterface";
38     private BluetoothAdapter mAdapter;
39 
40     @GuardedBy("INSTANCE_LOCK")
41     private static HidDeviceNativeInterface sInstance;
42     private static final Object INSTANCE_LOCK = new Object();
43 
44     static {
classInitNative()45         classInitNative();
46     }
47 
48     @VisibleForTesting
HidDeviceNativeInterface()49     private HidDeviceNativeInterface() {
50         mAdapter = BluetoothAdapter.getDefaultAdapter();
51         if (mAdapter == null) {
52             Log.wtf(TAG, "No Bluetooth Adapter Available");
53         }
54     }
55 
56     /**
57      * Get the singleton instance.
58      */
getInstance()59     public static HidDeviceNativeInterface getInstance() {
60         synchronized (INSTANCE_LOCK) {
61             if (sInstance == null) {
62                 setInstance(new HidDeviceNativeInterface());
63             }
64             return sInstance;
65         }
66     }
67 
68     /**
69      * Set the singleton instance.
70      *
71      * @param nativeInterface native interface
72      */
setInstance(HidDeviceNativeInterface nativeInterface)73     private static void setInstance(HidDeviceNativeInterface nativeInterface) {
74         sInstance = nativeInterface;
75     }
76 
77     /**
78      * Initializes the native interface.
79      */
init()80     public void init() {
81         initNative();
82     }
83 
84     /**
85      * Cleanup the native interface.
86      */
cleanup()87     public void cleanup() {
88         cleanupNative();
89     }
90 
91     /**
92      * Registers the application
93      *
94      * @param name name of the HID Device application
95      * @param description description of the HID Device application
96      * @param provider provider of the HID Device application
97      * @param subclass subclass of the HID Device application
98      * @param descriptors HID descriptors
99      * @param inQos incoming QoS settings
100      * @param outQos outgoing QoS settings
101      * @return the result of the native call
102      */
registerApp(String name, String description, String provider, byte subclass, byte[] descriptors, int[] inQos, int[] outQos)103     public boolean registerApp(String name, String description, String provider,
104             byte subclass, byte[] descriptors, int[] inQos, int[] outQos) {
105         return registerAppNative(name, description, provider, subclass, descriptors, inQos, outQos);
106     }
107 
108     /**
109      * Unregisters the application
110      *
111      * @return the result of the native call
112      */
unregisterApp()113     public boolean unregisterApp() {
114         return unregisterAppNative();
115     }
116 
117     /**
118      * Send report to the remote host
119      *
120      * @param id report ID
121      * @param data report data array
122      * @return the result of the native call
123      */
sendReport(int id, byte[] data)124     public boolean sendReport(int id, byte[] data) {
125         return sendReportNative(id, data);
126     }
127 
128     /**
129      * Reply report to the remote host
130      *
131      * @param type report type
132      * @param id report ID
133      * @param data report data array
134      * @return the result of the native call
135      */
replyReport(byte type, byte id, byte[] data)136     public boolean replyReport(byte type, byte id, byte[] data) {
137         return replyReportNative(type, id, data);
138     }
139 
140     /**
141      * Send virtual unplug to the remote host
142      *
143      * @return the result of the native call
144      */
unplug()145     public boolean unplug() {
146         return unplugNative();
147     }
148 
149     /**
150      * Connect to the remote host
151      *
152      * @param device remote host device
153      * @return the result of the native call
154      */
connect(BluetoothDevice device)155     public boolean connect(BluetoothDevice device) {
156         return connectNative(getByteAddress(device));
157     }
158 
159     /**
160      * Disconnect from the remote host
161      *
162      * @return the result of the native call
163      */
disconnect()164     public boolean disconnect() {
165         return disconnectNative();
166     }
167 
168     /**
169      * Report error to the remote host
170      *
171      * @param error error byte
172      * @return the result of the native call
173      */
reportError(byte error)174     public boolean reportError(byte error) {
175         return reportErrorNative(error);
176     }
177 
onApplicationStateChanged(byte[] address, boolean registered)178     private synchronized void onApplicationStateChanged(byte[] address, boolean registered) {
179         HidDeviceService service = HidDeviceService.getHidDeviceService();
180         if (service != null) {
181             service.onApplicationStateChangedFromNative(getDevice(address), registered);
182         } else {
183             Log.wtf(TAG, "FATAL: onApplicationStateChanged() "
184                     + "is called from the stack while service is not available.");
185         }
186     }
187 
onConnectStateChanged(byte[] address, int state)188     private synchronized void onConnectStateChanged(byte[] address, int state) {
189         HidDeviceService service = HidDeviceService.getHidDeviceService();
190         if (service != null) {
191             service.onConnectStateChangedFromNative(getDevice(address), state);
192         } else {
193             Log.wtf(TAG, "FATAL: onConnectStateChanged() "
194                     + "is called from the stack while service is not available.");
195         }
196     }
197 
onGetReport(byte type, byte id, short bufferSize)198     private synchronized void onGetReport(byte type, byte id, short bufferSize) {
199         HidDeviceService service = HidDeviceService.getHidDeviceService();
200         if (service != null) {
201             service.onGetReportFromNative(type, id, bufferSize);
202         } else {
203             Log.wtf(TAG, "FATAL: onGetReport() "
204                     + "is called from the stack while service is not available.");
205         }
206     }
207 
onSetReport(byte reportType, byte reportId, byte[] data)208     private synchronized void onSetReport(byte reportType, byte reportId, byte[] data) {
209         HidDeviceService service = HidDeviceService.getHidDeviceService();
210         if (service != null) {
211             service.onSetReportFromNative(reportType, reportId, data);
212         } else {
213             Log.wtf(TAG, "FATAL: onSetReport() "
214                     + "is called from the stack while service is not available.");
215         }
216     }
217 
onSetProtocol(byte protocol)218     private synchronized void onSetProtocol(byte protocol) {
219         HidDeviceService service = HidDeviceService.getHidDeviceService();
220         if (service != null) {
221             service.onSetProtocolFromNative(protocol);
222         } else {
223             Log.wtf(TAG, "FATAL: onSetProtocol() "
224                     + "is called from the stack while service is not available.");
225         }
226     }
227 
onInterruptData(byte reportId, byte[] data)228     private synchronized void onInterruptData(byte reportId, byte[] data) {
229         HidDeviceService service = HidDeviceService.getHidDeviceService();
230         if (service != null) {
231             service.onInterruptDataFromNative(reportId, data);
232         } else {
233             Log.wtf(TAG, "FATAL: onInterruptData() "
234                     + "is called from the stack while service is not available.");
235         }
236     }
237 
onVirtualCableUnplug()238     private synchronized void onVirtualCableUnplug() {
239         HidDeviceService service = HidDeviceService.getHidDeviceService();
240         if (service != null) {
241             service.onVirtualCableUnplugFromNative();
242         } else {
243             Log.wtf(TAG, "FATAL: onVirtualCableUnplug() "
244                     + "is called from the stack while service is not available.");
245         }
246     }
247 
getDevice(byte[] address)248     private BluetoothDevice getDevice(byte[] address) {
249         if (address == null) {
250             return null;
251         }
252         return mAdapter.getRemoteDevice(address);
253     }
254 
getByteAddress(BluetoothDevice device)255     private byte[] getByteAddress(BluetoothDevice device) {
256         return Utils.getBytesFromAddress(device.getAddress());
257     }
258 
classInitNative()259     private static native void classInitNative();
260 
initNative()261     private native void initNative();
262 
cleanupNative()263     private native void cleanupNative();
264 
registerAppNative(String name, String description, String provider, byte subclass, byte[] descriptors, int[] inQos, int[] outQos)265     private native boolean registerAppNative(String name, String description, String provider,
266             byte subclass, byte[] descriptors, int[] inQos, int[] outQos);
267 
unregisterAppNative()268     private native boolean unregisterAppNative();
269 
sendReportNative(int id, byte[] data)270     private native boolean sendReportNative(int id, byte[] data);
271 
replyReportNative(byte type, byte id, byte[] data)272     private native boolean replyReportNative(byte type, byte id, byte[] data);
273 
unplugNative()274     private native boolean unplugNative();
275 
connectNative(byte[] btAddress)276     private native boolean connectNative(byte[] btAddress);
277 
disconnectNative()278     private native boolean disconnectNative();
279 
reportErrorNative(byte error)280     private native boolean reportErrorNative(byte error);
281 }
282