com_android_bluetooth_avrcp.cpp revision aa1ffd5c6bd4f016c6ed452b3551e65872eaea8e
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_getPlayStatus; 30static jmethodID method_getElementAttr; 31static jmethodID method_registerNotification; 32 33static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; 34static jobject mCallbacksObj = NULL; 35static JNIEnv *sCallbackEnv = NULL; 36 37static bool checkCallbackThread() { 38 // Always fetch the latest callbackEnv from AdapterService. 39 // Caching this could cause this sCallbackEnv to go out-of-sync 40 // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event 41 // is received 42 sCallbackEnv = getCallbackEnv(); 43 44 JNIEnv* env = AndroidRuntime::getJNIEnv(); 45 if (sCallbackEnv != env || sCallbackEnv == NULL) return false; 46 return true; 47} 48 49static void btavrcp_get_play_status_callback() { 50 ALOGI("%s", __FUNCTION__); 51 52 if (!checkCallbackThread()) { 53 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 54 return; 55 } 56 57 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus); 58 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 59} 60 61static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) { 62 jintArray attrs; 63 64 ALOGI("%s", __FUNCTION__); 65 66 if (!checkCallbackThread()) { 67 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 68 return; 69 } 70 attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr); 71 if (!attrs) { 72 ALOGE("Fail to new jintArray for attrs"); 73 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 74 return; 75 } 76 sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs); 77 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs); 78 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 79 sCallbackEnv->DeleteLocalRef(attrs); 80} 81 82static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) { 83 ALOGI("%s", __FUNCTION__); 84 85 if (!checkCallbackThread()) { 86 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 87 return; 88 } 89 90 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification, 91 (jint)event_id, (jint)param); 92 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 93} 94 95static btrc_callbacks_t sBluetoothAvrcpCallbacks = { 96 sizeof(sBluetoothAvrcpCallbacks), 97 btavrcp_get_play_status_callback, 98 NULL, 99 NULL, 100 NULL, 101 NULL, 102 NULL, 103 NULL, 104 btavrcp_get_element_attr_callback, 105 btavrcp_register_notification_callback 106}; 107 108static void classInitNative(JNIEnv* env, jclass clazz) { 109 method_getPlayStatus = 110 env->GetMethodID(clazz, "getPlayStatus", "()V"); 111 112 method_getElementAttr = 113 env->GetMethodID(clazz, "getElementAttr", "(B[I)V"); 114 115 method_registerNotification = 116 env->GetMethodID(clazz, "registerNotification", "(II)V"); 117 118 ALOGI("%s: succeeds", __FUNCTION__); 119} 120 121static void initNative(JNIEnv *env, jobject object) { 122 const bt_interface_t* btInf; 123 bt_status_t status; 124 125 if ( (btInf = getBluetoothInterface()) == NULL) { 126 ALOGE("Bluetooth module is not loaded"); 127 return; 128 } 129 130 if (sBluetoothAvrcpInterface !=NULL) { 131 ALOGW("Cleaning up Avrcp Interface before initializing..."); 132 sBluetoothAvrcpInterface->cleanup(); 133 sBluetoothAvrcpInterface = NULL; 134 } 135 136 if (mCallbacksObj != NULL) { 137 ALOGW("Cleaning up Avrcp callback object"); 138 env->DeleteGlobalRef(mCallbacksObj); 139 mCallbacksObj = NULL; 140 } 141 142 if ( (sBluetoothAvrcpInterface = (btrc_interface_t *) 143 btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) { 144 ALOGE("Failed to get Bluetooth Avrcp Interface"); 145 return; 146 } 147 148 if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) != 149 BT_STATUS_SUCCESS) { 150 ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status); 151 sBluetoothAvrcpInterface = NULL; 152 return; 153 } 154 155 mCallbacksObj = env->NewGlobalRef(object); 156} 157 158static void cleanupNative(JNIEnv *env, jobject object) { 159 const bt_interface_t* btInf; 160 161 if ( (btInf = getBluetoothInterface()) == NULL) { 162 ALOGE("Bluetooth module is not loaded"); 163 return; 164 } 165 166 if (sBluetoothAvrcpInterface !=NULL) { 167 sBluetoothAvrcpInterface->cleanup(); 168 sBluetoothAvrcpInterface = NULL; 169 } 170 171 if (mCallbacksObj != NULL) { 172 env->DeleteGlobalRef(mCallbacksObj); 173 mCallbacksObj = NULL; 174 } 175} 176 177static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus, 178 jint songLen, jint songPos) { 179 bt_status_t status; 180 181 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 182 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 183 184 if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus, 185 songLen, songPos)) != BT_STATUS_SUCCESS) { 186 ALOGE("Failed get_play_status_rsp, status: %d", status); 187 } 188 189 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 190} 191 192 static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr, 193 jintArray attrIds, jobjectArray textArray) { 194 jint *attr; 195 bt_status_t status; 196 jstring text; 197 int i; 198 btrc_element_attr_val_t *pAttrs = NULL; 199 const char* textStr; 200 201 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 202 203 if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) { 204 ALOGE("get_element_attr_rsp: number of attributes exceed maximum"); 205 return JNI_FALSE; 206 } 207 208 pAttrs = new btrc_element_attr_val_t[numAttr]; 209 if (!pAttrs) { 210 ALOGE("get_element_attr_rsp: not have enough memeory"); 211 return JNI_FALSE; 212 } 213 214 attr = env->GetIntArrayElements(attrIds, NULL); 215 if (!attr) { 216 delete[] pAttrs; 217 jniThrowIOException(env, EINVAL); 218 return JNI_FALSE; 219 } 220 221 for (i = 0; i < numAttr; ++i) { 222 text = (jstring) env->GetObjectArrayElement(textArray, i); 223 textStr = env->GetStringUTFChars(text, NULL); 224 if (!textStr) { 225 ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL"); 226 env->DeleteLocalRef(text); 227 break; 228 } 229 230 pAttrs[i].attr_id = attr[i]; 231 if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) { 232 ALOGE("get_element_attr_rsp: string length exceed maximum"); 233 strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1); 234 pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0; 235 } else { 236 strcpy((char *)pAttrs[i].text, textStr); 237 } 238 env->ReleaseStringUTFChars(text, textStr); 239 env->DeleteLocalRef(text); 240 } 241 242 if (i < numAttr) { 243 delete[] pAttrs; 244 env->ReleaseIntArrayElements(attrIds, attr, 0); 245 return JNI_FALSE; 246 } 247 248 if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) != 249 BT_STATUS_SUCCESS) { 250 ALOGE("Failed get_element_attr_rsp, status: %d", status); 251 } 252 253 delete[] pAttrs; 254 env->ReleaseIntArrayElements(attrIds, attr, 0); 255 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 256} 257 258static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object, 259 jint type, jint playStatus) { 260 bt_status_t status; 261 btrc_register_notification_t param; 262 263 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 264 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 265 266 param.play_status = (btrc_play_status_t)playStatus; 267 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED, 268 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { 269 ALOGE("Failed register_notification_rsp play status, status: %d", status); 270 } 271 272 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 273} 274 275static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object, 276 jint type, jbyteArray track) { 277 bt_status_t status; 278 btrc_register_notification_t param; 279 jbyte *trk; 280 int i; 281 282 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 283 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 284 285 trk = env->GetByteArrayElements(track, NULL); 286 if (!trk) { 287 jniThrowIOException(env, EINVAL); 288 return JNI_FALSE; 289 } 290 291 for (i = 0; i < BTRC_UID_SIZE; ++i) { 292 param.track[i] = trk[i]; 293 } 294 295 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE, 296 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { 297 ALOGE("Failed register_notification_rsp track change, status: %d", status); 298 } 299 300 env->ReleaseByteArrayElements(track, trk, 0); 301 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 302} 303 304static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object, 305 jint type, jint playPos) { 306 bt_status_t status; 307 btrc_register_notification_t param; 308 309 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 310 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 311 312 param.song_pos = (uint32_t)playPos; 313 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED, 314 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { 315 ALOGE("Failed register_notification_rsp play position, status: %d", status); 316 } 317 318 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 319} 320 321static JNINativeMethod sMethods[] = { 322 {"classInitNative", "()V", (void *) classInitNative}, 323 {"initNative", "()V", (void *) initNative}, 324 {"cleanupNative", "()V", (void *) cleanupNative}, 325 {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative}, 326 {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative}, 327 {"registerNotificationRspPlayStatusNative", "(II)Z", 328 (void *) registerNotificationRspPlayStatusNative}, 329 {"registerNotificationRspTrackChangeNative", "(I[B)Z", 330 (void *) registerNotificationRspTrackChangeNative}, 331 {"registerNotificationRspPlayPosNative", "(II)Z", 332 (void *) registerNotificationRspPlayPosNative}, 333}; 334 335int register_com_android_bluetooth_avrcp(JNIEnv* env) 336{ 337 return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/Avrcp", 338 sMethods, NELEM(sMethods)); 339} 340 341} 342