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