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 28 namespace android { 29 static jmethodID method_getRcFeatures; 30 static jmethodID method_getPlayStatus; 31 static jmethodID method_getElementAttr; 32 static jmethodID method_registerNotification; 33 static jmethodID method_volumeChangeCallback; 34 static jmethodID method_handlePassthroughCmd; 35 36 static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; 37 static jobject mCallbacksObj = NULL; 38 static JNIEnv *sCallbackEnv = NULL; 39 40 static 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 52 static 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("Fail to new jbyteArray bd addr for connection state"); 63 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 64 return; 65 } 66 67 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); 68 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features); 69 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 70 } 71 72 static void btavrcp_get_play_status_callback() { 73 ALOGI("%s", __FUNCTION__); 74 75 if (!checkCallbackThread()) { 76 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 77 return; 78 } 79 80 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus); 81 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 82 } 83 84 static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) { 85 jintArray attrs; 86 87 ALOGI("%s", __FUNCTION__); 88 89 if (!checkCallbackThread()) { 90 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 91 return; 92 } 93 attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr); 94 if (!attrs) { 95 ALOGE("Fail to new jintArray for attrs"); 96 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 97 return; 98 } 99 sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs); 100 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs); 101 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 102 sCallbackEnv->DeleteLocalRef(attrs); 103 } 104 105 static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) { 106 ALOGI("%s", __FUNCTION__); 107 108 if (!checkCallbackThread()) { 109 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 110 return; 111 } 112 113 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification, 114 (jint)event_id, (jint)param); 115 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 116 } 117 118 static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) { 119 ALOGI("%s", __FUNCTION__); 120 121 if (!checkCallbackThread()) { 122 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 123 return; 124 } 125 126 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, (jint)volume, 127 (jint)ctype); 128 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 129 } 130 131 static void btavrcp_passthrough_command_callback(int id, int pressed) { 132 ALOGI("%s", __FUNCTION__); 133 134 if (!checkCallbackThread()) { 135 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); 136 return; 137 } 138 139 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, (jint)id, 140 (jint)pressed); 141 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); 142 } 143 144 static btrc_callbacks_t sBluetoothAvrcpCallbacks = { 145 sizeof(sBluetoothAvrcpCallbacks), 146 btavrcp_remote_features_callback, 147 btavrcp_get_play_status_callback, 148 NULL, 149 NULL, 150 NULL, 151 NULL, 152 NULL, 153 NULL, 154 btavrcp_get_element_attr_callback, 155 btavrcp_register_notification_callback, 156 btavrcp_volume_change_callback, 157 btavrcp_passthrough_command_callback 158 }; 159 160 static void classInitNative(JNIEnv* env, jclass clazz) { 161 method_getRcFeatures = 162 env->GetMethodID(clazz, "getRcFeatures", "([BI)V"); 163 method_getPlayStatus = 164 env->GetMethodID(clazz, "getPlayStatus", "()V"); 165 166 method_getElementAttr = 167 env->GetMethodID(clazz, "getElementAttr", "(B[I)V"); 168 169 method_registerNotification = 170 env->GetMethodID(clazz, "registerNotification", "(II)V"); 171 172 method_volumeChangeCallback = 173 env->GetMethodID(clazz, "volumeChangeCallback", "(II)V"); 174 175 method_handlePassthroughCmd = 176 env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V"); 177 178 ALOGI("%s: succeeds", __FUNCTION__); 179 } 180 181 static void initNative(JNIEnv *env, jobject object) { 182 const bt_interface_t* btInf; 183 bt_status_t status; 184 185 if ( (btInf = getBluetoothInterface()) == NULL) { 186 ALOGE("Bluetooth module is not loaded"); 187 return; 188 } 189 190 if (sBluetoothAvrcpInterface !=NULL) { 191 ALOGW("Cleaning up Avrcp Interface before initializing..."); 192 sBluetoothAvrcpInterface->cleanup(); 193 sBluetoothAvrcpInterface = NULL; 194 } 195 196 if (mCallbacksObj != NULL) { 197 ALOGW("Cleaning up Avrcp callback object"); 198 env->DeleteGlobalRef(mCallbacksObj); 199 mCallbacksObj = NULL; 200 } 201 202 if ( (sBluetoothAvrcpInterface = (btrc_interface_t *) 203 btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) { 204 ALOGE("Failed to get Bluetooth Avrcp Interface"); 205 return; 206 } 207 208 if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) != 209 BT_STATUS_SUCCESS) { 210 ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status); 211 sBluetoothAvrcpInterface = NULL; 212 return; 213 } 214 215 mCallbacksObj = env->NewGlobalRef(object); 216 } 217 218 static void cleanupNative(JNIEnv *env, jobject object) { 219 const bt_interface_t* btInf; 220 221 if ( (btInf = getBluetoothInterface()) == NULL) { 222 ALOGE("Bluetooth module is not loaded"); 223 return; 224 } 225 226 if (sBluetoothAvrcpInterface !=NULL) { 227 sBluetoothAvrcpInterface->cleanup(); 228 sBluetoothAvrcpInterface = NULL; 229 } 230 231 if (mCallbacksObj != NULL) { 232 env->DeleteGlobalRef(mCallbacksObj); 233 mCallbacksObj = NULL; 234 } 235 } 236 237 static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus, 238 jint songLen, jint songPos) { 239 bt_status_t status; 240 241 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 242 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 243 244 if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus, 245 songLen, songPos)) != BT_STATUS_SUCCESS) { 246 ALOGE("Failed get_play_status_rsp, status: %d", status); 247 } 248 249 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 250 } 251 252 static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr, 253 jintArray attrIds, jobjectArray textArray) { 254 jint *attr; 255 bt_status_t status; 256 jstring text; 257 int i; 258 btrc_element_attr_val_t *pAttrs = NULL; 259 const char* textStr; 260 261 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 262 263 if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) { 264 ALOGE("get_element_attr_rsp: number of attributes exceed maximum"); 265 return JNI_FALSE; 266 } 267 268 pAttrs = new btrc_element_attr_val_t[numAttr]; 269 if (!pAttrs) { 270 ALOGE("get_element_attr_rsp: not have enough memeory"); 271 return JNI_FALSE; 272 } 273 274 attr = env->GetIntArrayElements(attrIds, NULL); 275 if (!attr) { 276 delete[] pAttrs; 277 jniThrowIOException(env, EINVAL); 278 return JNI_FALSE; 279 } 280 281 for (i = 0; i < numAttr; ++i) { 282 text = (jstring) env->GetObjectArrayElement(textArray, i); 283 textStr = env->GetStringUTFChars(text, NULL); 284 if (!textStr) { 285 ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL"); 286 env->DeleteLocalRef(text); 287 break; 288 } 289 290 pAttrs[i].attr_id = attr[i]; 291 if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) { 292 ALOGE("get_element_attr_rsp: string length exceed maximum"); 293 strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1); 294 pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0; 295 } else { 296 strcpy((char *)pAttrs[i].text, textStr); 297 } 298 env->ReleaseStringUTFChars(text, textStr); 299 env->DeleteLocalRef(text); 300 } 301 302 if (i < numAttr) { 303 delete[] pAttrs; 304 env->ReleaseIntArrayElements(attrIds, attr, 0); 305 return JNI_FALSE; 306 } 307 308 if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) != 309 BT_STATUS_SUCCESS) { 310 ALOGE("Failed get_element_attr_rsp, status: %d", status); 311 } 312 313 delete[] pAttrs; 314 env->ReleaseIntArrayElements(attrIds, attr, 0); 315 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 316 } 317 318 static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object, 319 jint type, jint playStatus) { 320 bt_status_t status; 321 btrc_register_notification_t param; 322 323 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 324 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 325 326 param.play_status = (btrc_play_status_t)playStatus; 327 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED, 328 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { 329 ALOGE("Failed register_notification_rsp play status, status: %d", status); 330 } 331 332 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 333 } 334 335 static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object, 336 jint type, jbyteArray track) { 337 bt_status_t status; 338 btrc_register_notification_t param; 339 jbyte *trk; 340 int i; 341 342 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 343 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 344 345 trk = env->GetByteArrayElements(track, NULL); 346 if (!trk) { 347 jniThrowIOException(env, EINVAL); 348 return JNI_FALSE; 349 } 350 351 for (i = 0; i < BTRC_UID_SIZE; ++i) { 352 param.track[i] = trk[i]; 353 } 354 355 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE, 356 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { 357 ALOGE("Failed register_notification_rsp track change, status: %d", status); 358 } 359 360 env->ReleaseByteArrayElements(track, trk, 0); 361 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 362 } 363 364 static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object, 365 jint type, jint playPos) { 366 bt_status_t status; 367 btrc_register_notification_t param; 368 369 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 370 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 371 372 param.song_pos = (uint32_t)playPos; 373 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED, 374 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) { 375 ALOGE("Failed register_notification_rsp play position, status: %d", status); 376 } 377 378 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 379 } 380 381 static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) { 382 bt_status_t status; 383 384 //TODO: delete test code 385 ALOGI("%s: jint: %d, uint8_t: %u", __FUNCTION__, volume, (uint8_t) volume); 386 387 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); 388 if (!sBluetoothAvrcpInterface) return JNI_FALSE; 389 390 if ((status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume)) != BT_STATUS_SUCCESS) { 391 ALOGE("Failed set_volume, status: %d", status); 392 } 393 394 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; 395 } 396 397 static JNINativeMethod sMethods[] = { 398 {"classInitNative", "()V", (void *) classInitNative}, 399 {"initNative", "()V", (void *) initNative}, 400 {"cleanupNative", "()V", (void *) cleanupNative}, 401 {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative}, 402 {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative}, 403 {"registerNotificationRspPlayStatusNative", "(II)Z", 404 (void *) registerNotificationRspPlayStatusNative}, 405 {"registerNotificationRspTrackChangeNative", "(I[B)Z", 406 (void *) registerNotificationRspTrackChangeNative}, 407 {"registerNotificationRspPlayPosNative", "(II)Z", 408 (void *) registerNotificationRspPlayPosNative}, 409 {"setVolumeNative", "(I)Z", 410 (void *) setVolumeNative} 411 }; 412 413 int register_com_android_bluetooth_avrcp(JNIEnv* env) 414 { 415 return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/Avrcp", 416 sMethods, NELEM(sMethods)); 417 } 418 419 } 420