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