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