• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 // 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.
68 class AvrcpMediaInterfaceImpl : public MediaInterface {
69  public:
SendKeyEvent(uint8_t key,KeyState state)70   void SendKeyEvent(uint8_t key, KeyState state) {
71     sendMediaKeyEvent(key, state);
72   }
73 
GetSongInfo(SongInfoCallback cb)74   void GetSongInfo(SongInfoCallback cb) override {
75     auto info = getSongInfo();
76     cb.Run(info);
77   }
78 
GetPlayStatus(PlayStatusCallback cb)79   void GetPlayStatus(PlayStatusCallback cb) override {
80     auto status = getCurrentPlayStatus();
81     cb.Run(status);
82   }
83 
GetNowPlayingList(NowPlayingCallback cb)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 
GetMediaPlayerList(MediaListCallback cb)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 
GetFolderItems(uint16_t player_id,std::string media_id,FolderItemsCallback folder_cb)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 
SetBrowsedPlayer(uint16_t player_id,SetBrowsedPlayerCallback browse_cb)101   void SetBrowsedPlayer(uint16_t player_id,
102                         SetBrowsedPlayerCallback browse_cb) override {
103     setBrowsedPlayer(player_id, browse_cb);
104   }
105 
RegisterUpdateCallback(MediaCallbacks * callback)106   void RegisterUpdateCallback(MediaCallbacks* callback) override {
107     // TODO (apanicke): Allow multiple registrations in the future
108     mServiceCallbacks = callback;
109   }
110 
UnregisterUpdateCallback(MediaCallbacks * callback)111   void UnregisterUpdateCallback(MediaCallbacks* callback) override {
112     mServiceCallbacks = nullptr;
113   }
114 
PlayItem(uint16_t player_id,bool now_playing,std::string media_id)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 
SetActiveDevice(const RawAddress & address)120   void SetActiveDevice(const RawAddress& address) override {
121     setActiveDevice(address);
122   }
123 };
124 static AvrcpMediaInterfaceImpl mAvrcpInterface;
125 
126 class VolumeInterfaceImpl : public VolumeInterface {
127  public:
DeviceConnected(const RawAddress & bdaddr)128   void DeviceConnected(const RawAddress& bdaddr) override {
129     volumeDeviceConnected(bdaddr);
130   }
131 
DeviceConnected(const RawAddress & bdaddr,VolumeChangedCb cb)132   void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) override {
133     volumeDeviceConnected(bdaddr, cb);
134   }
135 
DeviceDisconnected(const RawAddress & bdaddr)136   void DeviceDisconnected(const RawAddress& bdaddr) override {
137     volumeDeviceDisconnected(bdaddr);
138   }
139 
SetVolume(int8_t volume)140   void SetVolume(int8_t volume) override { setVolume(volume); }
141 };
142 static VolumeInterfaceImpl mVolumeInterface;
143 
144 static jmethodID method_getCurrentSongInfo;
145 static jmethodID method_getPlaybackStatus;
146 static jmethodID method_sendMediaKeyEvent;
147 
148 static jmethodID method_getCurrentMediaId;
149 static jmethodID method_getNowPlayingList;
150 
151 static jmethodID method_setBrowsedPlayer;
152 static jmethodID method_getCurrentPlayerId;
153 static jmethodID method_getMediaPlayerList;
154 static jmethodID method_getFolderItemsRequest;
155 static jmethodID method_playItem;
156 
157 static jmethodID method_setActiveDevice;
158 
159 static jmethodID method_volumeDeviceConnected;
160 static jmethodID method_volumeDeviceDisconnected;
161 
162 static jmethodID method_setVolume;
163 
classInitNative(JNIEnv * env,jclass clazz)164 static 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 
initNative(JNIEnv * env,jobject object)209 static 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 
sendMediaUpdateNative(JNIEnv * env,jobject object,jboolean metadata,jboolean state,jboolean queue)219 static 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 
sendFolderUpdateNative(JNIEnv * env,jobject object,jboolean available_players,jboolean addressed_player,jboolean uids)233 static 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 
cleanupNative(JNIEnv * env,jobject object)248 static 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 
connectDeviceNative(JNIEnv * env,jobject object,jstring address)259 jboolean 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 
disconnectDeviceNative(JNIEnv * env,jobject object,jstring address)278 jboolean 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 
sendMediaKeyEvent(int key,KeyState state)297 static 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 
getSongInfoFromJavaObj(JNIEnv * env,jobject metadata)307 static 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 
getFolderInfoFromJavaObj(JNIEnv * env,jobject folder)404 static 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 
getSongInfo()433 static 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 
getCurrentPlayStatus()444 static 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 
getCurrentMediaId()474 static 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 
getNowPlayingList()493 static 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 
getCurrentPlayerId()523 static 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 
getMediaPlayerList()535 static 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
598 SetBrowsedPlayerCb set_browsed_player_cb;
599 
setBrowsedPlayer(uint16_t player_id,SetBrowsedPlayerCb cb)600 static 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 
setBrowsedPlayerResponseNative(JNIEnv * env,jobject object,jint player_id,jboolean success,jstring root_id,jint num_items)611 static 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 
626 using map_entry = std::pair<std::string, GetFolderItemsCb>;
627 std::map<std::string, GetFolderItemsCb> get_folder_items_cb_map;
628 
getFolderItemsResponseNative(JNIEnv * env,jobject object,jstring parent_id,jobject list)629 static 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 
getFolderItems(uint16_t player_id,std::string media_id,GetFolderItemsCb cb)704 static 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 
playItem(uint16_t player_id,bool now_playing,std::string media_id)720 static 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 
setActiveDevice(const RawAddress & address)732 static 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 
volumeDeviceConnected(const RawAddress & address)743 static 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 
754 std::map<RawAddress, ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb>
755     volumeCallbackMap;
756 
volumeDeviceConnected(const RawAddress & address,::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb)757 static 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 
volumeDeviceDisconnected(const RawAddress & address)772 static 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 
sendVolumeChangedNative(JNIEnv * env,jobject object,jint volume)785 static 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 
setVolume(int8_t volume)792 static 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 
801 static 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 
register_com_android_bluetooth_avrcp_target(JNIEnv * env)818 int 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