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