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
34namespace android {
35
36static jmethodID method_onConnectStateChanged;
37static jmethodID method_onGetProtocolMode;
38static jmethodID method_onGetReport;
39static jmethodID method_onHandshake;
40static jmethodID method_onVirtualUnplug;
41
42static const bthh_interface_t *sBluetoothHidInterface = NULL;
43static jobject mCallbacksObj = NULL;
44static JNIEnv *sCallbackEnv = NULL;
45
46static 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
60static 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
77static 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
100static 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
134static 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
167static 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
185static 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
198static void classInitNative(JNIEnv* env, jclass clazz) {
199    method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
200    method_onGetProtocolMode = env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
201    method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V");
202    method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V");
203    method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
204
205    ALOGI("%s: succeeds", __FUNCTION__);
206}
207
208static void initializeNative(JNIEnv *env, jobject object) {
209    const bt_interface_t* btInf;
210    bt_status_t status;
211
212    if ( (btInf = getBluetoothInterface()) == NULL) {
213        ALOGE("Bluetooth module is not loaded");
214        return;
215    }
216
217    if (sBluetoothHidInterface !=NULL) {
218        ALOGW("Cleaning up Bluetooth HID Interface before initializing...");
219        sBluetoothHidInterface->cleanup();
220        sBluetoothHidInterface = NULL;
221    }
222
223    if (mCallbacksObj != NULL) {
224        ALOGW("Cleaning up Bluetooth GID callback object");
225        env->DeleteGlobalRef(mCallbacksObj);
226        mCallbacksObj = NULL;
227    }
228
229
230    if ( (sBluetoothHidInterface = (bthh_interface_t *)
231          btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID)) == NULL) {
232        ALOGE("Failed to get Bluetooth HID Interface");
233        return;
234    }
235
236    if ( (status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks)) != BT_STATUS_SUCCESS) {
237        ALOGE("Failed to initialize Bluetooth HID, status: %d", status);
238        sBluetoothHidInterface = NULL;
239        return;
240    }
241
242
243
244    mCallbacksObj = env->NewGlobalRef(object);
245}
246
247static void cleanupNative(JNIEnv *env, jobject object) {
248    const bt_interface_t* btInf;
249
250    if ( (btInf = getBluetoothInterface()) == NULL) {
251        ALOGE("Bluetooth module is not loaded");
252        return;
253    }
254
255    if (sBluetoothHidInterface !=NULL) {
256        ALOGW("Cleaning up Bluetooth HID Interface...");
257        sBluetoothHidInterface->cleanup();
258        sBluetoothHidInterface = NULL;
259    }
260
261    if (mCallbacksObj != NULL) {
262        ALOGW("Cleaning up Bluetooth GID callback object");
263        env->DeleteGlobalRef(mCallbacksObj);
264        mCallbacksObj = NULL;
265    }
266
267    env->DeleteGlobalRef(mCallbacksObj);
268}
269
270static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
271    bt_status_t status;
272    jbyte *addr;
273    jboolean ret = JNI_TRUE;
274    if (!sBluetoothHidInterface) return JNI_FALSE;
275
276    addr = env->GetByteArrayElements(address, NULL);
277    if (!addr) {
278        ALOGE("Bluetooth device address null");
279        return JNI_FALSE;
280    }
281
282    if ((status = sBluetoothHidInterface->connect((bt_bdaddr_t *) addr)) !=
283         BT_STATUS_SUCCESS) {
284        ALOGE("Failed HID channel connection, status: %d", status);
285        ret = JNI_FALSE;
286    }
287    env->ReleaseByteArrayElements(address, addr, 0);
288
289    return ret;
290}
291
292static jboolean disconnectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
293    bt_status_t status;
294    jbyte *addr;
295    jboolean ret = JNI_TRUE;
296    if (!sBluetoothHidInterface) return JNI_FALSE;
297
298    addr = env->GetByteArrayElements(address, NULL);
299    if (!addr) {
300        ALOGE("Bluetooth device address null");
301        return JNI_FALSE;
302    }
303
304    if ( (status = sBluetoothHidInterface->disconnect((bt_bdaddr_t *) addr)) !=
305         BT_STATUS_SUCCESS) {
306        ALOGE("Failed disconnect hid channel, status: %d", status);
307        ret = JNI_FALSE;
308    }
309    env->ReleaseByteArrayElements(address, addr, 0);
310
311    return ret;
312}
313
314static jboolean getProtocolModeNative(JNIEnv *env, jobject object, jbyteArray address) {
315    bt_status_t status;
316    jbyte *addr;
317    jboolean ret = JNI_TRUE;
318    bthh_protocol_mode_t protocolMode;
319    if (!sBluetoothHidInterface) return JNI_FALSE;
320
321    addr = env->GetByteArrayElements(address, NULL);
322    if (!addr) {
323        ALOGE("Bluetooth device address null");
324        return JNI_FALSE;
325    }
326
327    if ( (status = sBluetoothHidInterface->get_protocol((bt_bdaddr_t *) addr, (bthh_protocol_mode_t) protocolMode)) !=
328         BT_STATUS_SUCCESS) {
329        ALOGE("Failed get protocol mode, status: %d", status);
330        ret = JNI_FALSE;
331    }
332    env->ReleaseByteArrayElements(address, addr, 0);
333
334    return ret;
335}
336
337static jboolean virtualUnPlugNative(JNIEnv *env, jobject object, jbyteArray address) {
338    bt_status_t status;
339    jbyte *addr;
340    jboolean ret = JNI_TRUE;
341    if (!sBluetoothHidInterface) return JNI_FALSE;
342
343    addr = env->GetByteArrayElements(address, NULL);
344        if (!addr) {
345            ALOGE("Bluetooth device address null");
346            return JNI_FALSE;
347        }
348    if ( (status = sBluetoothHidInterface->virtual_unplug((bt_bdaddr_t *) addr)) !=
349             BT_STATUS_SUCCESS) {
350        ALOGE("Failed virual unplug, status: %d", status);
351        ret = JNI_FALSE;
352    }
353    env->ReleaseByteArrayElements(address, addr, 0);
354    return ret;
355
356}
357
358
359static jboolean setProtocolModeNative(JNIEnv *env, jobject object, jbyteArray address, jint protocolMode) {
360    bt_status_t status;
361    jbyte *addr;
362    jboolean ret = JNI_TRUE;
363    if (!sBluetoothHidInterface) return JNI_FALSE;
364
365    ALOGD("%s: protocolMode = %d", __FUNCTION__, protocolMode);
366
367    addr = env->GetByteArrayElements(address, NULL);
368    if (!addr) {
369        ALOGE("Bluetooth device address null");
370        return JNI_FALSE;
371    }
372
373    bthh_protocol_mode_t mode;
374    switch(protocolMode){
375        case 0:
376            mode = BTHH_REPORT_MODE;
377            break;
378        case 1:
379            mode = BTHH_BOOT_MODE;
380            break;
381        default:
382            ALOGE("Unknown HID protocol mode");
383            return JNI_FALSE;
384    }
385    if ( (status = sBluetoothHidInterface->set_protocol((bt_bdaddr_t *) addr, mode)) !=
386             BT_STATUS_SUCCESS) {
387        ALOGE("Failed set protocol mode, status: %d", status);
388        ret = JNI_FALSE;
389    }
390    env->ReleaseByteArrayElements(address, addr, 0);
391
392    return JNI_TRUE;
393}
394
395static jboolean getReportNative(JNIEnv *env, jobject object, jbyteArray address, jbyte reportType, jbyte reportId, jint bufferSize) {
396    ALOGV("%s: reportType = %d, reportId = %d, bufferSize = %d", __FUNCTION__, reportType, reportId, bufferSize);
397
398    bt_status_t status;
399    jbyte *addr;
400    jboolean ret = JNI_TRUE;
401    if (!sBluetoothHidInterface) return JNI_FALSE;
402
403    addr = env->GetByteArrayElements(address, NULL);
404    if (!addr) {
405        ALOGE("Bluetooth device address null");
406        return JNI_FALSE;
407    }
408
409    jint rType = reportType;
410    jint rId = reportId;
411
412    if ( (status = sBluetoothHidInterface->get_report((bt_bdaddr_t *) addr, (bthh_report_type_t) rType, (uint8_t) rId, bufferSize)) !=
413             BT_STATUS_SUCCESS) {
414        ALOGE("Failed get report, status: %d", status);
415        ret = JNI_FALSE;
416    }
417    env->ReleaseByteArrayElements(address, addr, 0);
418
419    return ret;
420}
421
422
423static jboolean setReportNative(JNIEnv *env, jobject object, jbyteArray address, jbyte reportType, jstring report) {
424    ALOGV("%s: reportType = %d", __FUNCTION__, reportType);
425    bt_status_t status;
426    jbyte *addr;
427    jboolean ret = JNI_TRUE;
428    if (!sBluetoothHidInterface) return JNI_FALSE;
429
430    addr = env->GetByteArrayElements(address, NULL);
431    if (!addr) {
432        ALOGE("Bluetooth device address null");
433        return JNI_FALSE;
434    }
435    jint rType = reportType;
436    const char *c_report = env->GetStringUTFChars(report, NULL);
437
438    if ( (status = sBluetoothHidInterface->set_report((bt_bdaddr_t *) addr, (bthh_report_type_t)rType, (char*) c_report)) !=
439             BT_STATUS_SUCCESS) {
440        ALOGE("Failed set report, status: %d", status);
441        ret = JNI_FALSE;
442    }
443    env->ReleaseStringUTFChars(report, c_report);
444    env->ReleaseByteArrayElements(address, addr, 0);
445
446    return ret;
447}
448
449static jboolean sendDataNative(JNIEnv *env, jobject object, jbyteArray address, jstring report) {
450    ALOGV("%s", __FUNCTION__);
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    const char *c_report = env->GetStringUTFChars(report, NULL);
462    if ( (status = sBluetoothHidInterface->send_data((bt_bdaddr_t *) addr, (char*) c_report)) !=
463             BT_STATUS_SUCCESS) {
464        ALOGE("Failed set report, status: %d", status);
465        ret = JNI_FALSE;
466    }
467    env->ReleaseStringUTFChars(report, c_report);
468    env->ReleaseByteArrayElements(address, addr, 0);
469
470    return ret;
471
472}
473
474static JNINativeMethod sMethods[] = {
475    {"classInitNative", "()V", (void *) classInitNative},
476    {"initializeNative", "()V", (void *) initializeNative},
477    {"cleanupNative", "()V", (void *) cleanupNative},
478    {"connectHidNative", "([B)Z", (void *) connectHidNative},
479    {"disconnectHidNative", "([B)Z", (void *) disconnectHidNative},
480    {"getProtocolModeNative", "([B)Z", (void *) getProtocolModeNative},
481    {"virtualUnPlugNative", "([B)Z", (void *) virtualUnPlugNative},
482    {"setProtocolModeNative", "([BB)Z", (void *) setProtocolModeNative},
483    {"getReportNative", "([BBBI)Z", (void *) getReportNative},
484    {"setReportNative", "([BBLjava/lang/String;)Z", (void *) setReportNative},
485    {"sendDataNative", "([BLjava/lang/String;)Z", (void *) sendDataNative},
486};
487
488int register_com_android_bluetooth_hid(JNIEnv* env)
489{
490    return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidService",
491                                    sMethods, NELEM(sMethods));
492}
493
494}
495