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