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