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, ¶m)) != 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, ¶m)) != 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, ¶m)) != 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