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