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