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