• 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 #include "android_runtime/AndroidRuntime.h"
22 #include "com_android_bluetooth.h"
23 #include "hardware/bt_hh.h"
24 #include "utils/Log.h"
25 
26 #include <string.h>
27 
28 namespace android {
29 
30 static jmethodID method_onConnectStateChanged;
31 static jmethodID method_onGetProtocolMode;
32 static jmethodID method_onGetReport;
33 static jmethodID method_onHandshake;
34 static jmethodID method_onVirtualUnplug;
35 
36 static const bthh_interface_t* sBluetoothHidInterface = NULL;
37 static jobject mCallbacksObj = NULL;
38 
marshall_bda(bt_bdaddr_t * bd_addr)39 static jbyteArray marshall_bda(bt_bdaddr_t* bd_addr) {
40   CallbackEnv sCallbackEnv(__func__);
41   if (!sCallbackEnv.valid()) return NULL;
42 
43   jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
44   if (!addr) {
45     ALOGE("Fail to new jbyteArray bd addr");
46     return NULL;
47   }
48   sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
49                                    (jbyte*)bd_addr);
50   return addr;
51 }
52 
connection_state_callback(bt_bdaddr_t * bd_addr,bthh_connection_state_t state)53 static void connection_state_callback(bt_bdaddr_t* bd_addr,
54                                       bthh_connection_state_t state) {
55   CallbackEnv sCallbackEnv(__func__);
56   if (!sCallbackEnv.valid()) return;
57   if (!mCallbacksObj) {
58     ALOGE("%s: mCallbacksObj is null", __func__);
59     return;
60   }
61   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
62   if (!addr.get()) {
63     ALOGE("Fail to new jbyteArray bd addr for HID channel state");
64     return;
65   }
66 
67   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged,
68                                addr.get(), (jint)state);
69 }
70 
get_protocol_mode_callback(bt_bdaddr_t * bd_addr,bthh_status_t hh_status,bthh_protocol_mode_t mode)71 static void get_protocol_mode_callback(bt_bdaddr_t* bd_addr,
72                                        bthh_status_t hh_status,
73                                        bthh_protocol_mode_t mode) {
74   CallbackEnv sCallbackEnv(__func__);
75   if (!sCallbackEnv.valid()) return;
76   if (!mCallbacksObj) {
77     ALOGE("%s: mCallbacksObj is null", __func__);
78     return;
79   }
80   if (hh_status != BTHH_OK) {
81     ALOGE("BTHH Status is not OK!");
82     return;
83   }
84 
85   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
86   if (!addr.get()) {
87     ALOGE("Fail to new jbyteArray bd addr for get protocal mode callback");
88     return;
89   }
90 
91   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetProtocolMode,
92                                addr.get(), (jint)mode);
93 }
94 
get_report_callback(bt_bdaddr_t * bd_addr,bthh_status_t hh_status,uint8_t * rpt_data,int rpt_size)95 static void get_report_callback(bt_bdaddr_t* bd_addr, bthh_status_t hh_status,
96                                 uint8_t* rpt_data, int rpt_size) {
97   CallbackEnv sCallbackEnv(__func__);
98   if (!sCallbackEnv.valid()) return;
99   if (!mCallbacksObj) {
100     ALOGE("%s: mCallbacksObj is null", __func__);
101     return;
102   }
103   if (hh_status != BTHH_OK) {
104     ALOGE("BTHH Status is not OK!");
105     return;
106   }
107 
108   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
109   if (!addr.get()) {
110     ALOGE("Fail to new jbyteArray bd addr for get report callback");
111     return;
112   }
113   ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(),
114                                   sCallbackEnv->NewByteArray(rpt_size));
115   if (!data.get()) {
116     ALOGE("Fail to new jbyteArray data for get report callback");
117     return;
118   }
119 
120   sCallbackEnv->SetByteArrayRegion(data.get(), 0, rpt_size, (jbyte*)rpt_data);
121   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, addr.get(),
122                                data.get(), (jint)rpt_size);
123 }
124 
virtual_unplug_callback(bt_bdaddr_t * bd_addr,bthh_status_t hh_status)125 static void virtual_unplug_callback(bt_bdaddr_t* bd_addr,
126                                     bthh_status_t hh_status) {
127   ALOGV("call to virtual_unplug_callback");
128   CallbackEnv sCallbackEnv(__func__);
129   if (!sCallbackEnv.valid()) return;
130   if (!mCallbacksObj) {
131     ALOGE("%s: mCallbacksObj is null", __func__);
132     return;
133   }
134   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
135   if (!addr.get()) {
136     ALOGE("Fail to new jbyteArray bd addr for HID channel state");
137     return;
138   }
139   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualUnplug,
140                                addr.get(), (jint)hh_status);
141 }
142 
handshake_callback(bt_bdaddr_t * bd_addr,bthh_status_t hh_status)143 static void handshake_callback(bt_bdaddr_t* bd_addr, bthh_status_t hh_status) {
144   CallbackEnv sCallbackEnv(__func__);
145   if (!sCallbackEnv.valid()) return;
146   if (!mCallbacksObj) {
147     ALOGE("%s: mCallbacksObj is null", __func__);
148     return;
149   }
150 
151   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
152   if (!addr.get()) {
153     ALOGE("Fail to new jbyteArray bd addr for handshake callback");
154     return;
155   }
156   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHandshake, addr.get(),
157                                (jint)hh_status);
158 }
159 
160 static bthh_callbacks_t sBluetoothHidCallbacks = {
161     sizeof(sBluetoothHidCallbacks),
162     connection_state_callback,
163     NULL,
164     get_protocol_mode_callback,
165     NULL,
166     get_report_callback,
167     virtual_unplug_callback,
168     handshake_callback};
169 
170 // Define native functions
171 
classInitNative(JNIEnv * env,jclass clazz)172 static void classInitNative(JNIEnv* env, jclass clazz) {
173   method_onConnectStateChanged =
174       env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
175   method_onGetProtocolMode =
176       env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
177   method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V");
178   method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V");
179   method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
180 
181   ALOGI("%s: succeeds", __func__);
182 }
183 
initializeNative(JNIEnv * env,jobject object)184 static void initializeNative(JNIEnv* env, jobject object) {
185   const bt_interface_t* btInf = getBluetoothInterface();
186   if (btInf == NULL) {
187     ALOGE("Bluetooth module is not loaded");
188     return;
189   }
190 
191   if (sBluetoothHidInterface != NULL) {
192     ALOGW("Cleaning up Bluetooth HID Interface before initializing...");
193     sBluetoothHidInterface->cleanup();
194     sBluetoothHidInterface = NULL;
195   }
196 
197   if (mCallbacksObj != NULL) {
198     ALOGW("Cleaning up Bluetooth GID callback object");
199     env->DeleteGlobalRef(mCallbacksObj);
200     mCallbacksObj = NULL;
201   }
202 
203   sBluetoothHidInterface =
204       (bthh_interface_t*)btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID);
205   if (sBluetoothHidInterface == NULL) {
206     ALOGE("Failed to get Bluetooth HID Interface");
207     return;
208   }
209 
210   bt_status_t status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks);
211   if (status != BT_STATUS_SUCCESS) {
212     ALOGE("Failed to initialize Bluetooth HID, status: %d", status);
213     sBluetoothHidInterface = NULL;
214     return;
215   }
216 
217   mCallbacksObj = env->NewGlobalRef(object);
218 }
219 
cleanupNative(JNIEnv * env,jobject object)220 static void cleanupNative(JNIEnv* env, jobject object) {
221   const bt_interface_t* btInf = getBluetoothInterface();
222 
223   if (btInf == NULL) {
224     ALOGE("Bluetooth module is not loaded");
225     return;
226   }
227 
228   if (sBluetoothHidInterface != NULL) {
229     ALOGW("Cleaning up Bluetooth HID Interface...");
230     sBluetoothHidInterface->cleanup();
231     sBluetoothHidInterface = NULL;
232   }
233 
234   if (mCallbacksObj != NULL) {
235     ALOGW("Cleaning up Bluetooth GID callback object");
236     env->DeleteGlobalRef(mCallbacksObj);
237     mCallbacksObj = NULL;
238   }
239 }
240 
connectHidNative(JNIEnv * env,jobject object,jbyteArray address)241 static jboolean connectHidNative(JNIEnv* env, jobject object,
242                                  jbyteArray address) {
243   if (!sBluetoothHidInterface) return JNI_FALSE;
244 
245   jbyte* addr = env->GetByteArrayElements(address, NULL);
246   if (!addr) {
247     ALOGE("Bluetooth device address null");
248     return JNI_FALSE;
249   }
250 
251   jboolean ret = JNI_TRUE;
252   bt_status_t status = sBluetoothHidInterface->connect((bt_bdaddr_t*)addr);
253   if (status != BT_STATUS_SUCCESS) {
254     ALOGE("Failed HID channel connection, status: %d", status);
255     ret = JNI_FALSE;
256   }
257   env->ReleaseByteArrayElements(address, addr, 0);
258 
259   return ret;
260 }
261 
disconnectHidNative(JNIEnv * env,jobject object,jbyteArray address)262 static jboolean disconnectHidNative(JNIEnv* env, jobject object,
263                                     jbyteArray address) {
264   jbyte* addr;
265   jboolean ret = JNI_TRUE;
266   if (!sBluetoothHidInterface) return JNI_FALSE;
267 
268   addr = env->GetByteArrayElements(address, NULL);
269   if (!addr) {
270     ALOGE("Bluetooth device address null");
271     return JNI_FALSE;
272   }
273 
274   bt_status_t status = sBluetoothHidInterface->disconnect((bt_bdaddr_t*)addr);
275   if (status != BT_STATUS_SUCCESS) {
276     ALOGE("Failed disconnect hid channel, status: %d", status);
277     ret = JNI_FALSE;
278   }
279   env->ReleaseByteArrayElements(address, addr, 0);
280 
281   return ret;
282 }
283 
getProtocolModeNative(JNIEnv * env,jobject object,jbyteArray address)284 static jboolean getProtocolModeNative(JNIEnv* env, jobject object,
285                                       jbyteArray address) {
286   if (!sBluetoothHidInterface) return JNI_FALSE;
287 
288   jbyte* addr = env->GetByteArrayElements(address, NULL);
289   if (!addr) {
290     ALOGE("Bluetooth device address null");
291     return JNI_FALSE;
292   }
293 
294   jboolean ret = JNI_TRUE;
295   // TODO: protocolMode is unused by the backend: see b/28908173
296   bthh_protocol_mode_t protocolMode = BTHH_UNSUPPORTED_MODE;
297   bt_status_t status = sBluetoothHidInterface->get_protocol(
298       (bt_bdaddr_t*)addr, (bthh_protocol_mode_t)protocolMode);
299   if (status != 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,
309                                     jbyteArray address) {
310   if (!sBluetoothHidInterface) return JNI_FALSE;
311 
312   jbyte* addr = env->GetByteArrayElements(address, NULL);
313   if (!addr) {
314     ALOGE("Bluetooth device address null");
315     return JNI_FALSE;
316   }
317 
318   jboolean ret = JNI_TRUE;
319   bt_status_t status =
320       sBluetoothHidInterface->virtual_unplug((bt_bdaddr_t*)addr);
321   if (status != BT_STATUS_SUCCESS) {
322     ALOGE("Failed virual unplug, status: %d", status);
323     ret = JNI_FALSE;
324   }
325   env->ReleaseByteArrayElements(address, addr, 0);
326   return ret;
327 }
328 
setProtocolModeNative(JNIEnv * env,jobject object,jbyteArray address,jint protocolMode)329 static jboolean setProtocolModeNative(JNIEnv* env, jobject object,
330                                       jbyteArray address, jint protocolMode) {
331   if (!sBluetoothHidInterface) return JNI_FALSE;
332 
333   ALOGD("%s: protocolMode = %d", __func__, protocolMode);
334 
335   jbyte* addr = env->GetByteArrayElements(address, NULL);
336   if (!addr) {
337     ALOGE("Bluetooth device address null");
338     return JNI_FALSE;
339   }
340 
341   bthh_protocol_mode_t mode;
342   switch (protocolMode) {
343     case 0:
344       mode = BTHH_REPORT_MODE;
345       break;
346     case 1:
347       mode = BTHH_BOOT_MODE;
348       break;
349     default:
350       ALOGE("Unknown HID protocol mode");
351       return JNI_FALSE;
352   }
353 
354   jboolean ret = JNI_TRUE;
355   bt_status_t status =
356       sBluetoothHidInterface->set_protocol((bt_bdaddr_t*)addr, mode);
357   if (status != 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 ret;
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,
367                                 jbyte reportType, jbyte reportId,
368                                 jint bufferSize) {
369   ALOGV("%s: reportType = %d, reportId = %d, bufferSize = %d", __func__,
370         reportType, reportId, bufferSize);
371   if (!sBluetoothHidInterface) return JNI_FALSE;
372 
373   jbyte* addr = env->GetByteArrayElements(address, NULL);
374   if (!addr) {
375     ALOGE("Bluetooth device address null");
376     return JNI_FALSE;
377   }
378 
379   jint rType = reportType;
380   jint rId = reportId;
381 
382   bt_status_t status = sBluetoothHidInterface->get_report(
383       (bt_bdaddr_t*)addr, (bthh_report_type_t)rType, (uint8_t)rId, bufferSize);
384   jboolean ret = JNI_TRUE;
385   if (status != BT_STATUS_SUCCESS) {
386     ALOGE("Failed get report, status: %d", status);
387     ret = JNI_FALSE;
388   }
389   env->ReleaseByteArrayElements(address, addr, 0);
390 
391   return ret;
392 }
393 
setReportNative(JNIEnv * env,jobject object,jbyteArray address,jbyte reportType,jstring report)394 static jboolean setReportNative(JNIEnv* env, jobject object, jbyteArray address,
395                                 jbyte reportType, jstring report) {
396   ALOGV("%s: reportType = %d", __func__, reportType);
397   if (!sBluetoothHidInterface) return JNI_FALSE;
398 
399   jbyte* addr = env->GetByteArrayElements(address, NULL);
400   if (!addr) {
401     ALOGE("Bluetooth device address null");
402     return JNI_FALSE;
403   }
404   jint rType = reportType;
405   const char* c_report = env->GetStringUTFChars(report, NULL);
406 
407   jboolean ret = JNI_TRUE;
408   bt_status_t status = sBluetoothHidInterface->set_report(
409       (bt_bdaddr_t*)addr, (bthh_report_type_t)rType, (char*)c_report);
410   if (status != 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,
421                                jstring report) {
422   ALOGV("%s", __func__);
423   jboolean ret = JNI_TRUE;
424   if (!sBluetoothHidInterface) return JNI_FALSE;
425 
426   jbyte* addr = env->GetByteArrayElements(address, NULL);
427   if (!addr) {
428     ALOGE("Bluetooth device address null");
429     return JNI_FALSE;
430   }
431 
432   const char* c_report = env->GetStringUTFChars(report, NULL);
433 
434   bt_status_t status =
435       sBluetoothHidInterface->send_data((bt_bdaddr_t*)addr, (char*)c_report);
436   if (status != BT_STATUS_SUCCESS) {
437     ALOGE("Failed set report, status: %d", status);
438     ret = JNI_FALSE;
439   }
440   env->ReleaseStringUTFChars(report, c_report);
441   env->ReleaseByteArrayElements(address, addr, 0);
442 
443   return ret;
444 }
445 
446 static JNINativeMethod sMethods[] = {
447     {"classInitNative", "()V", (void*)classInitNative},
448     {"initializeNative", "()V", (void*)initializeNative},
449     {"cleanupNative", "()V", (void*)cleanupNative},
450     {"connectHidNative", "([B)Z", (void*)connectHidNative},
451     {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative},
452     {"getProtocolModeNative", "([B)Z", (void*)getProtocolModeNative},
453     {"virtualUnPlugNative", "([B)Z", (void*)virtualUnPlugNative},
454     {"setProtocolModeNative", "([BB)Z", (void*)setProtocolModeNative},
455     {"getReportNative", "([BBBI)Z", (void*)getReportNative},
456     {"setReportNative", "([BBLjava/lang/String;)Z", (void*)setReportNative},
457     {"sendDataNative", "([BLjava/lang/String;)Z", (void*)sendDataNative},
458 };
459 
register_com_android_bluetooth_hid(JNIEnv * env)460 int register_com_android_bluetooth_hid(JNIEnv* env) {
461   return jniRegisterNativeMethods(env, "com/android/bluetooth/hid/HidService",
462                                   sMethods, NELEM(sMethods));
463 }
464 }
465