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