1 /*
2  * Copyright (C) 2016 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 "BluetoothHidDeviceServiceJni"
18 
19 #include <string.h>
20 
21 #include "com_android_bluetooth.h"
22 #include "hardware/bt_hd.h"
23 
24 namespace android {
25 
26 static jmethodID method_onApplicationStateChanged;
27 static jmethodID method_onConnectStateChanged;
28 static jmethodID method_onGetReport;
29 static jmethodID method_onSetReport;
30 static jmethodID method_onSetProtocol;
31 static jmethodID method_onInterruptData;
32 static jmethodID method_onVirtualCableUnplug;
33 
34 static const bthd_interface_t* sHiddIf = NULL;
35 static jobject mCallbacksObj = NULL;
36 
marshall_bda(RawAddress * bd_addr)37 static jbyteArray marshall_bda(RawAddress* bd_addr) {
38   CallbackEnv sCallbackEnv(__func__);
39   if (!sCallbackEnv.valid()) return NULL;
40 
41   jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress));
42   if (!addr) {
43     log::error("Fail to new jbyteArray bd addr");
44     return NULL;
45   }
46   sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress),
47                                    (jbyte*)bd_addr);
48   return addr;
49 }
50 
application_state_callback(RawAddress * bd_addr,bthd_application_state_t state)51 static void application_state_callback(RawAddress* bd_addr,
52                                        bthd_application_state_t state) {
53   jboolean registered = JNI_FALSE;
54 
55   CallbackEnv sCallbackEnv(__func__);
56 
57   if (state == BTHD_APP_STATE_REGISTERED) {
58     registered = JNI_TRUE;
59   }
60 
61   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), NULL);
62 
63   if (bd_addr) {
64     addr.reset(marshall_bda(bd_addr));
65     if (!addr.get()) {
66       log::error("failed to allocate storage for bt_addr");
67       return;
68     }
69   }
70 
71   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onApplicationStateChanged,
72                                addr.get(), registered);
73 }
74 
connection_state_callback(RawAddress * bd_addr,bthd_connection_state_t state)75 static void connection_state_callback(RawAddress* bd_addr,
76                                       bthd_connection_state_t state) {
77   CallbackEnv sCallbackEnv(__func__);
78 
79   ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
80   if (!addr.get()) {
81     log::error("failed to allocate storage for bt_addr");
82     return;
83   }
84 
85   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged,
86                                addr.get(), (jint)state);
87 }
88 
get_report_callback(uint8_t type,uint8_t id,uint16_t buffer_size)89 static void get_report_callback(uint8_t type, uint8_t id,
90                                 uint16_t buffer_size) {
91   CallbackEnv sCallbackEnv(__func__);
92 
93   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetReport, type, id,
94                                buffer_size);
95 }
96 
set_report_callback(uint8_t type,uint8_t id,uint16_t len,uint8_t * p_data)97 static void set_report_callback(uint8_t type, uint8_t id, uint16_t len,
98                                 uint8_t* p_data) {
99   CallbackEnv sCallbackEnv(__func__);
100 
101   ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(),
102                                   sCallbackEnv->NewByteArray(len));
103   if (!data.get()) {
104     log::error("failed to allocate storage for report data");
105     return;
106   }
107   sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data);
108 
109   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetReport, (jbyte)type,
110                                (jbyte)id, data.get());
111 }
112 
set_protocol_callback(uint8_t protocol)113 static void set_protocol_callback(uint8_t protocol) {
114   CallbackEnv sCallbackEnv(__func__);
115 
116   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSetProtocol, protocol);
117 }
118 
intr_data_callback(uint8_t report_id,uint16_t len,uint8_t * p_data)119 static void intr_data_callback(uint8_t report_id, uint16_t len,
120                                uint8_t* p_data) {
121   CallbackEnv sCallbackEnv(__func__);
122 
123   ScopedLocalRef<jbyteArray> data(sCallbackEnv.get(),
124                                   sCallbackEnv->NewByteArray(len));
125   if (!data.get()) {
126     log::error("failed to allocate storage for report data");
127     return;
128   }
129   sCallbackEnv->SetByteArrayRegion(data.get(), 0, len, (jbyte*)p_data);
130 
131   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onInterruptData,
132                                (jbyte)report_id, data.get());
133 }
134 
vc_unplug_callback(void)135 static void vc_unplug_callback(void) {
136   CallbackEnv sCallbackEnv(__func__);
137   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVirtualCableUnplug);
138 }
139 
140 static bthd_callbacks_t sHiddCb = {
141     sizeof(sHiddCb),
142 
143     application_state_callback,
144     connection_state_callback,
145     get_report_callback,
146     set_report_callback,
147     set_protocol_callback,
148     intr_data_callback,
149     vc_unplug_callback,
150 };
151 
initNative(JNIEnv * env,jobject object)152 static void initNative(JNIEnv* env, jobject object) {
153   const bt_interface_t* btif;
154   bt_status_t status;
155 
156   log::verbose("enter");
157 
158   if ((btif = getBluetoothInterface()) == NULL) {
159     log::error("Cannot obtain BT interface");
160     return;
161   }
162 
163   if (sHiddIf != NULL) {
164     log::warn("Cleaning up interface");
165     sHiddIf->cleanup();
166     sHiddIf = NULL;
167   }
168 
169   if (mCallbacksObj != NULL) {
170     log::warn("Cleaning up callback object");
171     env->DeleteGlobalRef(mCallbacksObj);
172     mCallbacksObj = NULL;
173   }
174 
175   if ((sHiddIf = (bthd_interface_t*)btif->get_profile_interface(
176            BT_PROFILE_HIDDEV_ID)) == NULL) {
177     log::error("Cannot obtain interface");
178     return;
179   }
180 
181   if ((status = sHiddIf->init(&sHiddCb)) != BT_STATUS_SUCCESS) {
182     log::error("Failed to initialize interface ({})", bt_status_text(status));
183     sHiddIf = NULL;
184     return;
185   }
186 
187   mCallbacksObj = env->NewGlobalRef(object);
188 
189   log::verbose("done");
190 }
191 
cleanupNative(JNIEnv * env,jobject)192 static void cleanupNative(JNIEnv* env, jobject /* object */) {
193   log::verbose("enter");
194 
195   if (sHiddIf != NULL) {
196     log::info("Cleaning up interface");
197     sHiddIf->cleanup();
198     sHiddIf = NULL;
199   }
200 
201   if (mCallbacksObj != NULL) {
202     log::info("Cleaning up callback object");
203     env->DeleteGlobalRef(mCallbacksObj);
204     mCallbacksObj = NULL;
205   }
206 
207   log::verbose("done");
208 }
209 
fill_qos(JNIEnv * env,jintArray in,bthd_qos_param_t * out)210 static void fill_qos(JNIEnv* env, jintArray in, bthd_qos_param_t* out) {
211   // set default values
212   out->service_type = 0x01;  // best effort
213   out->token_rate = out->token_bucket_size = out->peak_bandwidth =
214       0;                                                    // don't care
215   out->access_latency = out->delay_variation = 0xffffffff;  // don't care
216 
217   if (in == NULL) return;
218 
219   jsize len = env->GetArrayLength(in);
220 
221   if (len != 6) return;
222 
223   uint32_t* buf = (uint32_t*)calloc(len, sizeof(uint32_t));
224 
225   if (buf == NULL) return;
226 
227   env->GetIntArrayRegion(in, 0, len, (jint*)buf);
228 
229   out->service_type = (uint8_t)buf[0];
230   out->token_rate = buf[1];
231   out->token_bucket_size = buf[2];
232   out->peak_bandwidth = buf[3];
233   out->access_latency = buf[4];
234   out->delay_variation = buf[5];
235 
236   free(buf);
237 }
238 
registerAppNative(JNIEnv * env,jobject,jstring name,jstring description,jstring provider,jbyte subclass,jbyteArray descriptors,jintArray p_in_qos,jintArray p_out_qos)239 static jboolean registerAppNative(JNIEnv* env, jobject /* thiz */, jstring name,
240                                   jstring description, jstring provider,
241                                   jbyte subclass, jbyteArray descriptors,
242                                   jintArray p_in_qos, jintArray p_out_qos) {
243   log::verbose("enter");
244 
245   if (!sHiddIf) {
246     log::error("Failed to get the Bluetooth HIDD Interface");
247     return JNI_FALSE;
248   }
249 
250   jboolean result = JNI_FALSE;
251   bthd_app_param_t app_param;
252   bthd_qos_param_t in_qos;
253   bthd_qos_param_t out_qos;
254   jsize size;
255   uint8_t* data;
256 
257   size = env->GetArrayLength(descriptors);
258   data = (uint8_t*)malloc(size);
259 
260   if (data != NULL) {
261     env->GetByteArrayRegion(descriptors, 0, size, (jbyte*)data);
262 
263     app_param.name = env->GetStringUTFChars(name, NULL);
264     app_param.description = env->GetStringUTFChars(description, NULL);
265     app_param.provider = env->GetStringUTFChars(provider, NULL);
266     app_param.subclass = subclass;
267     app_param.desc_list = data;
268     app_param.desc_list_len = size;
269 
270     fill_qos(env, p_in_qos, &in_qos);
271     fill_qos(env, p_out_qos, &out_qos);
272 
273     bt_status_t ret = sHiddIf->register_app(&app_param, &in_qos, &out_qos);
274 
275     log::verbose("register_app() returned {}", bt_status_text(ret));
276 
277     if (ret == BT_STATUS_SUCCESS) {
278       result = JNI_TRUE;
279     }
280 
281     env->ReleaseStringUTFChars(name, app_param.name);
282     env->ReleaseStringUTFChars(description, app_param.description);
283     env->ReleaseStringUTFChars(provider, app_param.provider);
284 
285     free(data);
286   }
287 
288   log::verbose("done ({})", result);
289 
290   return result;
291 }
292 
unregisterAppNative(JNIEnv *,jobject)293 static jboolean unregisterAppNative(JNIEnv* /* env */, jobject /* thiz */) {
294   log::verbose("enter");
295 
296   jboolean result = JNI_FALSE;
297 
298   if (!sHiddIf) {
299     log::error("Failed to get the Bluetooth HIDD Interface");
300     return JNI_FALSE;
301   }
302 
303   bt_status_t ret = sHiddIf->unregister_app();
304 
305   log::verbose("unregister_app() returned {}", bt_status_text(ret));
306 
307   if (ret == BT_STATUS_SUCCESS) {
308     result = JNI_TRUE;
309   }
310 
311   log::verbose("done ({})", result);
312 
313   return result;
314 }
315 
sendReportNative(JNIEnv * env,jobject,jint id,jbyteArray data)316 static jboolean sendReportNative(JNIEnv* env, jobject /* thiz */, jint id,
317                                  jbyteArray data) {
318   jboolean result = JNI_FALSE;
319 
320   if (!sHiddIf) {
321     log::error("Failed to get the Bluetooth HIDD Interface");
322     return JNI_FALSE;
323   }
324 
325   jsize size;
326   uint8_t* buf;
327 
328   size = env->GetArrayLength(data);
329   buf = (uint8_t*)malloc(size);
330 
331   if (buf != NULL) {
332     env->GetByteArrayRegion(data, 0, size, (jbyte*)buf);
333 
334     bt_status_t ret =
335         sHiddIf->send_report(BTHD_REPORT_TYPE_INTRDATA, id, size, buf);
336 
337     if (ret == BT_STATUS_SUCCESS) {
338       result = JNI_TRUE;
339     }
340 
341     free(buf);
342   }
343 
344   return result;
345 }
346 
replyReportNative(JNIEnv * env,jobject,jbyte type,jbyte id,jbyteArray data)347 static jboolean replyReportNative(JNIEnv* env, jobject /* thiz */, jbyte type,
348                                   jbyte id, jbyteArray data) {
349   log::verbose("enter");
350 
351   if (!sHiddIf) {
352     log::error("Failed to get the Bluetooth HIDD Interface");
353     return JNI_FALSE;
354   }
355 
356   jboolean result = JNI_FALSE;
357   jsize size;
358   uint8_t* buf;
359 
360   size = env->GetArrayLength(data);
361   buf = (uint8_t*)malloc(size);
362 
363   if (buf != NULL) {
364     int report_type = (type & 0x03);
365     env->GetByteArrayRegion(data, 0, size, (jbyte*)buf);
366 
367     bt_status_t ret =
368         sHiddIf->send_report((bthd_report_type_t)report_type, id, size, buf);
369 
370     log::verbose("send_report() returned {}", bt_status_text(ret));
371 
372     if (ret == BT_STATUS_SUCCESS) {
373       result = JNI_TRUE;
374     }
375 
376     free(buf);
377   }
378 
379   log::verbose("done ({})", result);
380 
381   return result;
382 }
383 
reportErrorNative(JNIEnv *,jobject,jbyte error)384 static jboolean reportErrorNative(JNIEnv* /* env */, jobject /* thiz */,
385                                   jbyte error) {
386   log::verbose("enter");
387 
388   if (!sHiddIf) {
389     log::error("Failed to get the Bluetooth HIDD Interface");
390     return JNI_FALSE;
391   }
392 
393   jboolean result = JNI_FALSE;
394 
395   bt_status_t ret = sHiddIf->report_error(error);
396 
397   log::verbose("report_error() returned {}", bt_status_text(ret));
398 
399   if (ret == BT_STATUS_SUCCESS) {
400     result = JNI_TRUE;
401   }
402 
403   log::verbose("done ({})", result);
404 
405   return result;
406 }
407 
unplugNative(JNIEnv *,jobject)408 static jboolean unplugNative(JNIEnv* /* env */, jobject /* thiz */) {
409   log::verbose("enter");
410 
411   if (!sHiddIf) {
412     log::error("Failed to get the Bluetooth HIDD Interface");
413     return JNI_FALSE;
414   }
415 
416   jboolean result = JNI_FALSE;
417 
418   bt_status_t ret = sHiddIf->virtual_cable_unplug();
419 
420   log::verbose("virtual_cable_unplug() returned {}", bt_status_text(ret));
421 
422   if (ret == BT_STATUS_SUCCESS) {
423     result = JNI_TRUE;
424   }
425 
426   log::verbose("done ({})", result);
427 
428   return result;
429 }
430 
connectNative(JNIEnv * env,jobject,jbyteArray address)431 static jboolean connectNative(JNIEnv* env, jobject /* thiz */,
432                               jbyteArray address) {
433   log::verbose("enter");
434 
435   if (!sHiddIf) {
436     log::error("Failed to get the Bluetooth HIDD Interface");
437     return JNI_FALSE;
438   }
439 
440   jboolean result = JNI_FALSE;
441 
442   jbyte* addr = env->GetByteArrayElements(address, NULL);
443   if (!addr) {
444     log::error("Bluetooth device address null");
445     return JNI_FALSE;
446   }
447 
448   bt_status_t ret = sHiddIf->connect((RawAddress*)addr);
449 
450   log::verbose("connect() returned {}", bt_status_text(ret));
451 
452   if (ret == BT_STATUS_SUCCESS) {
453     result = JNI_TRUE;
454   }
455 
456   log::verbose("done ({})", result);
457 
458   return result;
459 }
460 
disconnectNative(JNIEnv *,jobject)461 static jboolean disconnectNative(JNIEnv* /* env */, jobject /* thiz */) {
462   log::verbose("enter");
463 
464   if (!sHiddIf) {
465     log::error("Failed to get the Bluetooth HIDD Interface");
466     return JNI_FALSE;
467   }
468 
469   jboolean result = JNI_FALSE;
470 
471   bt_status_t ret = sHiddIf->disconnect();
472 
473   log::verbose("disconnect() returned {}", bt_status_text(ret));
474 
475   if (ret == BT_STATUS_SUCCESS) {
476     result = JNI_TRUE;
477   }
478 
479   log::verbose("done ({})", result);
480 
481   return result;
482 }
483 
register_com_android_bluetooth_hid_device(JNIEnv * env)484 int register_com_android_bluetooth_hid_device(JNIEnv* env) {
485   const JNINativeMethod methods[] = {
486       {"initNative", "()V", (void*)initNative},
487       {"cleanupNative", "()V", (void*)cleanupNative},
488       {"registerAppNative",
489        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z",
490        (void*)registerAppNative},
491       {"unregisterAppNative", "()Z", (void*)unregisterAppNative},
492       {"sendReportNative", "(I[B)Z", (void*)sendReportNative},
493       {"replyReportNative", "(BB[B)Z", (void*)replyReportNative},
494       {"reportErrorNative", "(B)Z", (void*)reportErrorNative},
495       {"unplugNative", "()Z", (void*)unplugNative},
496       {"connectNative", "([B)Z", (void*)connectNative},
497       {"disconnectNative", "()Z", (void*)disconnectNative},
498   };
499   const int result = REGISTER_NATIVE_METHODS(
500       env, "com/android/bluetooth/hid/HidDeviceNativeInterface", methods);
501   if (result != 0) {
502     return result;
503   }
504 
505   const JNIJavaMethod javaMethods[] = {
506       {"onApplicationStateChanged", "([BZ)V",
507        &method_onApplicationStateChanged},
508       {"onConnectStateChanged", "([BI)V", &method_onConnectStateChanged},
509       {"onGetReport", "(BBS)V", &method_onGetReport},
510       {"onSetReport", "(BB[B)V", &method_onSetReport},
511       {"onSetProtocol", "(B)V", &method_onSetProtocol},
512       {"onInterruptData", "(B[B)V", &method_onInterruptData},
513       {"onVirtualCableUnplug", "()V", &method_onVirtualCableUnplug},
514   };
515   GET_JAVA_METHODS(env, "com/android/bluetooth/hid/HidDeviceNativeInterface",
516                    javaMethods);
517 
518   return 0;
519 }
520 }  // namespace android
521