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