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