summaryrefslogtreecommitdiffstats
path: root/jni/com_android_bluetooth_avrcp.cpp
diff options
context:
space:
mode:
authorZhihai Xu <zhihaixu@google.com>2013-03-14 11:51:06 -0700
committerZhihai Xu <zhihaixu@google.com>2013-03-20 18:19:17 -0700
commitc1c259c0ace7195240f1443c805995bfe8692a72 (patch)
tree00a380a2eb1c1b63a496b82a005ea725e03e1883 /jni/com_android_bluetooth_avrcp.cpp
parentb241cda1eec2fbefd6d21e0819532f7a76947635 (diff)
downloadandroid_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.cpp321
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, &param)) != 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, &param)) != 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));
+}
+
+}