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
34namespace android {
35
36static jmethodID method_onConnectStateChanged;
37static jmethodID method_onGetProtocolMode;
38static jmethodID method_onGetReport;
39static jmethodID method_onVirtualUnplug;
40
41static const bthh_interface_t *sBluetoothHidInterface = NULL;
42static jobject mCallbacksObj = NULL;
43static JNIEnv *sCallbackEnv = NULL;
44
45static 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
59static 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
76static 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
99static 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
133static 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
145static 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
178static 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
217static 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
241static 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
263static 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
285static 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
308static 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
330static 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
366static 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
394static 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
420static 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
445static 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
459int 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