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