1 /*
2 * Copyright (C) 2012 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 "BluetoothHidServiceJni"
18
19 #define LOG_NDEBUG 1
20
21 #include "android_runtime/AndroidRuntime.h"
22 #include "com_android_bluetooth.h"
23 #include "hardware/bt_hh.h"
24 #include "utils/Log.h"
25
26 #include <string.h>
27
28 namespace android {
29
30 static jmethodID method_onConnectStateChanged;
31 static jmethodID method_onGetProtocolMode;
32 static jmethodID method_onGetReport;
33 static jmethodID method_onHandshake;
34 static jmethodID method_onVirtualUnplug;
35 static jmethodID method_onGetIdleTime;
36
37 static const bthh_interface_t* sBluetoothHidInterface = NULL;
38 static jobject mCallbacksObj = NULL;
39
marshall_bda(RawAddress * bd_addr)40 static jbyteArray marshall_bda(RawAddress* bd_addr) {
41 CallbackEnv sCallbackEnv(__func__);
42 if (!sCallbackEnv.valid()) return NULL;
43
44 jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress));
45 if (!addr) {
46 ALOGE("Fail to new jbyteArray bd addr");
47 return NULL;
48 }
49 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress),
50 (jbyte*)bd_addr);
51 return addr;
52 }
53
connection_state_callback(RawAddress * bd_addr,bthh_connection_state_t state)54 static void connection_state_callback(RawAddress* bd_addr,
55 bthh_connection_state_t state) {
56 CallbackEnv sCallbackEnv(__func__);
57 if (!sCallbackEnv.valid()) return;
58 if (!mCallbacksObj) {
59 ALOGE("%s: mCallbacksObj is null", __func__);
60 return;
61 }
62 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
63 if (!addr.get()) {
64 ALOGE("Fail to new jbyteArray bd addr for HID channel state");
65 return;
66 }
67
68 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged,
69 addr.get(), (jint)state);
70 }
71
get_protocol_mode_callback(RawAddress * bd_addr,bthh_status_t hh_status,bthh_protocol_mode_t mode)72 static void get_protocol_mode_callback(RawAddress* bd_addr,
73 bthh_status_t hh_status,
74 bthh_protocol_mode_t mode) {
75 CallbackEnv sCallbackEnv(__func__);
76 if (!sCallbackEnv.valid()) return;
77 if (!mCallbacksObj) {
78 ALOGE("%s: mCallbacksObj is null", __func__);
79 return;
80 }
81 if (hh_status != BTHH_OK) {
82 ALOGE("BTHH Status is not OK!");
83 return;
84 }
85
86 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
87 if (!addr.get()) {
88 ALOGE("Fail to new jbyteArray bd addr for get protocal mode callback");
89 return;
90 }
91
92 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetProtocolMode,
93 addr.get(), (jint)mode);
94 }
95
get_report_callback(RawAddress * bd_addr,bthh_status_t hh_status,uint8_t * rpt_data,int rpt_size)96 static void get_report_callback(RawAddress* bd_addr, bthh_status_t hh_status,
97 uint8_t* rpt_data, int rpt_size) {
98 CallbackEnv sCallbackEnv(__func__);
99 if (!sCallbackEnv.valid()) return;
100 if (!mCallbacksObj) {
101 ALOGE("%s: mCallbacksObj is null", __func__);
102 return;
103 }
104 if (hh_status != BTHH_OK) {
105 ALOGE("BTHH Status is not OK!");
106 return;
107 }
108
109 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
110 if (!addr.get()) {
111 ALOGE("Fail to new jbyteArray bd addr for get report callback");
112 return;
113 }
114 ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(),
115 sCallbackEnv->NewByteArray(rpt_size));
116 if (!data.get()) {
117 ALOGE("Fail to new jbyteArray data for get report callback");
118 return;
119 }
120
121 sCallbackEnv->SetByteArrayRegion(data.get(), 0, rpt_size, (jbyte*)rpt_data);
122 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, addr.get(),
123 data.get(), (jint)rpt_size);
124 }
125
virtual_unplug_callback(RawAddress * bd_addr,bthh_status_t hh_status)126 static void virtual_unplug_callback(RawAddress* bd_addr,
127 bthh_status_t hh_status) {
128 ALOGV("call to virtual_unplug_callback");
129 CallbackEnv sCallbackEnv(__func__);
130 if (!sCallbackEnv.valid()) return;
131 if (!mCallbacksObj) {
132 ALOGE("%s: mCallbacksObj is null", __func__);
133 return;
134 }
135 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
136 if (!addr.get()) {
137 ALOGE("Fail to new jbyteArray bd addr for HID channel state");
138 return;
139 }
140 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug,
141 addr.get(), (jint)hh_status);
142 }
143
handshake_callback(RawAddress * bd_addr,bthh_status_t hh_status)144 static void handshake_callback(RawAddress* bd_addr, bthh_status_t hh_status) {
145 CallbackEnv sCallbackEnv(__func__);
146 if (!sCallbackEnv.valid()) return;
147 if (!mCallbacksObj) {
148 ALOGE("%s: mCallbacksObj is null", __func__);
149 return;
150 }
151
152 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
153 if (!addr.get()) {
154 ALOGE("Fail to new jbyteArray bd addr for handshake callback");
155 return;
156 }
157 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHandshake, addr.get(),
158 (jint)hh_status);
159 }
160
get_idle_time_callback(RawAddress * bd_addr,bthh_status_t hh_status,int idle_time)161 static void get_idle_time_callback(RawAddress* bd_addr, bthh_status_t hh_status,
162 int idle_time) {
163 CallbackEnv sCallbackEnv(__func__);
164 if (!sCallbackEnv.valid()) return;
165
166 ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
167 if (!addr.get()) {
168 ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
169 return;
170 }
171 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetIdleTime, addr.get(),
172 (jint)idle_time);
173 }
174
175 static bthh_callbacks_t sBluetoothHidCallbacks = {
176 sizeof(sBluetoothHidCallbacks),
177 connection_state_callback,
178 NULL,
179 get_protocol_mode_callback,
180 get_idle_time_callback,
181 get_report_callback,
182 virtual_unplug_callback,
183 handshake_callback};
184
185 // Define native functions
186
classInitNative(JNIEnv * env,jclass clazz)187 static void classInitNative(JNIEnv* env, jclass clazz) {
188 method_onConnectStateChanged =
189 env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
190 method_onGetProtocolMode =
191 env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
192 method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V");
193 method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V");
194 method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
195 method_onGetIdleTime = env->GetMethodID(clazz, "onGetIdleTime", "([BI)V");
196
197 ALOGI("%s: succeeds", __func__);
198 }
199
initializeNative(JNIEnv * env,jobject object)200 static void initializeNative(JNIEnv* env, jobject object) {
201 const bt_interface_t* btInf = getBluetoothInterface();
202 if (btInf == NULL) {
203 ALOGE("Bluetooth module is not loaded");
204 return;
205 }
206
207 if (sBluetoothHidInterface != NULL) {
208 ALOGW("Cleaning up Bluetooth HID Interface before initializing...");
209 sBluetoothHidInterface->cleanup();
210 sBluetoothHidInterface = NULL;
211 }
212
213 if (mCallbacksObj != NULL) {
214 ALOGW("Cleaning up Bluetooth GID callback object");
215 env->DeleteGlobalRef(mCallbacksObj);
216 mCallbacksObj = NULL;
217 }
218
219 sBluetoothHidInterface =
220 (bthh_interface_t*)btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID);
221 if (sBluetoothHidInterface == NULL) {
222 ALOGE("Failed to get Bluetooth HID Interface");
223 return;
224 }
225
226 bt_status_t status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks);
227 if (status != BT_STATUS_SUCCESS) {
228 ALOGE("Failed to initialize Bluetooth HID, status: %d", status);
229 sBluetoothHidInterface = NULL;
230 return;
231 }
232
233 mCallbacksObj = env->NewGlobalRef(object);
234 }
235
cleanupNative(JNIEnv * env,jobject object)236 static void cleanupNative(JNIEnv* env, jobject object) {
237 const bt_interface_t* btInf = getBluetoothInterface();
238
239 if (btInf == NULL) {
240 ALOGE("Bluetooth module is not loaded");
241 return;
242 }
243
244 if (sBluetoothHidInterface != NULL) {
245 ALOGW("Cleaning up Bluetooth HID Interface...");
246 sBluetoothHidInterface->cleanup();
247 sBluetoothHidInterface = NULL;
248 }
249
250 if (mCallbacksObj != NULL) {
251 ALOGW("Cleaning up Bluetooth GID callback object");
252 env->DeleteGlobalRef(mCallbacksObj);
253 mCallbacksObj = NULL;
254 }
255 }
256
connectHidNative(JNIEnv * env,jobject object,jbyteArray address)257 static jboolean connectHidNative(JNIEnv* env, jobject object,
258 jbyteArray address) {
259 if (!sBluetoothHidInterface) return JNI_FALSE;
260
261 jbyte* addr = env->GetByteArrayElements(address, NULL);
262 if (!addr) {
263 ALOGE("Bluetooth device address null");
264 return JNI_FALSE;
265 }
266
267 jboolean ret = JNI_TRUE;
268 bt_status_t status = sBluetoothHidInterface->connect((RawAddress*)addr);
269 if (status != BT_STATUS_SUCCESS) {
270 ALOGE("Failed HID channel connection, status: %d", status);
271 ret = JNI_FALSE;
272 }
273 env->ReleaseByteArrayElements(address, addr, 0);
274
275 return ret;
276 }
277
disconnectHidNative(JNIEnv * env,jobject object,jbyteArray address)278 static jboolean disconnectHidNative(JNIEnv* env, jobject object,
279 jbyteArray address) {
280 jbyte* addr;
281 jboolean ret = JNI_TRUE;
282 if (!sBluetoothHidInterface) return JNI_FALSE;
283
284 addr = env->GetByteArrayElements(address, NULL);
285 if (!addr) {
286 ALOGE("Bluetooth device address null");
287 return JNI_FALSE;
288 }
289
290 bt_status_t status = sBluetoothHidInterface->disconnect((RawAddress*)addr);
291 if (status != BT_STATUS_SUCCESS) {
292 ALOGE("Failed disconnect hid channel, status: %d", status);
293 ret = JNI_FALSE;
294 }
295 env->ReleaseByteArrayElements(address, addr, 0);
296
297 return ret;
298 }
299
getProtocolModeNative(JNIEnv * env,jobject object,jbyteArray address)300 static jboolean getProtocolModeNative(JNIEnv* env, jobject object,
301 jbyteArray address) {
302 if (!sBluetoothHidInterface) return JNI_FALSE;
303
304 jbyte* addr = env->GetByteArrayElements(address, NULL);
305 if (!addr) {
306 ALOGE("Bluetooth device address null");
307 return JNI_FALSE;
308 }
309
310 jboolean ret = JNI_TRUE;
311 // TODO: protocolMode is unused by the backend: see b/28908173
312 bthh_protocol_mode_t protocolMode = BTHH_UNSUPPORTED_MODE;
313 bt_status_t status = sBluetoothHidInterface->get_protocol(
314 (RawAddress*)addr, (bthh_protocol_mode_t)protocolMode);
315 if (status != BT_STATUS_SUCCESS) {
316 ALOGE("Failed get protocol mode, status: %d", status);
317 ret = JNI_FALSE;
318 }
319 env->ReleaseByteArrayElements(address, addr, 0);
320
321 return ret;
322 }
323
virtualUnPlugNative(JNIEnv * env,jobject object,jbyteArray address)324 static jboolean virtualUnPlugNative(JNIEnv* env, jobject object,
325 jbyteArray address) {
326 if (!sBluetoothHidInterface) return JNI_FALSE;
327
328 jbyte* addr = env->GetByteArrayElements(address, NULL);
329 if (!addr) {
330 ALOGE("Bluetooth device address null");
331 return JNI_FALSE;
332 }
333
334 jboolean ret = JNI_TRUE;
335 bt_status_t status =
336 sBluetoothHidInterface->virtual_unplug((RawAddress*)addr);
337 if (status != BT_STATUS_SUCCESS) {
338 ALOGE("Failed virual unplug, status: %d", status);
339 ret = JNI_FALSE;
340 }
341 env->ReleaseByteArrayElements(address, addr, 0);
342 return ret;
343 }
344
setProtocolModeNative(JNIEnv * env,jobject object,jbyteArray address,jint protocolMode)345 static jboolean setProtocolModeNative(JNIEnv* env, jobject object,
346 jbyteArray address, jint protocolMode) {
347 if (!sBluetoothHidInterface) return JNI_FALSE;
348
349 ALOGD("%s: protocolMode = %d", __func__, protocolMode);
350
351 jbyte* addr = env->GetByteArrayElements(address, NULL);
352 if (!addr) {
353 ALOGE("Bluetooth device address null");
354 return JNI_FALSE;
355 }
356
357 bthh_protocol_mode_t mode;
358 switch (protocolMode) {
359 case 0:
360 mode = BTHH_REPORT_MODE;
361 break;
362 case 1:
363 mode = BTHH_BOOT_MODE;
364 break;
365 default:
366 ALOGE("Unknown HID protocol mode");
367 return JNI_FALSE;
368 }
369
370 jboolean ret = JNI_TRUE;
371 bt_status_t status =
372 sBluetoothHidInterface->set_protocol((RawAddress*)addr, mode);
373 if (status != BT_STATUS_SUCCESS) {
374 ALOGE("Failed set protocol mode, status: %d", status);
375 ret = JNI_FALSE;
376 }
377 env->ReleaseByteArrayElements(address, addr, 0);
378
379 return ret;
380 }
381
getReportNative(JNIEnv * env,jobject object,jbyteArray address,jbyte reportType,jbyte reportId,jint bufferSize)382 static jboolean getReportNative(JNIEnv* env, jobject object, jbyteArray address,
383 jbyte reportType, jbyte reportId,
384 jint bufferSize) {
385 ALOGV("%s: reportType = %d, reportId = %d, bufferSize = %d", __func__,
386 reportType, reportId, bufferSize);
387 if (!sBluetoothHidInterface) return JNI_FALSE;
388
389 jbyte* addr = env->GetByteArrayElements(address, NULL);
390 if (!addr) {
391 ALOGE("Bluetooth device address null");
392 return JNI_FALSE;
393 }
394
395 jint rType = reportType;
396 jint rId = reportId;
397
398 bt_status_t status = sBluetoothHidInterface->get_report(
399 (RawAddress*)addr, (bthh_report_type_t)rType, (uint8_t)rId, bufferSize);
400 jboolean ret = JNI_TRUE;
401 if (status != BT_STATUS_SUCCESS) {
402 ALOGE("Failed get report, status: %d", status);
403 ret = JNI_FALSE;
404 }
405 env->ReleaseByteArrayElements(address, addr, 0);
406
407 return ret;
408 }
409
setReportNative(JNIEnv * env,jobject object,jbyteArray address,jbyte reportType,jstring report)410 static jboolean setReportNative(JNIEnv* env, jobject object, jbyteArray address,
411 jbyte reportType, jstring report) {
412 ALOGV("%s: reportType = %d", __func__, reportType);
413 if (!sBluetoothHidInterface) return JNI_FALSE;
414
415 jbyte* addr = env->GetByteArrayElements(address, NULL);
416 if (!addr) {
417 ALOGE("Bluetooth device address null");
418 return JNI_FALSE;
419 }
420 jint rType = reportType;
421 const char* c_report = env->GetStringUTFChars(report, NULL);
422
423 jboolean ret = JNI_TRUE;
424 bt_status_t status = sBluetoothHidInterface->set_report(
425 (RawAddress*)addr, (bthh_report_type_t)rType, (char*)c_report);
426 if (status != BT_STATUS_SUCCESS) {
427 ALOGE("Failed set report, status: %d", status);
428 ret = JNI_FALSE;
429 }
430 env->ReleaseStringUTFChars(report, c_report);
431 env->ReleaseByteArrayElements(address, addr, 0);
432
433 return ret;
434 }
435
sendDataNative(JNIEnv * env,jobject object,jbyteArray address,jstring report)436 static jboolean sendDataNative(JNIEnv* env, jobject object, jbyteArray address,
437 jstring report) {
438 ALOGV("%s", __func__);
439 jboolean ret = JNI_TRUE;
440 if (!sBluetoothHidInterface) return JNI_FALSE;
441
442 jbyte* addr = env->GetByteArrayElements(address, NULL);
443 if (!addr) {
444 ALOGE("Bluetooth device address null");
445 return JNI_FALSE;
446 }
447
448 const char* c_report = env->GetStringUTFChars(report, NULL);
449
450 bt_status_t status =
451 sBluetoothHidInterface->send_data((RawAddress*)addr, (char*)c_report);
452 if (status != BT_STATUS_SUCCESS) {
453 ALOGE("Failed set data, status: %d", status);
454 ret = JNI_FALSE;
455 }
456 env->ReleaseStringUTFChars(report, c_report);
457 env->ReleaseByteArrayElements(address, addr, 0);
458
459 return ret;
460 }
461
getIdleTimeNative(JNIEnv * env,jobject object,jbyteArray address)462 static jboolean getIdleTimeNative(JNIEnv* env, jobject object,
463 jbyteArray address) {
464 if (!sBluetoothHidInterface) return JNI_FALSE;
465
466 jbyte* addr = env->GetByteArrayElements(address, NULL);
467 if (!addr) {
468 ALOGE("%s: Bluetooth device address null", __func__);
469 return JNI_FALSE;
470 }
471
472 bt_status_t status = sBluetoothHidInterface->get_idle_time((RawAddress*)addr);
473 if (status != BT_STATUS_SUCCESS) {
474 ALOGE("%s: Failed get idle time, status: %d", __func__, status);
475 }
476 env->ReleaseByteArrayElements(address, addr, 0);
477
478 return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE;
479 }
480
setIdleTimeNative(JNIEnv * env,jobject object,jbyteArray address,jbyte idle_time)481 static jboolean setIdleTimeNative(JNIEnv* env, jobject object,
482 jbyteArray address, jbyte idle_time) {
483 if (!sBluetoothHidInterface) return JNI_FALSE;
484
485 jbyte* addr = env->GetByteArrayElements(address, NULL);
486 if (!addr) {
487 ALOGE("%s: Bluetooth device address null", __func__);
488 return JNI_FALSE;
489 }
490
491 bt_status_t status =
492 sBluetoothHidInterface->set_idle_time((RawAddress*)addr, idle_time);
493 if (status != BT_STATUS_SUCCESS) {
494 ALOGE("%s: Failed set idle time, status: %d", __func__, status);
495 }
496 env->ReleaseByteArrayElements(address, addr, 0);
497
498 return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE;
499 }
500
501 static JNINativeMethod sMethods[] = {
502 {"classInitNative", "()V", (void*)classInitNative},
503 {"initializeNative", "()V", (void*)initializeNative},
504 {"cleanupNative", "()V", (void*)cleanupNative},
505 {"connectHidNative", "([B)Z", (void*)connectHidNative},
506 {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative},
507 {"getProtocolModeNative", "([B)Z", (void*)getProtocolModeNative},
508 {"virtualUnPlugNative", "([B)Z", (void*)virtualUnPlugNative},
509 {"setProtocolModeNative", "([BB)Z", (void*)setProtocolModeNative},
510 {"getReportNative", "([BBBI)Z", (void*)getReportNative},
511 {"setReportNative", "([BBLjava/lang/String;)Z", (void*)setReportNative},
512 {"sendDataNative", "([BLjava/lang/String;)Z", (void*)sendDataNative},
513 {"getIdleTimeNative", "([B)Z", (void*)getIdleTimeNative},
514 {"setIdleTimeNative", "([BB)Z", (void*)setIdleTimeNative},
515 };
516
register_com_android_bluetooth_hid(JNIEnv * env)517 int register_com_android_bluetooth_hid(JNIEnv* env) {
518 return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidService",
519 sMethods, NELEM(sMethods));
520 }
521 }
522