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