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