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