1/* 2 * Copyright (C) 2018 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 "NewAvrcpTargetJni" 18 19#include <base/bind.h> 20#include <map> 21#include <mutex> 22#include <shared_mutex> 23#include <vector> 24 25#include "android_runtime/AndroidRuntime.h" 26#include "avrcp.h" 27#include "com_android_bluetooth.h" 28#include "utils/Log.h" 29 30using namespace bluetooth::avrcp; 31 32namespace android { 33 34// Static Variables 35static MediaCallbacks* mServiceCallbacks; 36static ServiceInterface* sServiceInterface; 37static jobject mJavaInterface; 38static std::shared_timed_mutex interface_mutex; 39static std::shared_timed_mutex callbacks_mutex; 40 41// Forward Declarations 42static void sendMediaKeyEvent(int, KeyState); 43static std::string getCurrentMediaId(); 44static SongInfo getSongInfo(); 45static PlayStatus getCurrentPlayStatus(); 46static std::vector<SongInfo> getNowPlayingList(); 47static uint16_t getCurrentPlayerId(); 48static std::vector<MediaPlayerInfo> getMediaPlayerList(); 49using SetBrowsedPlayerCb = MediaInterface::SetBrowsedPlayerCallback; 50static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb); 51using GetFolderItemsCb = MediaInterface::FolderItemsCallback; 52static void getFolderItems(uint16_t player_id, std::string media_id, 53 GetFolderItemsCb cb); 54static void playItem(uint16_t player_id, bool now_playing, 55 std::string media_id); 56static void setActiveDevice(const RawAddress& address); 57 58static void volumeDeviceConnected(const RawAddress& address); 59static void volumeDeviceConnected( 60 const RawAddress& address, 61 ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb); 62static void volumeDeviceDisconnected(const RawAddress& address); 63static void setVolume(int8_t volume); 64 65// TODO (apanicke): In the future, this interface should guarantee that 66// all calls happen on the JNI Thread. Right now this is very difficult 67// as it is hard to get a handle on the JNI thread from here. 68class AvrcpMediaInterfaceImpl : public MediaInterface { 69 public: 70 void SendKeyEvent(uint8_t key, KeyState state) { 71 sendMediaKeyEvent(key, state); 72 } 73 74 void GetSongInfo(SongInfoCallback cb) override { 75 auto info = getSongInfo(); 76 cb.Run(info); 77 } 78 79 void GetPlayStatus(PlayStatusCallback cb) override { 80 auto status = getCurrentPlayStatus(); 81 cb.Run(status); 82 } 83 84 void GetNowPlayingList(NowPlayingCallback cb) override { 85 auto curr_song_id = getCurrentMediaId(); 86 auto now_playing_list = getNowPlayingList(); 87 cb.Run(curr_song_id, std::move(now_playing_list)); 88 } 89 90 void GetMediaPlayerList(MediaListCallback cb) override { 91 uint16_t current_player = getCurrentPlayerId(); 92 auto player_list = getMediaPlayerList(); 93 cb.Run(current_player, std::move(player_list)); 94 } 95 96 void GetFolderItems(uint16_t player_id, std::string media_id, 97 FolderItemsCallback folder_cb) override { 98 getFolderItems(player_id, media_id, folder_cb); 99 } 100 101 void SetBrowsedPlayer(uint16_t player_id, 102 SetBrowsedPlayerCallback browse_cb) override { 103 setBrowsedPlayer(player_id, browse_cb); 104 } 105 106 void RegisterUpdateCallback(MediaCallbacks* callback) override { 107 // TODO (apanicke): Allow multiple registrations in the future 108 mServiceCallbacks = callback; 109 } 110 111 void UnregisterUpdateCallback(MediaCallbacks* callback) override { 112 mServiceCallbacks = nullptr; 113 } 114 115 void PlayItem(uint16_t player_id, bool now_playing, 116 std::string media_id) override { 117 playItem(player_id, now_playing, media_id); 118 } 119 120 void SetActiveDevice(const RawAddress& address) override { 121 setActiveDevice(address); 122 } 123}; 124static AvrcpMediaInterfaceImpl mAvrcpInterface; 125 126class VolumeInterfaceImpl : public VolumeInterface { 127 public: 128 void DeviceConnected(const RawAddress& bdaddr) override { 129 volumeDeviceConnected(bdaddr); 130 } 131 132 void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) override { 133 volumeDeviceConnected(bdaddr, cb); 134 } 135 136 void DeviceDisconnected(const RawAddress& bdaddr) override { 137 volumeDeviceDisconnected(bdaddr); 138 } 139 140 void SetVolume(int8_t volume) override { setVolume(volume); } 141}; 142static VolumeInterfaceImpl mVolumeInterface; 143 144static jmethodID method_getCurrentSongInfo; 145static jmethodID method_getPlaybackStatus; 146static jmethodID method_sendMediaKeyEvent; 147 148static jmethodID method_getCurrentMediaId; 149static jmethodID method_getNowPlayingList; 150 151static jmethodID method_setBrowsedPlayer; 152static jmethodID method_getCurrentPlayerId; 153static jmethodID method_getMediaPlayerList; 154static jmethodID method_getFolderItemsRequest; 155static jmethodID method_playItem; 156 157static jmethodID method_setActiveDevice; 158 159static jmethodID method_volumeDeviceConnected; 160static jmethodID method_volumeDeviceDisconnected; 161 162static jmethodID method_setVolume; 163 164static void classInitNative(JNIEnv* env, jclass clazz) { 165 method_getCurrentSongInfo = env->GetMethodID( 166 clazz, "getCurrentSongInfo", "()Lcom/android/bluetooth/avrcp/Metadata;"); 167 168 method_getPlaybackStatus = env->GetMethodID( 169 clazz, "getPlayStatus", "()Lcom/android/bluetooth/avrcp/PlayStatus;"); 170 171 method_sendMediaKeyEvent = 172 env->GetMethodID(clazz, "sendMediaKeyEvent", "(IZ)V"); 173 174 method_getCurrentMediaId = 175 env->GetMethodID(clazz, "getCurrentMediaId", "()Ljava/lang/String;"); 176 177 method_getNowPlayingList = 178 env->GetMethodID(clazz, "getNowPlayingList", "()Ljava/util/List;"); 179 180 method_getCurrentPlayerId = 181 env->GetMethodID(clazz, "getCurrentPlayerId", "()I"); 182 183 method_getMediaPlayerList = 184 env->GetMethodID(clazz, "getMediaPlayerList", "()Ljava/util/List;"); 185 186 method_setBrowsedPlayer = env->GetMethodID(clazz, "setBrowsedPlayer", "(I)V"); 187 188 method_getFolderItemsRequest = env->GetMethodID( 189 clazz, "getFolderItemsRequest", "(ILjava/lang/String;)V"); 190 191 method_playItem = 192 env->GetMethodID(clazz, "playItem", "(IZLjava/lang/String;)V"); 193 194 method_setActiveDevice = 195 env->GetMethodID(clazz, "setActiveDevice", "(Ljava/lang/String;)V"); 196 197 // Volume Management functions 198 method_volumeDeviceConnected = 199 env->GetMethodID(clazz, "deviceConnected", "(Ljava/lang/String;Z)V"); 200 201 method_volumeDeviceDisconnected = 202 env->GetMethodID(clazz, "deviceDisconnected", "(Ljava/lang/String;)V"); 203 204 method_setVolume = env->GetMethodID(clazz, "setVolume", "(I)V"); 205 206 ALOGI("%s: AvrcpTargetJni initialized!", __func__); 207} 208 209static void initNative(JNIEnv* env, jobject object) { 210 ALOGD("%s", __func__); 211 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); 212 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex); 213 mJavaInterface = env->NewGlobalRef(object); 214 215 sServiceInterface = getBluetoothInterface()->get_avrcp_service(); 216 sServiceInterface->Init(&mAvrcpInterface, &mVolumeInterface); 217} 218 219static void sendMediaUpdateNative(JNIEnv* env, jobject object, 220 jboolean metadata, jboolean state, 221 jboolean queue) { 222 ALOGD("%s", __func__); 223 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); 224 if (mServiceCallbacks == nullptr) { 225 ALOGW("%s: Service not loaded.", __func__); 226 return; 227 } 228 229 mServiceCallbacks->SendMediaUpdate(metadata == JNI_TRUE, state == JNI_TRUE, 230 queue == JNI_TRUE); 231} 232 233static void sendFolderUpdateNative(JNIEnv* env, jobject object, 234 jboolean available_players, 235 jboolean addressed_player, jboolean uids) { 236 ALOGD("%s", __func__); 237 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); 238 if (mServiceCallbacks == nullptr) { 239 ALOGW("%s: Service not loaded.", __func__); 240 return; 241 } 242 243 mServiceCallbacks->SendFolderUpdate(available_players == JNI_TRUE, 244 addressed_player == JNI_TRUE, 245 uids == JNI_TRUE); 246} 247 248static void cleanupNative(JNIEnv* env, jobject object) { 249 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); 250 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex); 251 252 sServiceInterface->Cleanup(); 253 env->DeleteGlobalRef(mJavaInterface); 254 mJavaInterface = nullptr; 255 mServiceCallbacks = nullptr; 256 sServiceInterface = nullptr; 257} 258 259jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) { 260 ALOGD("%s", __func__); 261 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); 262 if (mServiceCallbacks == nullptr) { 263 ALOGW("%s: Service not loaded.", __func__); 264 return JNI_FALSE; 265 } 266 267 const char* tmp_addr = env->GetStringUTFChars(address, 0); 268 RawAddress bdaddr; 269 bool success = RawAddress::FromString(tmp_addr, bdaddr); 270 env->ReleaseStringUTFChars(address, tmp_addr); 271 272 if (!success) return JNI_FALSE; 273 274 return sServiceInterface->ConnectDevice(bdaddr) == true ? JNI_TRUE 275 : JNI_FALSE; 276} 277 278jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) { 279 ALOGD("%s", __func__); 280 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex); 281 if (mServiceCallbacks == nullptr) { 282 ALOGW("%s: Service not loaded.", __func__); 283 return JNI_FALSE; 284 } 285 286 const char* tmp_addr = env->GetStringUTFChars(address, 0); 287 RawAddress bdaddr; 288 bool success = RawAddress::FromString(tmp_addr, bdaddr); 289 env->ReleaseStringUTFChars(address, tmp_addr); 290 291 if (!success) return JNI_FALSE; 292 293 return sServiceInterface->DisconnectDevice(bdaddr) == true ? JNI_TRUE 294 : JNI_FALSE; 295} 296 297static void sendMediaKeyEvent(int key, KeyState state) { 298 ALOGD("%s", __func__); 299 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 300 CallbackEnv sCallbackEnv(__func__); 301 if (!sCallbackEnv.valid() || !mJavaInterface) return; 302 sCallbackEnv->CallVoidMethod( 303 mJavaInterface, method_sendMediaKeyEvent, key, 304 state == KeyState::PUSHED ? JNI_TRUE : JNI_FALSE); 305} 306 307static SongInfo getSongInfoFromJavaObj(JNIEnv* env, jobject metadata) { 308 SongInfo info; 309 310 if (metadata == nullptr) return info; 311 312 jclass class_metadata = env->GetObjectClass(metadata); 313 jfieldID field_mediaId = 314 env->GetFieldID(class_metadata, "mediaId", "Ljava/lang/String;"); 315 jfieldID field_title = 316 env->GetFieldID(class_metadata, "title", "Ljava/lang/String;"); 317 jfieldID field_artist = 318 env->GetFieldID(class_metadata, "artist", "Ljava/lang/String;"); 319 jfieldID field_album = 320 env->GetFieldID(class_metadata, "album", "Ljava/lang/String;"); 321 jfieldID field_trackNum = 322 env->GetFieldID(class_metadata, "trackNum", "Ljava/lang/String;"); 323 jfieldID field_numTracks = 324 env->GetFieldID(class_metadata, "numTracks", "Ljava/lang/String;"); 325 jfieldID field_genre = 326 env->GetFieldID(class_metadata, "genre", "Ljava/lang/String;"); 327 jfieldID field_playingTime = 328 env->GetFieldID(class_metadata, "duration", "Ljava/lang/String;"); 329 330 jstring jstr = (jstring)env->GetObjectField(metadata, field_mediaId); 331 if (jstr != nullptr) { 332 const char* value = env->GetStringUTFChars(jstr, nullptr); 333 info.media_id = std::string(value); 334 env->ReleaseStringUTFChars(jstr, value); 335 env->DeleteLocalRef(jstr); 336 } 337 338 jstr = (jstring)env->GetObjectField(metadata, field_title); 339 if (jstr != nullptr) { 340 const char* value = env->GetStringUTFChars(jstr, nullptr); 341 info.attributes.insert( 342 AttributeEntry(Attribute::TITLE, std::string(value))); 343 env->ReleaseStringUTFChars(jstr, value); 344 env->DeleteLocalRef(jstr); 345 } 346 347 jstr = (jstring)env->GetObjectField(metadata, field_artist); 348 if (jstr != nullptr) { 349 const char* value = env->GetStringUTFChars(jstr, nullptr); 350 info.attributes.insert( 351 AttributeEntry(Attribute::ARTIST_NAME, std::string(value))); 352 env->ReleaseStringUTFChars(jstr, value); 353 env->DeleteLocalRef(jstr); 354 } 355 356 jstr = (jstring)env->GetObjectField(metadata, field_album); 357 if (jstr != nullptr) { 358 const char* value = env->GetStringUTFChars(jstr, nullptr); 359 info.attributes.insert( 360 AttributeEntry(Attribute::ALBUM_NAME, std::string(value))); 361 env->ReleaseStringUTFChars(jstr, value); 362 env->DeleteLocalRef(jstr); 363 } 364 365 jstr = (jstring)env->GetObjectField(metadata, field_trackNum); 366 if (jstr != nullptr) { 367 const char* value = env->GetStringUTFChars(jstr, nullptr); 368 info.attributes.insert( 369 AttributeEntry(Attribute::TRACK_NUMBER, std::string(value))); 370 env->ReleaseStringUTFChars(jstr, value); 371 env->DeleteLocalRef(jstr); 372 } 373 374 jstr = (jstring)env->GetObjectField(metadata, field_numTracks); 375 if (jstr != nullptr) { 376 const char* value = env->GetStringUTFChars(jstr, nullptr); 377 info.attributes.insert( 378 AttributeEntry(Attribute::TOTAL_NUMBER_OF_TRACKS, std::string(value))); 379 env->ReleaseStringUTFChars(jstr, value); 380 env->DeleteLocalRef(jstr); 381 } 382 383 jstr = (jstring)env->GetObjectField(metadata, field_genre); 384 if (jstr != nullptr) { 385 const char* value = env->GetStringUTFChars(jstr, nullptr); 386 info.attributes.insert( 387 AttributeEntry(Attribute::GENRE, std::string(value))); 388 env->ReleaseStringUTFChars(jstr, value); 389 env->DeleteLocalRef(jstr); 390 } 391 392 jstr = (jstring)env->GetObjectField(metadata, field_playingTime); 393 if (jstr != nullptr) { 394 const char* value = env->GetStringUTFChars(jstr, nullptr); 395 info.attributes.insert( 396 AttributeEntry(Attribute::PLAYING_TIME, std::string(value))); 397 env->ReleaseStringUTFChars(jstr, value); 398 env->DeleteLocalRef(jstr); 399 } 400 401 return info; 402} 403 404static FolderInfo getFolderInfoFromJavaObj(JNIEnv* env, jobject folder) { 405 FolderInfo info; 406 407 jclass class_folder = env->GetObjectClass(folder); 408 jfieldID field_mediaId = 409 env->GetFieldID(class_folder, "mediaId", "Ljava/lang/String;"); 410 jfieldID field_isPlayable = env->GetFieldID(class_folder, "isPlayable", "Z"); 411 jfieldID field_name = 412 env->GetFieldID(class_folder, "title", "Ljava/lang/String;"); 413 414 jstring jstr = (jstring)env->GetObjectField(folder, field_mediaId); 415 if (jstr != nullptr) { 416 const char* value = env->GetStringUTFChars(jstr, nullptr); 417 info.media_id = std::string(value); 418 env->ReleaseStringUTFChars(jstr, value); 419 } 420 421 info.is_playable = env->GetBooleanField(folder, field_isPlayable) == JNI_TRUE; 422 423 jstr = (jstring)env->GetObjectField(folder, field_name); 424 if (jstr != nullptr) { 425 const char* value = env->GetStringUTFChars(jstr, nullptr); 426 info.name = std::string(value); 427 env->ReleaseStringUTFChars(jstr, value); 428 } 429 430 return info; 431} 432 433static SongInfo getSongInfo() { 434 ALOGD("%s", __func__); 435 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 436 CallbackEnv sCallbackEnv(__func__); 437 if (!sCallbackEnv.valid() || !mJavaInterface) return SongInfo(); 438 439 jobject metadata = 440 sCallbackEnv->CallObjectMethod(mJavaInterface, method_getCurrentSongInfo); 441 return getSongInfoFromJavaObj(sCallbackEnv.get(), metadata); 442} 443 444static PlayStatus getCurrentPlayStatus() { 445 ALOGD("%s", __func__); 446 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 447 CallbackEnv sCallbackEnv(__func__); 448 if (!sCallbackEnv.valid() || !mJavaInterface) return PlayStatus(); 449 450 PlayStatus status; 451 jobject playStatus = 452 sCallbackEnv->CallObjectMethod(mJavaInterface, method_getPlaybackStatus); 453 454 if (playStatus == nullptr) { 455 ALOGE("%s: Got a null play status", __func__); 456 return status; 457 } 458 459 jclass class_playStatus = sCallbackEnv->GetObjectClass(playStatus); 460 jfieldID field_position = 461 sCallbackEnv->GetFieldID(class_playStatus, "position", "J"); 462 jfieldID field_duration = 463 sCallbackEnv->GetFieldID(class_playStatus, "duration", "J"); 464 jfieldID field_state = 465 sCallbackEnv->GetFieldID(class_playStatus, "state", "B"); 466 467 status.position = sCallbackEnv->GetLongField(playStatus, field_position); 468 status.duration = sCallbackEnv->GetLongField(playStatus, field_duration); 469 status.state = (PlayState)sCallbackEnv->GetByteField(playStatus, field_state); 470 471 return status; 472} 473 474static std::string getCurrentMediaId() { 475 ALOGD("%s", __func__); 476 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 477 CallbackEnv sCallbackEnv(__func__); 478 if (!sCallbackEnv.valid() || !mJavaInterface) return ""; 479 480 jstring media_id = (jstring)sCallbackEnv->CallObjectMethod( 481 mJavaInterface, method_getCurrentMediaId); 482 if (media_id == nullptr) { 483 ALOGE("%s: Got a null media ID", __func__); 484 return ""; 485 } 486 487 const char* value = sCallbackEnv->GetStringUTFChars(media_id, nullptr); 488 std::string ret(value); 489 sCallbackEnv->ReleaseStringUTFChars(media_id, value); 490 return ret; 491} 492 493static std::vector<SongInfo> getNowPlayingList() { 494 ALOGD("%s", __func__); 495 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 496 CallbackEnv sCallbackEnv(__func__); 497 if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector<SongInfo>(); 498 499 jobject song_list = 500 sCallbackEnv->CallObjectMethod(mJavaInterface, method_getNowPlayingList); 501 if (song_list == nullptr) { 502 ALOGE("%s: Got a null now playing list", __func__); 503 return std::vector<SongInfo>(); 504 } 505 506 jclass class_list = sCallbackEnv->GetObjectClass(song_list); 507 jmethodID method_get = 508 sCallbackEnv->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;"); 509 jmethodID method_size = sCallbackEnv->GetMethodID(class_list, "size", "()I"); 510 511 auto size = sCallbackEnv->CallIntMethod(song_list, method_size); 512 if (size == 0) return std::vector<SongInfo>(); 513 std::vector<SongInfo> ret; 514 for (int i = 0; i < size; i++) { 515 jobject song = sCallbackEnv->CallObjectMethod(song_list, method_get, i); 516 ret.push_back(getSongInfoFromJavaObj(sCallbackEnv.get(), song)); 517 sCallbackEnv->DeleteLocalRef(song); 518 } 519 520 return ret; 521} 522 523static uint16_t getCurrentPlayerId() { 524 ALOGD("%s", __func__); 525 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 526 CallbackEnv sCallbackEnv(__func__); 527 if (!sCallbackEnv.valid() || !mJavaInterface) return 0u; 528 529 jint id = 530 sCallbackEnv->CallIntMethod(mJavaInterface, method_getCurrentPlayerId); 531 532 return (static_cast<int>(id) & 0xFFFF); 533} 534 535static std::vector<MediaPlayerInfo> getMediaPlayerList() { 536 ALOGD("%s", __func__); 537 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 538 CallbackEnv sCallbackEnv(__func__); 539 if (!sCallbackEnv.valid() || !mJavaInterface) 540 return std::vector<MediaPlayerInfo>(); 541 542 jobject player_list = (jobject)sCallbackEnv->CallObjectMethod( 543 mJavaInterface, method_getMediaPlayerList); 544 545 if (player_list == nullptr) { 546 ALOGE("%s: Got a null media player list", __func__); 547 return std::vector<MediaPlayerInfo>(); 548 } 549 550 jclass class_list = sCallbackEnv->GetObjectClass(player_list); 551 jmethodID method_get = 552 sCallbackEnv->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;"); 553 jmethodID method_size = sCallbackEnv->GetMethodID(class_list, "size", "()I"); 554 555 jint list_size = sCallbackEnv->CallIntMethod(player_list, method_size); 556 if (list_size == 0) { 557 return std::vector<MediaPlayerInfo>(); 558 } 559 560 jclass class_playerInfo = sCallbackEnv->GetObjectClass( 561 sCallbackEnv->CallObjectMethod(player_list, method_get, 0)); 562 jfieldID field_playerId = 563 sCallbackEnv->GetFieldID(class_playerInfo, "id", "I"); 564 jfieldID field_name = 565 sCallbackEnv->GetFieldID(class_playerInfo, "name", "Ljava/lang/String;"); 566 jfieldID field_browsable = 567 sCallbackEnv->GetFieldID(class_playerInfo, "browsable", "Z"); 568 569 std::vector<MediaPlayerInfo> ret_list; 570 for (jsize i = 0; i < list_size; i++) { 571 jobject player = sCallbackEnv->CallObjectMethod(player_list, method_get, i); 572 573 MediaPlayerInfo temp; 574 temp.id = sCallbackEnv->GetIntField(player, field_playerId); 575 576 jstring jstr = (jstring)sCallbackEnv->GetObjectField(player, field_name); 577 if (jstr != nullptr) { 578 const char* value = sCallbackEnv->GetStringUTFChars(jstr, nullptr); 579 temp.name = std::string(value); 580 sCallbackEnv->ReleaseStringUTFChars(jstr, value); 581 sCallbackEnv->DeleteLocalRef(jstr); 582 } 583 584 temp.browsing_supported = 585 sCallbackEnv->GetBooleanField(player, field_browsable) == JNI_TRUE 586 ? true 587 : false; 588 589 ret_list.push_back(std::move(temp)); 590 sCallbackEnv->DeleteLocalRef(player); 591 } 592 593 return ret_list; 594} 595 596// TODO (apanicke): Use a map here to store the callback in order to 597// support multi-browsing 598SetBrowsedPlayerCb set_browsed_player_cb; 599 600static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb cb) { 601 ALOGD("%s", __func__); 602 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 603 CallbackEnv sCallbackEnv(__func__); 604 if (!sCallbackEnv.valid() || !mJavaInterface) return; 605 606 set_browsed_player_cb = cb; 607 sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer, 608 player_id); 609} 610 611static void setBrowsedPlayerResponseNative(JNIEnv* env, jobject object, 612 jint player_id, jboolean success, 613 jstring root_id, jint num_items) { 614 ALOGD("%s", __func__); 615 616 std::string root; 617 if (root_id != nullptr) { 618 const char* value = env->GetStringUTFChars(root_id, nullptr); 619 root = std::string(value); 620 env->ReleaseStringUTFChars(root_id, value); 621 } 622 623 set_browsed_player_cb.Run(success == JNI_TRUE, root, num_items); 624} 625 626using map_entry = std::pair<std::string, GetFolderItemsCb>; 627std::map<std::string, GetFolderItemsCb> get_folder_items_cb_map; 628 629static void getFolderItemsResponseNative(JNIEnv* env, jobject object, 630 jstring parent_id, jobject list) { 631 ALOGD("%s", __func__); 632 633 std::string id; 634 if (parent_id != nullptr) { 635 const char* value = env->GetStringUTFChars(parent_id, nullptr); 636 id = std::string(value); 637 env->ReleaseStringUTFChars(parent_id, value); 638 } 639 640 // TODO (apanicke): Right now browsing will fail on a second device if two 641 // devices browse the same folder. Use a MultiMap to fix this behavior so 642 // that both callbacks can be handled with one lookup if a request comes 643 // for a folder that is already trying to be looked at. 644 if (get_folder_items_cb_map.find(id) == get_folder_items_cb_map.end()) { 645 ALOGE("Could not find response callback for the request of \"%s\"", 646 id.c_str()); 647 return; 648 } 649 650 auto callback = get_folder_items_cb_map.find(id)->second; 651 get_folder_items_cb_map.erase(id); 652 653 if (list == nullptr) { 654 ALOGE("%s: Got a null get folder items response list", __func__); 655 callback.Run(std::vector<ListItem>()); 656 return; 657 } 658 659 jclass class_list = env->GetObjectClass(list); 660 jmethodID method_get = 661 env->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;"); 662 jmethodID method_size = env->GetMethodID(class_list, "size", "()I"); 663 664 jint list_size = env->CallIntMethod(list, method_size); 665 if (list_size == 0) { 666 callback.Run(std::vector<ListItem>()); 667 return; 668 } 669 670 jclass class_listItem = 671 env->GetObjectClass(env->CallObjectMethod(list, method_get, 0)); 672 jfieldID field_isFolder = env->GetFieldID(class_listItem, "isFolder", "Z"); 673 jfieldID field_folder = env->GetFieldID( 674 class_listItem, "folder", "Lcom/android/bluetooth/avrcp/Folder;"); 675 jfieldID field_song = env->GetFieldID( 676 class_listItem, "song", "Lcom/android/bluetooth/avrcp/Metadata;"); 677 678 std::vector<ListItem> ret_list; 679 for (jsize i = 0; i < list_size; i++) { 680 jobject item = env->CallObjectMethod(list, method_get, i); 681 682 bool is_folder = env->GetBooleanField(item, field_isFolder) == JNI_TRUE; 683 684 if (is_folder) { 685 ListItem temp = {ListItem::FOLDER, 686 getFolderInfoFromJavaObj( 687 env, env->GetObjectField(item, field_folder)), 688 SongInfo()}; 689 690 ret_list.push_back(temp); 691 } else { 692 ListItem temp = { 693 ListItem::SONG, FolderInfo(), 694 getSongInfoFromJavaObj(env, env->GetObjectField(item, field_song))}; 695 696 ret_list.push_back(temp); 697 } 698 env->DeleteLocalRef(item); 699 } 700 701 callback.Run(std::move(ret_list)); 702} 703 704static void getFolderItems(uint16_t player_id, std::string media_id, 705 GetFolderItemsCb cb) { 706 ALOGD("%s", __func__); 707 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 708 CallbackEnv sCallbackEnv(__func__); 709 if (!sCallbackEnv.valid() || !mJavaInterface) return; 710 711 // TODO (apanicke): Fix a potential media_id collision if two media players 712 // use the same media_id scheme or two devices browse the same content. 713 get_folder_items_cb_map.insert(map_entry(media_id, cb)); 714 715 jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str()); 716 sCallbackEnv->CallVoidMethod(mJavaInterface, method_getFolderItemsRequest, 717 player_id, j_media_id); 718} 719 720static void playItem(uint16_t player_id, bool now_playing, 721 std::string media_id) { 722 ALOGD("%s", __func__); 723 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 724 CallbackEnv sCallbackEnv(__func__); 725 if (!sCallbackEnv.valid() || !mJavaInterface) return; 726 727 jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str()); 728 sCallbackEnv->CallVoidMethod(mJavaInterface, method_playItem, player_id, 729 now_playing ? JNI_TRUE : JNI_FALSE, j_media_id); 730} 731 732static void setActiveDevice(const RawAddress& address) { 733 ALOGD("%s", __func__); 734 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 735 CallbackEnv sCallbackEnv(__func__); 736 if (!sCallbackEnv.valid() || !mJavaInterface) return; 737 738 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); 739 sCallbackEnv->CallVoidMethod(mJavaInterface, method_setActiveDevice, 740 j_bdaddr); 741} 742 743static void volumeDeviceConnected(const RawAddress& address) { 744 ALOGD("%s", __func__); 745 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 746 CallbackEnv sCallbackEnv(__func__); 747 if (!sCallbackEnv.valid() || !mJavaInterface) return; 748 749 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); 750 sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected, 751 j_bdaddr, JNI_FALSE); 752} 753 754std::map<RawAddress, ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb> 755 volumeCallbackMap; 756 757static void volumeDeviceConnected( 758 const RawAddress& address, 759 ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb) { 760 ALOGD("%s", __func__); 761 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 762 CallbackEnv sCallbackEnv(__func__); 763 if (!sCallbackEnv.valid() || !mJavaInterface) return; 764 765 volumeCallbackMap.emplace(address, cb); 766 767 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); 768 sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected, 769 j_bdaddr, JNI_TRUE); 770} 771 772static void volumeDeviceDisconnected(const RawAddress& address) { 773 ALOGD("%s", __func__); 774 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 775 CallbackEnv sCallbackEnv(__func__); 776 if (!sCallbackEnv.valid() || !mJavaInterface) return; 777 778 volumeCallbackMap.erase(address); 779 780 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str()); 781 sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceDisconnected, 782 j_bdaddr); 783} 784 785static void sendVolumeChangedNative(JNIEnv* env, jobject object, jint volume) { 786 ALOGD("%s", __func__); 787 for (const auto& cb : volumeCallbackMap) { 788 cb.second.Run(volume & 0x7F); 789 } 790} 791 792static void setVolume(int8_t volume) { 793 ALOGD("%s", __func__); 794 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); 795 CallbackEnv sCallbackEnv(__func__); 796 if (!sCallbackEnv.valid() || !mJavaInterface) return; 797 798 sCallbackEnv->CallVoidMethod(mJavaInterface, method_setVolume, volume); 799} 800 801static JNINativeMethod sMethods[] = { 802 {"classInitNative", "()V", (void*)classInitNative}, 803 {"initNative", "()V", (void*)initNative}, 804 {"sendMediaUpdateNative", "(ZZZ)V", (void*)sendMediaUpdateNative}, 805 {"sendFolderUpdateNative", "(ZZZ)V", (void*)sendFolderUpdateNative}, 806 {"setBrowsedPlayerResponseNative", "(IZLjava/lang/String;I)V", 807 (void*)setBrowsedPlayerResponseNative}, 808 {"getFolderItemsResponseNative", "(Ljava/lang/String;Ljava/util/List;)V", 809 (void*)getFolderItemsResponseNative}, 810 {"cleanupNative", "()V", (void*)cleanupNative}, 811 {"connectDeviceNative", "(Ljava/lang/String;)Z", 812 (void*)connectDeviceNative}, 813 {"disconnectDeviceNative", "(Ljava/lang/String;)Z", 814 (void*)disconnectDeviceNative}, 815 {"sendVolumeChangedNative", "(I)V", (void*)sendVolumeChangedNative}, 816}; 817 818int register_com_android_bluetooth_avrcp_target(JNIEnv* env) { 819 return jniRegisterNativeMethods( 820 env, "com/android/bluetooth/avrcp/AvrcpNativeInterface", sMethods, 821 NELEM(sMethods)); 822} 823 824} // namespace android 825