• 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 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