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