com_android_bluetooth_avrcp.cpp revision 17675906064bb72fdcca75baa56cdf8bb8968d01
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 "BluetoothAvrcpServiceJni"
18
19#define LOG_NDEBUG 0
20
21#include "com_android_bluetooth.h"
22#include "hardware/bt_rc.h"
23#include "utils/Log.h"
24#include "android_runtime/AndroidRuntime.h"
25
26#include <string.h>
27
28namespace android {
29static jmethodID method_getRcFeatures;
30static jmethodID method_getPlayStatus;
31static jmethodID method_getElementAttr;
32static jmethodID method_registerNotification;
33static jmethodID method_volumeChangeCallback;
34static jmethodID method_handlePassthroughCmd;
35
36static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
37static jobject mCallbacksObj = NULL;
38static JNIEnv *sCallbackEnv = NULL;
39
40static bool checkCallbackThread() {
41    // Always fetch the latest callbackEnv from AdapterService.
42    // Caching this could cause this sCallbackEnv to go out-of-sync
43    // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
44    // is received
45    sCallbackEnv = getCallbackEnv();
46
47    JNIEnv* env = AndroidRuntime::getJNIEnv();
48    if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
49    return true;
50}
51
52static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr, btrc_remote_features_t features) {
53    ALOGI("%s", __FUNCTION__);
54    jbyteArray addr;
55
56    if (!checkCallbackThread()) {
57        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
58        return;
59    }
60    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
61    if (!addr) {
62        ALOGE("Fail to new jbyteArray bd addr for connection state");
63        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
64        return;
65    }
66
67    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
68    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features);
69    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
70}
71
72static void btavrcp_get_play_status_callback() {
73    ALOGI("%s", __FUNCTION__);
74
75    if (!checkCallbackThread()) {
76        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
77        return;
78    }
79
80    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
81    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
82}
83
84static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) {
85    jintArray attrs;
86
87    ALOGI("%s", __FUNCTION__);
88
89    if (!checkCallbackThread()) {
90        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
91        return;
92    }
93    attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
94    if (!attrs) {
95        ALOGE("Fail to new jintArray for attrs");
96        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
97        return;
98    }
99    sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
100    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
101    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
102    sCallbackEnv->DeleteLocalRef(attrs);
103}
104
105static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) {
106    ALOGI("%s", __FUNCTION__);
107
108    if (!checkCallbackThread()) {
109        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
110        return;
111    }
112
113    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
114                                 (jint)event_id, (jint)param);
115    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
116}
117
118static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) {
119    ALOGI("%s", __FUNCTION__);
120
121    if (!checkCallbackThread()) {
122        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
123        return;
124    }
125
126    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, (jint)volume,
127                                                                             (jint)ctype);
128    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
129}
130
131static void btavrcp_passthrough_command_callback(int id, int pressed) {
132    ALOGI("%s", __FUNCTION__);
133
134    if (!checkCallbackThread()) {
135        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
136        return;
137    }
138
139    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, (jint)id,
140                                                                             (jint)pressed);
141    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
142}
143
144static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
145    sizeof(sBluetoothAvrcpCallbacks),
146    btavrcp_remote_features_callback,
147    btavrcp_get_play_status_callback,
148    NULL,
149    NULL,
150    NULL,
151    NULL,
152    NULL,
153    NULL,
154    btavrcp_get_element_attr_callback,
155    btavrcp_register_notification_callback,
156    btavrcp_volume_change_callback,
157    btavrcp_passthrough_command_callback
158};
159
160static void classInitNative(JNIEnv* env, jclass clazz) {
161    method_getRcFeatures =
162        env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
163    method_getPlayStatus =
164        env->GetMethodID(clazz, "getPlayStatus", "()V");
165
166    method_getElementAttr =
167        env->GetMethodID(clazz, "getElementAttr", "(B[I)V");
168
169    method_registerNotification =
170        env->GetMethodID(clazz, "registerNotification", "(II)V");
171
172    method_volumeChangeCallback =
173        env->GetMethodID(clazz, "volumeChangeCallback", "(II)V");
174
175    method_handlePassthroughCmd =
176        env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V");
177
178    ALOGI("%s: succeeds", __FUNCTION__);
179}
180
181static void initNative(JNIEnv *env, jobject object) {
182    const bt_interface_t* btInf;
183    bt_status_t status;
184
185    if ( (btInf = getBluetoothInterface()) == NULL) {
186        ALOGE("Bluetooth module is not loaded");
187        return;
188    }
189
190    if (sBluetoothAvrcpInterface !=NULL) {
191         ALOGW("Cleaning up Avrcp Interface before initializing...");
192         sBluetoothAvrcpInterface->cleanup();
193         sBluetoothAvrcpInterface = NULL;
194    }
195
196    if (mCallbacksObj != NULL) {
197         ALOGW("Cleaning up Avrcp callback object");
198         env->DeleteGlobalRef(mCallbacksObj);
199         mCallbacksObj = NULL;
200    }
201
202    if ( (sBluetoothAvrcpInterface = (btrc_interface_t *)
203          btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) {
204        ALOGE("Failed to get Bluetooth Avrcp Interface");
205        return;
206    }
207
208    if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) !=
209         BT_STATUS_SUCCESS) {
210        ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
211        sBluetoothAvrcpInterface = NULL;
212        return;
213    }
214
215    mCallbacksObj = env->NewGlobalRef(object);
216}
217
218static void cleanupNative(JNIEnv *env, jobject object) {
219    const bt_interface_t* btInf;
220
221    if ( (btInf = getBluetoothInterface()) == NULL) {
222        ALOGE("Bluetooth module is not loaded");
223        return;
224    }
225
226    if (sBluetoothAvrcpInterface !=NULL) {
227        sBluetoothAvrcpInterface->cleanup();
228        sBluetoothAvrcpInterface = NULL;
229    }
230
231    if (mCallbacksObj != NULL) {
232        env->DeleteGlobalRef(mCallbacksObj);
233        mCallbacksObj = NULL;
234    }
235}
236
237static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus,
238                                       jint songLen, jint songPos) {
239    bt_status_t status;
240
241    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
242    if (!sBluetoothAvrcpInterface) return JNI_FALSE;
243
244    if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
245                                            songLen, songPos)) != BT_STATUS_SUCCESS) {
246        ALOGE("Failed get_play_status_rsp, status: %d", status);
247    }
248
249    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
250}
251
252  static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
253                                          jintArray attrIds, jobjectArray textArray) {
254    jint *attr;
255    bt_status_t status;
256    jstring text;
257    int i;
258    btrc_element_attr_val_t *pAttrs = NULL;
259    const char* textStr;
260
261    if (!sBluetoothAvrcpInterface) return JNI_FALSE;
262
263    if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
264        ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
265        return JNI_FALSE;
266    }
267
268    pAttrs = new btrc_element_attr_val_t[numAttr];
269    if (!pAttrs) {
270        ALOGE("get_element_attr_rsp: not have enough memeory");
271        return JNI_FALSE;
272    }
273
274    attr = env->GetIntArrayElements(attrIds, NULL);
275    if (!attr) {
276        delete[] pAttrs;
277        jniThrowIOException(env, EINVAL);
278        return JNI_FALSE;
279    }
280
281    for (i = 0; i < numAttr; ++i) {
282        text = (jstring) env->GetObjectArrayElement(textArray, i);
283        textStr = env->GetStringUTFChars(text, NULL);
284        if (!textStr) {
285            ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL");
286            env->DeleteLocalRef(text);
287            break;
288        }
289
290        pAttrs[i].attr_id = attr[i];
291        if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
292            ALOGE("get_element_attr_rsp: string length exceed maximum");
293            strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1);
294            pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0;
295        } else {
296            strcpy((char *)pAttrs[i].text, textStr);
297        }
298        env->ReleaseStringUTFChars(text, textStr);
299        env->DeleteLocalRef(text);
300    }
301
302    if (i < numAttr) {
303        delete[] pAttrs;
304        env->ReleaseIntArrayElements(attrIds, attr, 0);
305        return JNI_FALSE;
306    }
307
308    if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) !=
309        BT_STATUS_SUCCESS) {
310        ALOGE("Failed get_element_attr_rsp, status: %d", status);
311    }
312
313    delete[] pAttrs;
314    env->ReleaseIntArrayElements(attrIds, attr, 0);
315    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
316}
317
318static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object,
319                                                        jint type, jint playStatus) {
320    bt_status_t status;
321    btrc_register_notification_t param;
322
323    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
324    if (!sBluetoothAvrcpInterface) return JNI_FALSE;
325
326    param.play_status = (btrc_play_status_t)playStatus;
327    if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
328                  (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
329        ALOGE("Failed register_notification_rsp play status, status: %d", status);
330    }
331
332    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
333}
334
335static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object,
336                                                         jint type, jbyteArray track) {
337    bt_status_t status;
338    btrc_register_notification_t param;
339    jbyte *trk;
340    int i;
341
342    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
343    if (!sBluetoothAvrcpInterface) return JNI_FALSE;
344
345    trk = env->GetByteArrayElements(track, NULL);
346    if (!trk) {
347        jniThrowIOException(env, EINVAL);
348        return JNI_FALSE;
349    }
350
351    for (i = 0; i < BTRC_UID_SIZE; ++i) {
352      param.track[i] = trk[i];
353    }
354
355    if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
356                  (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
357        ALOGE("Failed register_notification_rsp track change, status: %d", status);
358    }
359
360    env->ReleaseByteArrayElements(track, trk, 0);
361    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
362}
363
364static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object,
365                                                        jint type, jint playPos) {
366    bt_status_t status;
367    btrc_register_notification_t param;
368
369    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
370    if (!sBluetoothAvrcpInterface) return JNI_FALSE;
371
372    param.song_pos = (uint32_t)playPos;
373    if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
374                  (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
375        ALOGE("Failed register_notification_rsp play position, status: %d", status);
376    }
377
378    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
379}
380
381static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) {
382    bt_status_t status;
383
384    //TODO: delete test code
385    ALOGI("%s: jint: %d, uint8_t: %u", __FUNCTION__, volume, (uint8_t) volume);
386
387    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
388    if (!sBluetoothAvrcpInterface) return JNI_FALSE;
389
390    if ((status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume)) != BT_STATUS_SUCCESS) {
391        ALOGE("Failed set_volume, status: %d", status);
392    }
393
394    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
395}
396
397static JNINativeMethod sMethods[] = {
398    {"classInitNative", "()V", (void *) classInitNative},
399    {"initNative", "()V", (void *) initNative},
400    {"cleanupNative", "()V", (void *) cleanupNative},
401    {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative},
402    {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative},
403    {"registerNotificationRspPlayStatusNative", "(II)Z",
404     (void *) registerNotificationRspPlayStatusNative},
405    {"registerNotificationRspTrackChangeNative", "(I[B)Z",
406     (void *) registerNotificationRspTrackChangeNative},
407    {"registerNotificationRspPlayPosNative", "(II)Z",
408     (void *) registerNotificationRspPlayPosNative},
409    {"setVolumeNative", "(I)Z",
410     (void *) setVolumeNative}
411};
412
413int register_com_android_bluetooth_avrcp(JNIEnv* env)
414{
415    return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/Avrcp",
416                                    sMethods, NELEM(sMethods));
417}
418
419}
420