com_android_bluetooth_hfp.cpp revision 6c91bc0a163cc7600c40d7fb979777fd911d1ef1
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5#define LOG_TAG "BluetoothHeadsetServiceJni"
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_hf.h"
17#include "utils/Log.h"
18#include "android_runtime/AndroidRuntime.h"
19
20#include <string.h>
21
22namespace android {
23
24static jmethodID method_onConnectionStateChanged;
25static jmethodID method_onAudioStateChanged;
26static jmethodID method_onVrStateChanged;
27static jmethodID method_onAnswerCall;
28static jmethodID method_onHangupCall;
29static jmethodID method_onVolumeChanged;
30static jmethodID method_onDialCall;
31static jmethodID method_onSendDtmf;
32static jmethodID method_onNoiceReductionEnable;
33static jmethodID method_onAtChld;
34static jmethodID method_onAtCnum;
35static jmethodID method_onAtCind;
36static jmethodID method_onAtCops;
37static jmethodID method_onAtClcc;
38static jmethodID method_onUnknownAt;
39static jmethodID method_onKeyPressed;
40
41static const bthf_interface_t *sBluetoothHfpInterface = NULL;
42static jobject mCallbacksObj = NULL;
43static JNIEnv *sCallbackEnv = NULL;
44
45static bool checkCallbackThread() {
46    if (sCallbackEnv == NULL) {
47        sCallbackEnv = getCallbackEnv();
48    }
49
50    JNIEnv* env = AndroidRuntime::getJNIEnv();
51    if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
52    return true;
53}
54
55static void connection_state_callback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr) {
56    jbyteArray addr;
57
58    LOGI("%s", __FUNCTION__);
59
60    CHECK_CALLBACK_ENV
61    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
62    if (!addr) {
63        LOGE("Fail to new jbyteArray bd addr for connection state");
64        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
65        return;
66    }
67
68    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
69    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
70                                 (jint) state, addr);
71    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
72    sCallbackEnv->DeleteLocalRef(addr);
73}
74
75static void audio_state_callback(bthf_audio_state_t state, bt_bdaddr_t* bd_addr) {
76    jbyteArray addr;
77
78    CHECK_CALLBACK_ENV
79    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
80    if (!addr) {
81        LOGE("Fail to new jbyteArray bd addr for audio state");
82        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
83        return;
84    }
85
86    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
87    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state, addr);
88    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
89    sCallbackEnv->DeleteLocalRef(addr);
90}
91
92static void voice_recognition_callback(bthf_vr_state_t state) {
93    CHECK_CALLBACK_ENV
94    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVrStateChanged, (jint) state);
95    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
96}
97
98static void answer_call_callback() {
99    CHECK_CALLBACK_ENV
100    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAnswerCall);
101    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
102}
103
104static void hangup_call_callback() {
105    CHECK_CALLBACK_ENV
106    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onHangupCall);
107    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
108}
109
110static void volume_control_callback(bthf_volume_type_t type, int volume) {
111    CHECK_CALLBACK_ENV
112    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onVolumeChanged, (jint) type, (jint) volume);
113    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
114}
115
116static void dial_call_callback(char *number) {
117    CHECK_CALLBACK_ENV
118    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDialCall,
119                                 sCallbackEnv->NewStringUTF(number));
120    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
121}
122
123static void dtmf_cmd_callback(char dtmf) {
124    CHECK_CALLBACK_ENV
125    // TBD dtmf has changed from int to char
126    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSendDtmf, dtmf);
127    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
128}
129
130static void noice_reduction_callback(bthf_nrec_t nrec) {
131    CHECK_CALLBACK_ENV
132    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNoiceReductionEnable,
133                                 nrec == BTHF_NREC_START);
134    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
135}
136
137static void at_chld_callback(bthf_chld_type_t chld) {
138    CHECK_CALLBACK_ENV
139    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtChld, chld);
140    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
141}
142
143static void at_cnum_callback() {
144    CHECK_CALLBACK_ENV
145    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCnum);
146    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
147}
148
149static void at_cind_callback() {
150    CHECK_CALLBACK_ENV
151    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCind);
152    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
153}
154
155static void at_cops_callback() {
156    CHECK_CALLBACK_ENV
157    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtCops);
158    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
159}
160
161static void at_clcc_callback() {
162    CHECK_CALLBACK_ENV
163    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtClcc);
164    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
165}
166
167static void unknown_at_callback(char *at_string) {
168    CHECK_CALLBACK_ENV
169    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onUnknownAt,
170                                 sCallbackEnv->NewStringUTF(at_string));
171    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
172}
173
174static void key_pressed_callback() {
175    CHECK_CALLBACK_ENV
176    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onKeyPressed);
177    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
178}
179
180static bthf_callbacks_t sBluetoothHfpCallbacks = {
181    sizeof(sBluetoothHfpCallbacks),
182    connection_state_callback,
183    audio_state_callback,
184    voice_recognition_callback,
185    answer_call_callback,
186    hangup_call_callback,
187    volume_control_callback,
188    dial_call_callback,
189    dtmf_cmd_callback,
190    noice_reduction_callback,
191    at_chld_callback,
192    at_cnum_callback,
193    at_cind_callback,
194    at_cops_callback,
195    at_clcc_callback,
196    unknown_at_callback,
197    key_pressed_callback
198};
199
200static void classInitNative(JNIEnv* env, jclass clazz) {
201    int err;
202    const bt_interface_t* btInf;
203    bt_status_t status;
204
205    method_onConnectionStateChanged =
206        env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
207    method_onAudioStateChanged = env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
208    method_onVrStateChanged = env->GetMethodID(clazz, "onVrStateChanged", "(I)V");
209    method_onAnswerCall = env->GetMethodID(clazz, "onAnswerCall", "()V");
210    method_onHangupCall = env->GetMethodID(clazz, "onHangupCall", "()V");
211    method_onVolumeChanged = env->GetMethodID(clazz, "onVolumeChanged", "(II)V");
212    method_onDialCall = env->GetMethodID(clazz, "onDialCall", "(Ljava/lang/String;)V");
213    method_onSendDtmf = env->GetMethodID(clazz, "onSendDtmf", "(I)V");
214    method_onNoiceReductionEnable = env->GetMethodID(clazz, "onNoiceReductionEnable", "(Z)V");
215    method_onAtChld = env->GetMethodID(clazz, "onAtChld", "(I)V");
216    method_onAtCnum = env->GetMethodID(clazz, "onAtCnum", "()V");
217    method_onAtCind = env->GetMethodID(clazz, "onAtCind", "()V");
218    method_onAtCops = env->GetMethodID(clazz, "onAtCops", "()V");
219    method_onAtClcc = env->GetMethodID(clazz, "onAtClcc", "()V");
220    method_onUnknownAt = env->GetMethodID(clazz, "onUnknownAt", "(Ljava/lang/String;)V");
221    method_onKeyPressed = env->GetMethodID(clazz, "onKeyPressed", "()V");
222
223    if ( (btInf = getBluetoothInterface()) == NULL) {
224        LOGE("Bluetooth module is not loaded");
225        return;
226    }
227
228    if ( (sBluetoothHfpInterface = (bthf_interface_t *)
229          btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID)) == NULL) {
230        LOGE("Failed to get Bluetooth Handsfree Interface");
231        return;
232    }
233
234    // TODO(BT) do this only once or
235    //          Do we need to do this every time the BT reenables?
236    if ( (status = sBluetoothHfpInterface->init(&sBluetoothHfpCallbacks)) != BT_STATUS_SUCCESS) {
237        LOGE("Failed to initialize Bluetooth HFP, status: %d", status);
238        sBluetoothHfpInterface = NULL;
239        return;
240    }
241
242    LOGI("%s: succeeds", __FUNCTION__);
243}
244
245static void initializeNativeDataNative(JNIEnv *env, jobject object) {
246    // TODO(BT) clean it up when hfp service is stopped
247    //          Is there a need to do cleanup since HFP is always present for phone?
248    //          We need handle it for tablets. But should that be handle at compile time?
249    mCallbacksObj = env->NewGlobalRef(object);
250}
251
252static jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {
253    jbyte *addr;
254    bt_bdaddr_t * btAddr;
255    bt_status_t status;
256
257    LOGI("%s: sBluetoothHfpInterface: %p", __FUNCTION__, sBluetoothHfpInterface);
258    if (!sBluetoothHfpInterface) return JNI_FALSE;
259
260    addr = env->GetByteArrayElements(address, NULL);
261    btAddr = (bt_bdaddr_t *) addr;
262    if (!addr) {
263        jniThrowIOException(env, EINVAL);
264        return JNI_FALSE;
265    }
266
267    if ((status = sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
268        LOGE("Failed HF connection, status: %d", status);
269    }
270    env->ReleaseByteArrayElements(address, addr, 0);
271    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
272}
273
274static jboolean disconnectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {
275    jbyte *addr;
276    bt_status_t status;
277
278    if (!sBluetoothHfpInterface) return JNI_FALSE;
279
280    addr = env->GetByteArrayElements(address, NULL);
281    if (!addr) {
282        jniThrowIOException(env, EINVAL);
283        return JNI_FALSE;
284    }
285
286    if ( (status = sBluetoothHfpInterface->disconnect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
287        LOGE("Failed HF disconnection, status: %d", status);
288    }
289    env->ReleaseByteArrayElements(address, addr, 0);
290    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
291}
292
293static jboolean connectAudioNative(JNIEnv *env, jobject object, jbyteArray address) {
294    jbyte *addr;
295    bt_status_t status;
296
297    if (!sBluetoothHfpInterface) return JNI_FALSE;
298
299    addr = env->GetByteArrayElements(address, NULL);
300    if (!addr) {
301        jniThrowIOException(env, EINVAL);
302        return JNI_FALSE;
303    }
304
305    if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) !=
306         BT_STATUS_SUCCESS) {
307        LOGE("Failed HF audio connection, status: %d", status);
308    }
309    env->ReleaseByteArrayElements(address, addr, 0);
310    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
311}
312
313static jboolean disconnectAudioNative(JNIEnv *env, jobject object, jbyteArray address) {
314    jbyte *addr;
315    bt_status_t status;
316
317    if (!sBluetoothHfpInterface) return JNI_FALSE;
318
319    addr = env->GetByteArrayElements(address, NULL);
320    if (!addr) {
321        jniThrowIOException(env, EINVAL);
322        return JNI_FALSE;
323    }
324
325    if ( (status = sBluetoothHfpInterface->disconnect_audio((bt_bdaddr_t *) addr)) !=
326         BT_STATUS_SUCCESS) {
327        LOGE("Failed HF audio disconnection, status: %d", status);
328    }
329    env->ReleaseByteArrayElements(address, addr, 0);
330    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
331}
332
333static jboolean startVoiceRecognitionNative(JNIEnv *env, jobject object) {
334    bt_status_t status;
335    if (!sBluetoothHfpInterface) return JNI_FALSE;
336
337    if ( (status = sBluetoothHfpInterface->start_voice_recognition()) != BT_STATUS_SUCCESS) {
338        LOGE("Failed to start voice recognition, status: %d", status);
339    }
340    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
341}
342
343static jboolean stopVoiceRecognitionNative(JNIEnv *env, jobject object) {
344    bt_status_t status;
345    if (!sBluetoothHfpInterface) return JNI_FALSE;
346
347    if ( (status = sBluetoothHfpInterface->stop_voice_recognition()) != BT_STATUS_SUCCESS) {
348        LOGE("Failed to stop voice recognition, status: %d", status);
349    }
350    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
351}
352
353static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume_type, jint volume) {
354    bt_status_t status;
355    if (!sBluetoothHfpInterface) return JNI_FALSE;
356
357    if ( (status = sBluetoothHfpInterface->volume_control((bthf_volume_type_t) volume_type,
358                                                          volume)) != BT_STATUS_SUCCESS) {
359        LOGE("FAILED to control volume, status: %d", status);
360    }
361    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
362}
363
364static jboolean notifyDeviceStatusNative(JNIEnv *env, jobject object,
365                                         jint network_state, jint service_type, jint signal,
366                                         jint battery_charge) {
367    bt_status_t status;
368    if (!sBluetoothHfpInterface) return JNI_FALSE;
369
370    if ( (status = sBluetoothHfpInterface->device_status_notification
371          ((bthf_network_state_t) network_state, (bthf_service_type_t) service_type,
372           signal, battery_charge)) != BT_STATUS_SUCCESS) {
373        LOGE("FAILED to notify device status, status: %d", status);
374    }
375    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
376}
377
378static jboolean copsResponseNative(JNIEnv *env, jobject object, jstring operator_str) {
379    bt_status_t status;
380    const char *operator_name;
381    if (!sBluetoothHfpInterface) return JNI_FALSE;
382
383    operator_name = env->GetStringUTFChars(operator_str, NULL);
384
385    if ( (status = sBluetoothHfpInterface->cops_response(operator_name)) != BT_STATUS_SUCCESS) {
386        LOGE("Failed sending cops response, status: %d", status);
387    }
388    env->ReleaseStringUTFChars(operator_str, operator_name);
389    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
390}
391
392static jboolean cindResponseNative(JNIEnv *env, jobject object,
393                                   jint service, jint num_active, jint num_held, jint call_state,
394                                   jint signal, jint roam, jint battery_charge) {
395    bt_status_t status;
396    if (!sBluetoothHfpInterface) return JNI_FALSE;
397
398    if ( (status = sBluetoothHfpInterface->cind_response(service, num_active, num_held,
399                       (bthf_call_state_t) call_state,
400                       signal, roam, battery_charge)) != BT_STATUS_SUCCESS) {
401        LOGE("Failed cind_response, status: %d", status);
402    }
403    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
404}
405
406
407static jboolean atResponseStringNative(JNIEnv *env, jobject object, jstring response_str) {
408    bt_status_t status;
409    const char *response;
410    if (!sBluetoothHfpInterface) return JNI_FALSE;
411
412    response = env->GetStringUTFChars(response_str, NULL);
413
414    if ( (status = sBluetoothHfpInterface->formatted_at_response(response)) != BT_STATUS_SUCCESS) {
415        LOGE("Failed formatted AT response, status: %d", status);
416    }
417    env->ReleaseStringUTFChars(response_str, response);
418    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
419}
420
421static jboolean atResponseCodeNative(JNIEnv *env, jobject object, jint response_code) {
422    bt_status_t status;
423    if (!sBluetoothHfpInterface) return JNI_FALSE;
424
425    if ( (status = sBluetoothHfpInterface->at_response((bthf_at_response_t) response_code)) !=
426         BT_STATUS_SUCCESS) {
427        LOGE("Failed AT response, status: %d", status);
428    }
429    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
430}
431
432static jboolean clccResponseNative(JNIEnv *env, jobject object, jint index, jint dir,
433                                   jint callStatus, jint mode, jboolean mpty, jstring number_str,
434                                   jint type) {
435    bt_status_t status;
436    const char *number;
437    if (!sBluetoothHfpInterface) return JNI_FALSE;
438
439    number = env->GetStringUTFChars(number_str, NULL);
440
441    if ( (status = sBluetoothHfpInterface->clcc_response(index, (bthf_call_direction_t) dir,
442                     (bthf_call_state_t) callStatus,  (bthf_call_mode_t) mode,
443                     mpty ? BTHF_CALL_MPTY_TYPE_MULTI : BTHF_CALL_MPTY_TYPE_SINGLE,
444                     number, (bthf_call_addrtype_t) type)) != BT_STATUS_SUCCESS) {
445        LOGE("Failed sending CLCC response, status: %d", status);
446    }
447    env->ReleaseStringUTFChars(number_str, number);
448    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
449}
450
451static jboolean phoneStateChangeNative(JNIEnv *env, jobject object, jint num_active, jint num_held,
452                                       jint call_state, jstring number_str, jint type) {
453    bt_status_t status;
454    const char *number;
455    if (!sBluetoothHfpInterface) return JNI_FALSE;
456
457    number = env->GetStringUTFChars(number_str, NULL);
458
459    if ( (status = sBluetoothHfpInterface->phone_state_change(num_active, num_held,
460                       (bthf_call_state_t) call_state, number,
461                       (bthf_call_addrtype_t) type)) != BT_STATUS_SUCCESS) {
462        LOGE("Failed report phone state change, status: %d", status);
463    }
464    env->ReleaseStringUTFChars(number_str, number);
465    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
466}
467
468static JNINativeMethod sMethods[] = {
469    {"classInitNative", "()V", (void *) classInitNative},
470    {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
471    {"connectHfpNative", "([B)Z", (void *) connectHfpNative},
472    {"disconnectHfpNative", "([B)Z", (void *) disconnectHfpNative},
473    {"connectAudioNative", "([B)Z", (void *) connectAudioNative},
474    {"disconnectAudioNative", "([B)Z", (void *) disconnectAudioNative},
475    {"startVoiceRecognitionNative", "()Z", (void *) startVoiceRecognitionNative},
476    {"stopVoiceRecognitionNative", "()Z", (void *) stopVoiceRecognitionNative},
477    {"setVolumeNative", "(II)Z", (void *) setVolumeNative},
478    {"notifyDeviceStatusNative", "(IIII)Z", (void *) notifyDeviceStatusNative},
479    {"copsResponseNative", "(Ljava/lang/String;)Z", (void *) copsResponseNative},
480    {"cindResponseNative", "(IIIIIII)Z", (void *) cindResponseNative},
481    {"atResponseStringNative", "(Ljava/lang/String;)Z", (void *) atResponseStringNative},
482    {"atResponseCodeNative", "(I)Z", (void *)atResponseCodeNative},
483    {"clccResponseNative", "(IIIIZLjava/lang/String;I)Z", (void *) clccResponseNative},
484    {"phoneStateChangeNative", "(IIILjava/lang/String;I)Z", (void *) phoneStateChangeNative},
485    // TODO(BT) clean up
486};
487
488int register_com_android_bluetooth_hfp(JNIEnv* env)
489{
490    return jniRegisterNativeMethods(env, "com/android/bluetooth/hfp/HeadsetStateMachine",
491                                    sMethods, NELEM(sMethods));
492}
493
494} /* namespace android */
495