diff options
author | Zhihai Xu <zhihaixu@google.com> | 2013-03-14 11:51:06 -0700 |
---|---|---|
committer | Zhihai Xu <zhihaixu@google.com> | 2013-03-20 18:19:17 -0700 |
commit | c1c259c0ace7195240f1443c805995bfe8692a72 (patch) | |
tree | 00a380a2eb1c1b63a496b82a005ea725e03e1883 /jni/com_android_bluetooth_avrcp.cpp | |
parent | b241cda1eec2fbefd6d21e0819532f7a76947635 (diff) | |
download | android_packages_apps_Bluetooth-c1c259c0ace7195240f1443c805995bfe8692a72.tar.gz android_packages_apps_Bluetooth-c1c259c0ace7195240f1443c805995bfe8692a72.tar.bz2 android_packages_apps_Bluetooth-c1c259c0ace7195240f1443c805995bfe8692a72.zip |
framework support for new Bluetooth profiles AVRCP 1.3:Metadata and play status
send track changed response if the metadata is changed.
issue 8383522
Change-Id: Ie55ed368d355484a6b83f4aa24c70aa33b72f799
Diffstat (limited to 'jni/com_android_bluetooth_avrcp.cpp')
-rw-r--r-- | jni/com_android_bluetooth_avrcp.cpp | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/jni/com_android_bluetooth_avrcp.cpp b/jni/com_android_bluetooth_avrcp.cpp new file mode 100644 index 000000000..ee7083e7d --- /dev/null +++ b/jni/com_android_bluetooth_avrcp.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BluetoothAvrcpServiceJni" + +#define LOG_NDEBUG 0 + +#include "com_android_bluetooth.h" +#include "hardware/bt_rc.h" +#include "utils/Log.h" +#include "android_runtime/AndroidRuntime.h" + +#include <string.h> + +namespace android { +static jmethodID method_getPlayStatus; +static jmethodID method_getElementAttr; +static jmethodID method_registerNotification; + +static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; +static jobject mCallbacksObj = NULL; +static JNIEnv *sCallbackEnv = NULL; + +static bool checkCallbackThread() { + // Always fetch the latest callbackEnv from AdapterService. + // Caching this could cause this sCallbackEnv to go out-of-sync + // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event + // is received + sCallbackEnv = getCallbackEnv(); + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (sCallbackEnv != env || sCallbackEnv == NULL) return false; + return true; +} + +static void btavrcp_get_play_status_callback() { + ALOGI("%s", __FUNCTION__); + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus); + checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); +} + +static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) { + jintArray attrs; + + ALOGI("%s", __FUNCTION__); + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr); + if (!attrs) { + ALOGE("Fail to new jintArray for attrs"); + checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); + return; + } + sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs); + checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); + sCallbackEnv->DeleteLocalRef(attrs); +} + +static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) { + ALOGI("%s", __FUNCTION__); + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification, + (jint)event_id, (jint)param); + checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); +} + +static btrc_callbacks_t sBluetoothAvrcpCallbacks = { + sizeof(sBluetoothAvrcpCallbacks), + btavrcp_get_play_status_callback, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + btavrcp_get_element_attr_callback, + btavrcp_register_notification_callback +}; + +static void classInitNative(JNIEnv* env, jclass clazz) { + method_getPlayStatus = + env->GetMethodID(clazz, "getPlayStatus", "()V"); + + method_getElementAttr = + env->GetMethodID(clazz, "getElementAttr", "(B[I)V"); + + method_registerNotification = + env->GetMethodID(clazz, "registerNotification", "(II)V"); + + ALOGI("%s: succeeds", __FUNCTION__); +} + +static void initNative(JNIEnv *env, jobject object) { + const bt_interface_t* btInf; + bt_status_t status; + + if ( (btInf = getBluetoothInterface()) == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothAvrcpInterface !=NULL) { + ALOGW("Cleaning up Avrcp Interface before initializing..."); + sBluetoothAvrcpInterface->cleanup(); + sBluetoothAvrcpInterface = NULL; + } + + if (mCallbacksObj != NULL) { + ALOGW("Cleaning up Avrcp callback object"); + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } + + if ( (sBluetoothAvrcpInterface = (btrc_interface_t *) + btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) { + ALOGE("Failed to get Bluetooth Avrcp Interface"); + return; + } + + if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) != + BT_STATUS_SUCCESS) { + ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status); + sBluetoothAvrcpInterface = NULL; + return; + } + + mCallbacksObj = env->NewGlobalRef(object); +} + +static void cleanupNative(JNIEnv *env, jobject object) { + const bt_interface_t* btInf; + + if ( (btInf = getBluetoothInterface()) == NULL) { + ALOGE("Bluetooth module is not loaded"); + return; + } + + if (sBluetoothAvrcpInterface !=NULL) { + sBluetoothAvrcpInterface->cleanup(); + sBluetoothAvrcpInterface = NULL; + } + + if (mCallbacksObj != NULL) { + env->DeleteGlobalRef(mCallbacksObj); + mCallbacksObj = NULL; + } +} + +static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus, + jint songLen, jint songPos) { + bt_status_t status; + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); + if (!sBluetoothAvrcpInterface) return JNI_FALSE; + + if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus, + songLen, songPos)) != BT_STATUS_SUCCESS) { + ALOGE("Failed get_play_status_rsp, status: %d", status); + } + + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + + static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr, + jintArray attrIds, jobjectArray textArray) { + jint *attr; + bt_status_t status; + jstring text; + int i; + btrc_element_attr_val_t *pAttrs = NULL; + const char* textStr; + + if (!sBluetoothAvrcpInterface) return JNI_FALSE; + + if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) { + ALOGE("get_element_attr_rsp: number of attributes exceed maximum"); + return JNI_FALSE; + } + + pAttrs = new btrc_element_attr_val_t[numAttr]; + if (!pAttrs) { + ALOGE("get_element_attr_rsp: not have enough memeory"); + return JNI_FALSE; + } + + attr = env->GetIntArrayElements(attrIds, NULL); + if (!attr) { + delete[] pAttrs; + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + for (i = 0; i < numAttr; ++i) { + text = (jstring) env->GetObjectArrayElement(textArray, i); + textStr = env->GetStringUTFChars(text, NULL); + if (!textStr) { + ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL"); + env->DeleteLocalRef(text); + break; + } + if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) { + ALOGE("get_element_attr_rsp: string length exceed maximum"); + env->ReleaseStringUTFChars(text, textStr); + env->DeleteLocalRef(text); + break; + } + pAttrs[i].attr_id = attr[i]; + strcpy((char *)pAttrs[i].text, textStr); + env->ReleaseStringUTFChars(text, textStr); + env->DeleteLocalRef(text); + } + + if (i < numAttr) { + delete[] pAttrs; + env->ReleaseIntArrayElements(attrIds, attr, 0); + return JNI_FALSE; + } + + if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) != + BT_STATUS_SUCCESS) { + ALOGE("Failed get_element_attr_rsp, status: %d", status); + } + + delete[] pAttrs; + env->ReleaseIntArrayElements(attrIds, attr, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object, + jint type, jint playStatus) { + bt_status_t status; + btrc_register_notification_t param; + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); + if (!sBluetoothAvrcpInterface) return JNI_FALSE; + + param.play_status = (btrc_play_status_t)playStatus; + if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED, + (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { + ALOGE("Failed register_notification_rsp play status, status: %d", status); + } + + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object, + jint type, jbyteArray track) { + bt_status_t status; + btrc_register_notification_t param; + jbyte *trk; + int i; + + ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); + if (!sBluetoothAvrcpInterface) return JNI_FALSE; + + trk = env->GetByteArrayElements(track, NULL); + if (!trk) { + jniThrowIOException(env, EINVAL); + return JNI_FALSE; + } + + for (i = 0; i < BTRC_UID_SIZE; ++i) { + param.track[i] = trk[i]; + } + + if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE, + (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { + ALOGE("Failed register_notification_rsp track change, status: %d", status); + } + + env->ReleaseByteArrayElements(track, trk, 0); + return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + {"classInitNative", "()V", (void *) classInitNative}, + {"initNative", "()V", (void *) initNative}, + {"cleanupNative", "()V", (void *) cleanupNative}, + {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative}, + {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative}, + {"registerNotificationRspPlayStatusNative", "(II)Z", + (void *) registerNotificationRspPlayStatusNative}, + {"registerNotificationRspTrackChangeNative", "(I[B)Z", + (void *) registerNotificationRspTrackChangeNative}, +}; + +int register_com_android_bluetooth_avrcp(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/Avrcp", + sMethods, NELEM(sMethods)); +} + +} |