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