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 #define LOG_TAG "BluetoothHidDeviceServiceJni"
18 
19 #include <bluetooth/log.h>
20 #include <jni.h>
21 #include <nativehelper/scoped_local_ref.h>
22 
23 #include <cstdint>
24 #include <cstdlib>
25 #include <cstring>
26 
27 #include "com_android_bluetooth.h"
28 #include "hardware/bluetooth.h"
29 #include "hardware/bt_hd.h"
30 #include "types/raw_address.h"
31 
32 namespace android {
33 
34 static jmethodID method_onApplicationStateChanged;
35 static jmethodID method_onConnectStateChanged;
36 static jmethodID method_onGetReport;
37 static jmethodID method_onSetReport;
38 static jmethodID method_onSetProtocol;
39 static jmethodID method_onInterruptData;
40 static jmethodID method_onVirtualCableUnplug;
41 
42 static const bthd_interface_t* sHiddIf = NULL;
43 static jobject mCallbacksObj = NULL;
44 
marshall_bda(RawAddress * bd_addr)45 static jbyteArray marshall_bda(RawAddress* bd_addr) {
46   CallbackEnv sCallbackEnv(__func__);
47   if (!sCallbackEnv.valid()) {
48     return NULL;
49   }
50 
51   jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress));
52   if (!addr) {
53     log::error("Fail to new jbyteArray bd addr");
54     return NULL;
55   }
56   sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress), (jbyte*)bd_addr);
57   return addr;
58 }
59 
application_state_callback(RawAddress * bd_addr,bthd_application_state_t state)60 static void application_state_callback(RawAddress* bd_addr, bthd_application_state_t state) {
61   jboolean registered = JNI_FALSE;
62 
63   CallbackEnv sCallbackEnv(__func__);
64 
65   if (state == BTHD_APP_STATE_REGISTERED) {
66     registered = JNI_TRUE;
67   }
68 
69   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), NULL);
70 
71   if (bd_addr) {
72     addr.reset(marshall_bda(bd_addr));
73     if (!addr.get()) {
74       log::error("failed to allocate storage for bt_addr");
75       return;
76     }
77   }
78 
79   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onApplicationStateChanged, addr.get(),
80                                registered);
81 }
82 
connection_state_callback(RawAddress * bd_addr,bthd_connection_state_t state)83 static void connection_state_callback(RawAddress* bd_addr, bthd_connection_state_t state) {
84   CallbackEnv sCallbackEnv(__func__);
85 
86   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
87   if (!addr.get()) {
88     log::error("failed to allocate storage for bt_addr");
89     return;
90   }
91 
92   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr.get(),
93                                (jint)state);
94 }
95 
get_report_callback(uint8_t type,uint8_t id,uint16_t buffer_size)96 static void get_report_callback(uint8_t type, uint8_t id, uint16_t buffer_size) {
97   CallbackEnv sCallbackEnv(__func__);
98 
99   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, type, id, buffer_size);
100 }
101 
set_report_callback(uint8_t type,uint8_t id,uint16_t len,uint8_t * p_data)102 static void set_report_callback(uint8_t type, uint8_t id, uint16_t len, uint8_t* p_data) {
103   CallbackEnv sCallbackEnv(__func__);
104 
105   ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(), sCallbackEnv->NewByteArray(len));
106   if (!data.get()) {
107     log::error("failed to allocate storage for report data");
108     return;
109   }
110   sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data);
111 
112   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetReport, (jbyte)type, (jbyte)id,
113                                data.get());
114 }
115 
set_protocol_callback(uint8_t protocol)116 static void set_protocol_callback(uint8_t protocol) {
117   CallbackEnv sCallbackEnv(__func__);
118 
119   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetProtocol, protocol);
120 }
121 
intr_data_callback(uint8_t report_id,uint16_t len,uint8_t * p_data)122 static void intr_data_callback(uint8_t report_id, uint16_t len, uint8_t* p_data) {
123   CallbackEnv sCallbackEnv(__func__);
124 
125   ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(), sCallbackEnv->NewByteArray(len));
126   if (!data.get()) {
127     log::error("failed to allocate storage for report data");
128     return;
129   }
130   sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data);
131 
132   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onInterruptData, (jbyte)report_id, data.get());
133 }
134 
vc_unplug_callback(void)135 static void vc_unplug_callback(void) {
136   CallbackEnv sCallbackEnv(__func__);
137   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualCableUnplug);
138 }
139 
140 static bthd_callbacks_t sHiddCb = {
141         sizeof(sHiddCb),
142 
143         application_state_callback,
144         connection_state_callback,
145         get_report_callback,
146         set_report_callback,
147         set_protocol_callback,
148         intr_data_callback,
149         vc_unplug_callback,
150 };
151 
initNative(JNIEnv * env,jobject object)152 static void initNative(JNIEnv* env, jobject object) {
153   const bt_interface_t* btif;
154   bt_status_t status;
155 
156   log::verbose("enter");
157 
158   if ((btif = getBluetoothInterface()) == NULL) {
159     log::error("Cannot obtain BT interface");
160     return;
161   }
162 
163   if (sHiddIf != NULL) {
164     log::warn("Cleaning up interface");
165     sHiddIf->cleanup();
166     sHiddIf = NULL;
167   }
168 
169   if (mCallbacksObj != NULL) {
170     log::warn("Cleaning up callback object");
171     env->DeleteGlobalRef(mCallbacksObj);
172     mCallbacksObj = NULL;
173   }
174 
175   if ((sHiddIf = (bthd_interface_t*)btif->get_profile_interface(BT_PROFILE_HIDDEV_ID)) == NULL) {
176     log::error("Cannot obtain interface");
177     return;
178   }
179 
180   if ((status = sHiddIf->init(&sHiddCb)) != BT_STATUS_SUCCESS) {
181     log::error("Failed to initialize interface ({})", bt_status_text(status));
182     sHiddIf = NULL;
183     return;
184   }
185 
186   mCallbacksObj = env->NewGlobalRef(object);
187 
188   log::verbose("done");
189 }
190 
cleanupNative(JNIEnv * env,jobject)191 static void cleanupNative(JNIEnv* env, jobject /* object */) {
192   log::verbose("enter");
193 
194   if (sHiddIf != NULL) {
195     log::info("Cleaning up interface");
196     sHiddIf->cleanup();
197     sHiddIf = NULL;
198   }
199 
200   if (mCallbacksObj != NULL) {
201     log::info("Cleaning up callback object");
202     env->DeleteGlobalRef(mCallbacksObj);
203     mCallbacksObj = NULL;
204   }
205 
206   log::verbose("done");
207 }
208 
fill_qos(JNIEnv * env,jintArray in,bthd_qos_param_t * out)209 static void fill_qos(JNIEnv* env, jintArray in, bthd_qos_param_t* out) {
210   // set default values
211   out->service_type = 0x01;                                            // best effort
212   out->token_rate = out->token_bucket_size = out->peak_bandwidth = 0;  // don't care
213   out->access_latency = out->delay_variation = 0xffffffff;             // don't care
214 
215   if (in == NULL) {
216     return;
217   }
218 
219   jsize len = env->GetArrayLength(in);
220 
221   if (len != 6) {
222     return;
223   }
224 
225   uint32_t* buf = (uint32_t*)calloc(len, sizeof(uint32_t));
226 
227   if (buf == NULL) {
228     return;
229   }
230 
231   env->GetIntArrayRegion(in, 0, len, (jint*)buf);
232 
233   out->service_type = (uint8_t)buf[0];
234   out->token_rate = buf[1];
235   out->token_bucket_size = buf[2];
236   out->peak_bandwidth = buf[3];
237   out->access_latency = buf[4];
238   out->delay_variation = buf[5];
239 
240   free(buf);
241 }
242 
registerAppNative(JNIEnv * env,jobject,jstring name,jstring description,jstring provider,jbyte subclass,jbyteArray descriptors,jintArray p_in_qos,jintArray p_out_qos)243 static jboolean registerAppNative(JNIEnv* env, jobject /* thiz */, jstring name,
244                                   jstring description, jstring provider, jbyte subclass,
245                                   jbyteArray descriptors, jintArray p_in_qos, jintArray p_out_qos) {
246   log::verbose("enter");
247 
248   if (!sHiddIf) {
249     log::error("Failed to get the Bluetooth HIDD Interface");
250     return JNI_FALSE;
251   }
252 
253   jboolean result = JNI_FALSE;
254   bthd_app_param_t app_param;
255   bthd_qos_param_t in_qos;
256   bthd_qos_param_t out_qos;
257   jsize size;
258   uint8_t* data;
259 
260   size = env->GetArrayLength(descriptors);
261   data = (uint8_t*)malloc(size);
262 
263   if (data != NULL) {
264     env->GetByteArrayRegion(descriptors, 0, size, (jbyte*)data);
265 
266     app_param.name = env->GetStringUTFChars(name, NULL);
267     app_param.description = env->GetStringUTFChars(description, NULL);
268     app_param.provider = env->GetStringUTFChars(provider, NULL);
269     app_param.subclass = subclass;
270     app_param.desc_list = data;
271     app_param.desc_list_len = size;
272 
273     fill_qos(env, p_in_qos, &in_qos);
274     fill_qos(env, p_out_qos, &out_qos);
275 
276     bt_status_t ret = sHiddIf->register_app(&app_param, &in_qos, &out_qos);
277 
278     log::verbose("register_app() returned {}", bt_status_text(ret));
279 
280     if (ret == BT_STATUS_SUCCESS) {
281       result = JNI_TRUE;
282     }
283 
284     env->ReleaseStringUTFChars(name, app_param.name);
285     env->ReleaseStringUTFChars(description, app_param.description);
286     env->ReleaseStringUTFChars(provider, app_param.provider);
287 
288     free(data);
289   }
290 
291   log::verbose("done ({})", result);
292 
293   return result;
294 }
295 
unregisterAppNative(JNIEnv *,jobject)296 static jboolean unregisterAppNative(JNIEnv* /* env */, jobject /* thiz */) {
297   log::verbose("enter");
298 
299   jboolean result = JNI_FALSE;
300 
301   if (!sHiddIf) {
302     log::error("Failed to get the Bluetooth HIDD Interface");
303     return JNI_FALSE;
304   }
305 
306   bt_status_t ret = sHiddIf->unregister_app();
307 
308   log::verbose("unregister_app() returned {}", bt_status_text(ret));
309 
310   if (ret == BT_STATUS_SUCCESS) {
311     result = JNI_TRUE;
312   }
313 
314   log::verbose("done ({})", result);
315 
316   return result;
317 }
318 
sendReportNative(JNIEnv * env,jobject,jint id,jbyteArray data)319 static jboolean sendReportNative(JNIEnv* env, jobject /* thiz */, jint id, jbyteArray data) {
320   jboolean result = JNI_FALSE;
321 
322   if (!sHiddIf) {
323     log::error("Failed to get the Bluetooth HIDD Interface");
324     return JNI_FALSE;
325   }
326 
327   jsize size;
328   uint8_t* buf;
329 
330   size = env->GetArrayLength(data);
331   buf = (uint8_t*)malloc(size);
332 
333   if (buf != NULL) {
334     env->GetByteArrayRegion(data, 0, size, (jbyte*)buf);
335 
336     bt_status_t ret = sHiddIf->send_report(BTHD_REPORT_TYPE_INTRDATA, id, size, buf);
337 
338     if (ret == BT_STATUS_SUCCESS) {
339       result = JNI_TRUE;
340     }
341 
342     free(buf);
343   }
344 
345   return result;
346 }
347 
replyReportNative(JNIEnv * env,jobject,jbyte type,jbyte id,jbyteArray data)348 static jboolean replyReportNative(JNIEnv* env, jobject /* thiz */, jbyte type, jbyte id,
349                                   jbyteArray data) {
350   log::verbose("enter");
351 
352   if (!sHiddIf) {
353     log::error("Failed to get the Bluetooth HIDD Interface");
354     return JNI_FALSE;
355   }
356 
357   jboolean result = JNI_FALSE;
358   jsize size;
359   uint8_t* buf;
360 
361   size = env->GetArrayLength(data);
362   buf = (uint8_t*)malloc(size);
363 
364   if (buf != NULL) {
365     int report_type = (type & 0x03);
366     env->GetByteArrayRegion(data, 0, size, (jbyte*)buf);
367 
368     bt_status_t ret = sHiddIf->send_report((bthd_report_type_t)report_type, id, size, buf);
369 
370     log::verbose("send_report() returned {}", bt_status_text(ret));
371 
372     if (ret == BT_STATUS_SUCCESS) {
373       result = JNI_TRUE;
374     }
375 
376     free(buf);
377   }
378 
379   log::verbose("done ({})", result);
380 
381   return result;
382 }
383 
reportErrorNative(JNIEnv *,jobject,jbyte error)384 static jboolean reportErrorNative(JNIEnv* /* env */, jobject /* thiz */, jbyte error) {
385   log::verbose("enter");
386 
387   if (!sHiddIf) {
388     log::error("Failed to get the Bluetooth HIDD Interface");
389     return JNI_FALSE;
390   }
391 
392   jboolean result = JNI_FALSE;
393 
394   bt_status_t ret = sHiddIf->report_error(error);
395 
396   log::verbose("report_error() returned {}", bt_status_text(ret));
397 
398   if (ret == BT_STATUS_SUCCESS) {
399     result = JNI_TRUE;
400   }
401 
402   log::verbose("done ({})", result);
403 
404   return result;
405 }
406 
unplugNative(JNIEnv *,jobject)407 static jboolean unplugNative(JNIEnv* /* env */, jobject /* thiz */) {
408   log::verbose("enter");
409 
410   if (!sHiddIf) {
411     log::error("Failed to get the Bluetooth HIDD Interface");
412     return JNI_FALSE;
413   }
414 
415   jboolean result = JNI_FALSE;
416 
417   bt_status_t ret = sHiddIf->virtual_cable_unplug();
418 
419   log::verbose("virtual_cable_unplug() returned {}", bt_status_text(ret));
420 
421   if (ret == BT_STATUS_SUCCESS) {
422     result = JNI_TRUE;
423   }
424 
425   log::verbose("done ({})", result);
426 
427   return result;
428 }
429 
connectNative(JNIEnv * env,jobject,jbyteArray address)430 static jboolean connectNative(JNIEnv* env, jobject /* thiz */, jbyteArray address) {
431   log::verbose("enter");
432 
433   if (!sHiddIf) {
434     log::error("Failed to get the Bluetooth HIDD Interface");
435     return JNI_FALSE;
436   }
437 
438   jboolean result = JNI_FALSE;
439 
440   jbyte* addr = env->GetByteArrayElements(address, NULL);
441   if (!addr) {
442     log::error("Bluetooth device address null");
443     return JNI_FALSE;
444   }
445 
446   bt_status_t ret = sHiddIf->connect((RawAddress*)addr);
447 
448   log::verbose("connect() returned {}", bt_status_text(ret));
449 
450   if (ret == BT_STATUS_SUCCESS) {
451     result = JNI_TRUE;
452   }
453 
454   log::verbose("done ({})", result);
455 
456   return result;
457 }
458 
disconnectNative(JNIEnv *,jobject)459 static jboolean disconnectNative(JNIEnv* /* env */, jobject /* thiz */) {
460   log::verbose("enter");
461 
462   if (!sHiddIf) {
463     log::error("Failed to get the Bluetooth HIDD Interface");
464     return JNI_FALSE;
465   }
466 
467   jboolean result = JNI_FALSE;
468 
469   bt_status_t ret = sHiddIf->disconnect();
470 
471   log::verbose("disconnect() returned {}", bt_status_text(ret));
472 
473   if (ret == BT_STATUS_SUCCESS) {
474     result = JNI_TRUE;
475   }
476 
477   log::verbose("done ({})", result);
478 
479   return result;
480 }
481 
register_com_android_bluetooth_hid_device(JNIEnv * env)482 int register_com_android_bluetooth_hid_device(JNIEnv* env) {
483   const JNINativeMethod methods[] = {
484           {"initNative", "()V", (void*)initNative},
485           {"cleanupNative", "()V", (void*)cleanupNative},
486           {"registerAppNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z",
487            (void*)registerAppNative},
488           {"unregisterAppNative", "()Z", (void*)unregisterAppNative},
489           {"sendReportNative", "(I[B)Z", (void*)sendReportNative},
490           {"replyReportNative", "(BB[B)Z", (void*)replyReportNative},
491           {"reportErrorNative", "(B)Z", (void*)reportErrorNative},
492           {"unplugNative", "()Z", (void*)unplugNative},
493           {"connectNative", "([B)Z", (void*)connectNative},
494           {"disconnectNative", "()Z", (void*)disconnectNative},
495   };
496   const int result = REGISTER_NATIVE_METHODS(
497           env, "com/android/bluetooth/hid/HidDeviceNativeInterface", methods);
498   if (result != 0) {
499     return result;
500   }
501 
502   const JNIJavaMethod javaMethods[] = {
503           {"onApplicationStateChanged", "([BZ)V", &method_onApplicationStateChanged},
504           {"onConnectStateChanged", "([BI)V", &method_onConnectStateChanged},
505           {"onGetReport", "(BBS)V", &method_onGetReport},
506           {"onSetReport", "(BB[B)V", &method_onSetReport},
507           {"onSetProtocol", "(B)V", &method_onSetProtocol},
508           {"onInterruptData", "(B[B)V", &method_onInterruptData},
509           {"onVirtualCableUnplug", "()V", &method_onVirtualCableUnplug},
510   };
511   GET_JAVA_METHODS(env, "com/android/bluetooth/hid/HidDeviceNativeInterface", javaMethods);
512 
513   return 0;
514 }
515 }  // namespace android
516