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