com_android_bluetooth_hid.cpp revision 89d2a16ff98d1b6254139e1589404161d5c419c7
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5#define LOG_TAG "BluetoothHidServiceJni"
6
7#define LOG_NDEBUG 0
8
9#define CHECK_CALLBACK_ENV                                                      \
10   if (!checkCallbackThread()) {                                                \
11       LOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);\
12       return;                                                                  \
13   }
14
15#include "com_android_bluetooth.h"
16#include "hardware/bt_hh.h"
17#include "utils/Log.h"
18#include "android_runtime/AndroidRuntime.h"
19
20#include <string.h>
21
22namespace android {
23
24static jmethodID method_onConnectStateChanged;
25
26static const bthh_interface_t *sBluetoothHidInterface = NULL;
27static jobject mCallbacksObj = NULL;
28static JNIEnv *sCallbackEnv = NULL;
29
30static bool checkCallbackThread() {
31
32    // Always fetch the latest callbackEnv from AdapterService.
33    // Caching this could cause this sCallbackEnv to go out-of-sync
34    // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
35    // is received
36
37    sCallbackEnv = getCallbackEnv();
38
39    JNIEnv* env = AndroidRuntime::getJNIEnv();
40    if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
41    return true;
42}
43
44static void connection_state_callback(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) {
45    jbyteArray addr;
46
47    CHECK_CALLBACK_ENV
48    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
49    if (!addr) {
50        LOGE("Fail to new jbyteArray bd addr for HID channel state");
51        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
52        return;
53    }
54    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
55
56    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr, (jint) state);
57    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
58    sCallbackEnv->DeleteLocalRef(addr);
59}
60
61static bthh_callbacks_t sBluetoothHidCallbacks = {
62    sizeof(sBluetoothHidCallbacks),
63    connection_state_callback,
64    NULL,
65    NULL,
66    NULL,
67    NULL,
68    NULL
69};
70
71// Define native functions
72
73static void classInitNative(JNIEnv* env, jclass clazz) {
74    int err;
75    const bt_interface_t* btInf;
76    bt_status_t status;
77
78    method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged",
79                                                    "([BI)V");
80
81    if ( (btInf = getBluetoothInterface()) == NULL) {
82        LOGE("Bluetooth module is not loaded");
83        return;
84    }
85
86    if ( (sBluetoothHidInterface = (bthh_interface_t *)
87          btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID)) == NULL) {
88        LOGE("Failed to get Bluetooth Handsfree Interface");
89        return;
90    }
91
92    // TODO(BT) do this only once or
93    //          Do we need to do this every time the BT reenables?
94    if ( (status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks)) != BT_STATUS_SUCCESS) {
95        LOGE("Failed to initialize Bluetooth HID, status: %d", status);
96        sBluetoothHidInterface = NULL;
97        return;
98    }
99
100    LOGI("%s: succeeds", __FUNCTION__);
101}
102
103static void initializeNativeDataNative(JNIEnv *env, jobject object) {
104    // TODO(BT) clean it up when hid service is stopped
105    mCallbacksObj = env->NewGlobalRef(object);
106}
107
108static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
109    bt_status_t status;
110    jbyte *addr;
111    jboolean ret = JNI_TRUE;
112    if (!sBluetoothHidInterface) return JNI_FALSE;
113
114    addr = env->GetByteArrayElements(address, NULL);
115    if (!addr) {
116        LOGE("Bluetooth device address null");
117        return JNI_FALSE;
118    }
119
120    if ((status = sBluetoothHidInterface->connect((bt_bdaddr_t *) addr)) !=
121         BT_STATUS_SUCCESS) {
122        LOGE("Failed HID channel connection, status: %d", status);
123        ret = JNI_FALSE;
124    }
125    env->ReleaseByteArrayElements(address, addr, 0);
126
127    return ret;
128}
129
130static jboolean disconnectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
131    bt_status_t status;
132    jbyte *addr;
133    jboolean ret = JNI_TRUE;
134    if (!sBluetoothHidInterface) return JNI_FALSE;
135
136    addr = env->GetByteArrayElements(address, NULL);
137    if (!addr) {
138        LOGE("Bluetooth device address null");
139        return JNI_FALSE;
140    }
141
142    if ( (status = sBluetoothHidInterface->disconnect((bt_bdaddr_t *) addr)) !=
143         BT_STATUS_SUCCESS) {
144        LOGE("Failed disconnect hid channel, status: %d", status);
145        ret = JNI_FALSE;
146    }
147    env->ReleaseByteArrayElements(address, addr, 0);
148
149    return ret;
150}
151
152static JNINativeMethod sMethods[] = {
153    {"classInitNative", "()V", (void *) classInitNative},
154    {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
155    {"connectHidNative", "([B)Z", (void *) connectHidNative},
156    {"disconnectHidNative", "([B)Z", (void *) disconnectHidNative},
157    // TBD
158};
159
160int register_com_android_bluetooth_hid(JNIEnv* env)
161{
162    return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidService",
163                                    sMethods, NELEM(sMethods));
164}
165
166}
167