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