AddressedMediaPlayer.java revision 827c417f70082918b965a5213c38395398389811
1/* 2 * Copyright (C) 2016 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 17package com.android.bluetooth.avrcp; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.bluetooth.BluetoothAvrcp; 22import android.media.session.MediaSession; 23import android.media.session.PlaybackState; 24import android.media.session.MediaSession.QueueItem; 25import android.media.MediaDescription; 26import android.media.MediaMetadata; 27import android.os.Bundle; 28import android.util.Log; 29 30import com.android.bluetooth.btservice.ProfileService; 31import com.android.bluetooth.Utils; 32 33import java.nio.ByteBuffer; 34import java.util.List; 35import java.util.Arrays; 36import java.util.ArrayList; 37 38/************************************************************************************************* 39 * Provides functionality required for Addressed Media Player, like Now Playing List related 40 * browsing commands, control commands to the current addressed player(playItem, play, pause, etc) 41 * Acts as an Interface to communicate with media controller APIs for NowPlayingItems. 42 ************************************************************************************************/ 43 44public class AddressedMediaPlayer { 45 static private final String TAG = "AddressedMediaPlayer"; 46 static private final Boolean DEBUG = false; 47 48 static private final long SINGLE_QID = 1; 49 static private final String UNKNOWN_TITLE = "(unknown)"; 50 51 private AvrcpMediaRspInterface mMediaInterface; 52 private List<MediaSession.QueueItem> mNowPlayingList; 53 54 private final List<MediaSession.QueueItem> mUnknownNowPlayingList; 55 56 private long mLastTrackIdSent; 57 58 public AddressedMediaPlayer(AvrcpMediaRspInterface mediaInterface) { 59 mNowPlayingList = null; 60 mMediaInterface = mediaInterface; 61 mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID; 62 List<MediaSession.QueueItem> unknown = new ArrayList<MediaSession.QueueItem>(); 63 unknown.add(getCurrentQueueItem(null, SINGLE_QID)); 64 mUnknownNowPlayingList = unknown; 65 } 66 67 void cleanup() { 68 if (DEBUG) Log.v(TAG, "cleanup"); 69 mNowPlayingList = null; 70 mMediaInterface = null; 71 mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID; 72 } 73 74 /* get now playing list from addressed player */ 75 void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd.FolderItemsCmd reqObj, 76 @Nullable MediaController mediaController) { 77 if (DEBUG) Log.v(TAG, "getFolderItemsNowPlaying"); 78 if (mediaController == null) { 79 // No players (if a player exists, we would have selected it) 80 Log.e(TAG, "mediaController = null, sending no available players response"); 81 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, null); 82 return; 83 } 84 List<MediaSession.QueueItem> items = getNowPlayingList(mediaController); 85 getFolderItemsFilterAttr(bdaddr, reqObj, items, AvrcpConstants.BTRC_SCOPE_NOW_PLAYING, 86 reqObj.mStartItem, reqObj.mEndItem, mediaController); 87 } 88 89 /* get item attributes for item in now playing list */ 90 void getItemAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd itemAttr, 91 @Nullable MediaController mediaController) { 92 int status = AvrcpConstants.RSP_NO_ERROR; 93 long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong(); 94 List<MediaSession.QueueItem> items = getNowPlayingList(mediaController); 95 96 /* checking if item attributes has been asked for now playing item or 97 * some other item with specific media id */ 98 if (Arrays.equals(itemAttr.mUid, AvrcpConstants.TRACK_IS_SELECTED)) { 99 if (DEBUG) Log.d(TAG, "getItemAttr: Remote requests for now playing contents:"); 100 101 // get the current playing metadata and send. 102 getItemAttrFilterAttr(bdaddr, itemAttr, getCurrentQueueItem(mediaController, mediaId), 103 mediaController); 104 return; 105 } 106 107 if (DEBUG) Log.d(TAG, "getItemAttr-UID: 0x" + Utils.byteArrayToString(itemAttr.mUid)); 108 for (MediaSession.QueueItem item : items) { 109 if (item.getQueueId() == mediaId) { 110 getItemAttrFilterAttr(bdaddr, itemAttr, item, mediaController); 111 return; 112 } 113 } 114 115 // Couldn't find it, so the id is invalid 116 mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_INV_ITEM, null); 117 } 118 119 /* Refresh and get the queue of now playing. 120 */ 121 private List<MediaSession.QueueItem> getNowPlayingList( 122 @Nullable MediaController mediaController) { 123 if (mediaController == null) return mUnknownNowPlayingList; 124 if (mNowPlayingList != null) return mNowPlayingList; 125 List<MediaSession.QueueItem> items = mediaController.getQueue(); 126 if (items == null) { 127 Log.i(TAG, "null queue from " + mediaController.getPackageName() 128 + ", constructing current-item list"); 129 MediaMetadata metadata = mediaController.getMetadata(); 130 // Because we are database-unaware, we can just number the item here whatever we want 131 // because they have to re-poll it every time. 132 MediaSession.QueueItem current = getCurrentQueueItem(mediaController, SINGLE_QID); 133 items = new ArrayList<MediaSession.QueueItem>(); 134 items.add(current); 135 } 136 mNowPlayingList = items; 137 return items; 138 } 139 140 /* Constructs a queue item representing the current playing metadata from an 141 * active controller with queue id |qid|. 142 */ 143 private MediaSession.QueueItem getCurrentQueueItem( 144 @Nullable MediaController controller, long qid) { 145 if (controller == null) { 146 MediaDescription.Builder bob = new MediaDescription.Builder(); 147 bob.setTitle(UNKNOWN_TITLE); 148 return new QueueItem(bob.build(), qid); 149 } 150 151 MediaMetadata metadata = controller.getMetadata(); 152 if (metadata == null) { 153 Log.w(TAG, "Controller has no metadata!? Making an empty one"); 154 metadata = (new MediaMetadata.Builder()).build(); 155 } 156 157 MediaDescription.Builder bob = new MediaDescription.Builder(); 158 MediaDescription desc = metadata.getDescription(); 159 160 // set the simple ones that MediaMetadata builds for us 161 bob.setMediaId(desc.getMediaId()); 162 bob.setTitle(desc.getTitle()); 163 bob.setSubtitle(desc.getSubtitle()); 164 bob.setDescription(desc.getDescription()); 165 // fill the ones that we use later 166 bob.setExtras(fillBundle(metadata, desc.getExtras())); 167 168 // build queue item with the new metadata 169 desc = bob.build(); 170 return new QueueItem(desc, qid); 171 } 172 173 private Bundle fillBundle(MediaMetadata metadata, Bundle currentExtras) { 174 if (metadata == null) { 175 return currentExtras; 176 } 177 178 Bundle bundle = currentExtras; 179 if (bundle == null) bundle = new Bundle(); 180 181 String[] stringKeys = {MediaMetadata.METADATA_KEY_TITLE, MediaMetadata.METADATA_KEY_ARTIST, 182 MediaMetadata.METADATA_KEY_ALBUM, MediaMetadata.METADATA_KEY_GENRE}; 183 for (String key : stringKeys) { 184 String current = bundle.getString(key); 185 if (current == null) bundle.putString(key, metadata.getString(key)); 186 } 187 188 String[] longKeys = {MediaMetadata.METADATA_KEY_TRACK_NUMBER, 189 MediaMetadata.METADATA_KEY_NUM_TRACKS, MediaMetadata.METADATA_KEY_DURATION}; 190 for (String key : longKeys) { 191 if (!bundle.containsKey(key)) bundle.putLong(key, metadata.getLong(key)); 192 } 193 return bundle; 194 } 195 196 void updateNowPlayingList(List<MediaSession.QueueItem> queue){ 197 mNowPlayingList = queue; 198 } 199 200 /* Instructs media player to play particular media item */ 201 void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) { 202 long qid = ByteBuffer.wrap(uid).getLong(); 203 List<MediaSession.QueueItem> items = mNowPlayingList; 204 205 if (mediaController == null) { 206 Log.e(TAG, "No mediaController when PlayItem " + qid + " requested"); 207 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); 208 return; 209 } 210 211 MediaController.TransportControls mediaControllerCntrl = 212 mediaController.getTransportControls(); 213 214 if (items == null) { 215 Log.w(TAG, "nowPlayingItems is null"); 216 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); 217 return; 218 } 219 220 for (MediaSession.QueueItem item : items) { 221 if (qid == item.getQueueId()) { 222 if (DEBUG) Log.d(TAG, "Skipping to ID " + qid); 223 mediaControllerCntrl.skipToQueueItem(qid); 224 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR); 225 return; 226 } 227 } 228 229 Log.w(TAG, "Invalid now playing Queue ID " + qid); 230 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INV_ITEM); 231 } 232 233 void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) { 234 if (DEBUG) Log.d(TAG, "getTotalNumOfItems"); 235 List<MediaSession.QueueItem> items = mNowPlayingList; 236 if (items != null) { 237 // We already have the cached list, send the response to remote 238 mMediaInterface.getTotalNumOfItemsRsp( 239 bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size()); 240 return; 241 } 242 243 if (mediaController == null) { 244 Log.e(TAG, "getTotalNumOfItems with no mediaController, sending no items"); 245 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, 0); 246 return; 247 } 248 249 // We don't have the cached list, fetch it from Media Controller 250 items = mediaController.getQueue(); 251 if (items == null) { 252 // We may be presenting a queue with only 1 item (the current one) 253 int count = mediaController.getMetadata() != null ? 1 : 0; 254 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, count); 255 } 256 // Cache the response for later 257 mNowPlayingList = items; 258 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, items.size()); 259 } 260 261 boolean sendTrackChangeWithId(boolean requesting, @Nullable MediaController mediaController) { 262 if (DEBUG) 263 Log.d(TAG, "sendTrackChangeWithId (" + requesting + "): controller " + mediaController); 264 long qid = getActiveQueueItemId(mediaController); 265 byte[] track = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array(); 266 if (DEBUG) Log.d(TAG, "trackChangedRsp: 0x" + Utils.byteArrayToString(track)); 267 int trackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 268 if (requesting) trackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 269 mMediaInterface.trackChangedRsp(trackChangedNT, track); 270 mLastTrackIdSent = qid; 271 return (trackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_CHANGED); 272 } 273 274 /* 275 * helper method to check if startItem and endItem index is with range of 276 * MediaItem list. (Resultset containing all items in current path) 277 */ 278 private List<MediaSession.QueueItem> checkIndexOutofBounds( 279 byte[] bdaddr, List<MediaSession.QueueItem> items, long startItem, long endItem) { 280 if (endItem > items.size()) endItem = items.size() - 1; 281 if (startItem > Integer.MAX_VALUE) startItem = Integer.MAX_VALUE; 282 try { 283 List<MediaSession.QueueItem> selected = 284 items.subList((int) startItem, (int) Math.min(items.size(), endItem + 1)); 285 if (selected.isEmpty()) { 286 Log.i(TAG, "itemsSubList is empty."); 287 return null; 288 } 289 return selected; 290 } catch (IndexOutOfBoundsException ex) { 291 Log.i(TAG, "Range (" + startItem + ", " + endItem + ") invalid"); 292 } catch (IllegalArgumentException ex) { 293 Log.i(TAG, "Range start " + startItem + " > size (" + items.size() + ")"); 294 } 295 return null; 296 } 297 298 /* 299 * helper method to filter required attibutes before sending GetFolderItems 300 * response 301 */ 302 private void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd.FolderItemsCmd folderItemsReqObj, 303 List<MediaSession.QueueItem> items, byte scope, long startItem, long endItem, 304 @NonNull MediaController mediaController) { 305 if (DEBUG) Log.d(TAG, "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " 306 + endItem); 307 308 List<MediaSession.QueueItem> result_items = new ArrayList<MediaSession.QueueItem>(); 309 310 if (items == null) { 311 Log.e(TAG, "items is null in getFolderItemsFilterAttr"); 312 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null); 313 return; 314 } 315 316 result_items = checkIndexOutofBounds(bdaddr, items, startItem, endItem); 317 /* check for index out of bound errors */ 318 if (result_items == null) { 319 Log.w(TAG, "result_items is null."); 320 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null); 321 return; 322 } 323 324 FolderItemsData folderDataNative = new FolderItemsData(result_items.size()); 325 326 /* variables to accumulate attrs */ 327 ArrayList<String> attrArray = new ArrayList<String>(); 328 ArrayList<Integer> attrId = new ArrayList<Integer>(); 329 330 for (int itemIndex = 0; itemIndex < result_items.size(); itemIndex++) { 331 MediaSession.QueueItem item = result_items.get(itemIndex); 332 // get the queue id 333 long qid = item.getQueueId(); 334 byte[] uid = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array(); 335 336 // get the array of uid from 2d to array 1D array 337 for (int idx = 0; idx < AvrcpConstants.UID_SIZE; idx++) { 338 folderDataNative.mItemUid[itemIndex * AvrcpConstants.UID_SIZE + idx] = uid[idx]; 339 } 340 341 /* Set display name for current item */ 342 folderDataNative.mDisplayNames[itemIndex] = 343 getAttrValue(AvrcpConstants.ATTRID_TITLE, item, mediaController); 344 345 int maxAttributesRequested = 0; 346 boolean isAllAttribRequested = false; 347 /* check if remote requested for attributes */ 348 if (folderItemsReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 349 int attrCnt = 0; 350 351 /* add requested attr ids to a temp array */ 352 if (folderItemsReqObj.mNumAttr == AvrcpConstants.NUM_ATTR_ALL) { 353 isAllAttribRequested = true; 354 maxAttributesRequested = AvrcpConstants.MAX_NUM_ATTR; 355 } else { 356 /* get only the requested attribute ids from the request */ 357 maxAttributesRequested = folderItemsReqObj.mNumAttr; 358 } 359 360 /* lookup and copy values of attributes for ids requested above */ 361 for (int idx = 0; idx < maxAttributesRequested; idx++) { 362 /* check if media player provided requested attributes */ 363 String value = null; 364 365 int attribId = 366 isAllAttribRequested ? (idx + 1) : folderItemsReqObj.mAttrIDs[idx]; 367 value = getAttrValue(attribId, item, mediaController); 368 if (value != null) { 369 attrArray.add(value); 370 attrId.add(attribId); 371 attrCnt++; 372 } 373 } 374 /* add num attr actually received from media player for a particular item */ 375 folderDataNative.mAttributesNum[itemIndex] = attrCnt; 376 } 377 } 378 379 /* copy filtered attr ids and attr values to response parameters */ 380 if (folderItemsReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 381 folderDataNative.mAttrIds = new int[attrId.size()]; 382 for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) 383 folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex); 384 folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]); 385 } 386 for (int attrIndex = 0; attrIndex < folderDataNative.mAttributesNum.length; attrIndex++) 387 if (DEBUG) 388 Log.d(TAG, "folderDataNative.mAttributesNum" 389 + folderDataNative.mAttributesNum[attrIndex] + " attrIndex " 390 + attrIndex); 391 392 /* create rsp object and send response to remote device */ 393 FolderItemsRsp rspObj = new FolderItemsRsp(AvrcpConstants.RSP_NO_ERROR, Avrcp.sUIDCounter, 394 scope, folderDataNative.mNumItems, folderDataNative.mFolderTypes, 395 folderDataNative.mPlayable, folderDataNative.mItemTypes, folderDataNative.mItemUid, 396 folderDataNative.mDisplayNames, folderDataNative.mAttributesNum, 397 folderDataNative.mAttrIds, folderDataNative.mAttrValues); 398 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, rspObj); 399 } 400 401 private String getAttrValue( 402 int attr, MediaSession.QueueItem item, @Nullable MediaController mediaController) { 403 String attrValue = null; 404 if (item == null) { 405 if (DEBUG) Log.d(TAG, "getAttrValue received null item"); 406 return null; 407 } 408 try { 409 MediaDescription desc = item.getDescription(); 410 Bundle extras = desc.getExtras(); 411 boolean isCurrentTrack = item.getQueueId() == getActiveQueueItemId(mediaController); 412 if (isCurrentTrack) { 413 if (DEBUG) Log.d(TAG, "getAttrValue: item is active, using current data"); 414 extras = fillBundle(mediaController.getMetadata(), extras); 415 } 416 if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc); 417 switch (attr) { 418 case AvrcpConstants.ATTRID_TITLE: 419 /* Title is mandatory attribute */ 420 if (isCurrentTrack) { 421 attrValue = extras.getString(MediaMetadata.METADATA_KEY_TITLE); 422 } else { 423 attrValue = desc.getTitle().toString(); 424 } 425 break; 426 427 case AvrcpConstants.ATTRID_ARTIST: 428 attrValue = extras.getString(MediaMetadata.METADATA_KEY_ARTIST); 429 break; 430 431 case AvrcpConstants.ATTRID_ALBUM: 432 attrValue = extras.getString(MediaMetadata.METADATA_KEY_ALBUM); 433 break; 434 435 case AvrcpConstants.ATTRID_TRACK_NUM: 436 attrValue = 437 Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)); 438 break; 439 440 case AvrcpConstants.ATTRID_NUM_TRACKS: 441 attrValue = 442 Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)); 443 break; 444 445 case AvrcpConstants.ATTRID_GENRE: 446 attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE); 447 break; 448 449 case AvrcpConstants.ATTRID_PLAY_TIME: 450 attrValue = Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_DURATION)); 451 break; 452 453 case AvrcpConstants.ATTRID_COVER_ART: 454 Log.e(TAG, "getAttrValue: Cover art attribute not supported"); 455 return null; 456 457 default: 458 Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr); 459 return null; 460 } 461 } catch (NullPointerException ex) { 462 Log.w(TAG, "getAttrValue: attr id not found in result"); 463 /* checking if attribute is title, then it is mandatory and cannot send null */ 464 if (attr == AvrcpConstants.ATTRID_TITLE) { 465 attrValue = "<Unknown Title>"; 466 } else { 467 return null; 468 } 469 } 470 if (DEBUG) Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + ", attr id:" + attr); 471 return attrValue; 472 } 473 474 private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd mItemAttrReqObj, 475 MediaSession.QueueItem mediaItem, @Nullable MediaController mediaController) { 476 /* Response parameters */ 477 int[] attrIds = null; /* array of attr ids */ 478 String[] attrValues = null; /* array of attr values */ 479 480 /* variables to temperorily add attrs */ 481 ArrayList<String> attrArray = new ArrayList<String>(); 482 ArrayList<Integer> attrId = new ArrayList<Integer>(); 483 ArrayList<Integer> attrTempId = new ArrayList<Integer>(); 484 485 /* check if remote device has requested for attributes */ 486 if (mItemAttrReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 487 if (mItemAttrReqObj.mNumAttr == AvrcpConstants.NUM_ATTR_ALL) { 488 for (int idx = 1; idx < AvrcpConstants.MAX_NUM_ATTR; idx++) { 489 attrTempId.add(idx); /* attr id 0x00 is unused */ 490 } 491 } else { 492 /* get only the requested attribute ids from the request */ 493 for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) { 494 if (DEBUG) 495 Log.d(TAG, "getItemAttrFilterAttr: attr id[" + idx + "] :" 496 + mItemAttrReqObj.mAttrIDs[idx]); 497 attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]); 498 } 499 } 500 } 501 502 if (DEBUG) Log.d(TAG, "getItemAttrFilterAttr: attr id list size:" + attrTempId.size()); 503 /* lookup and copy values of attributes for ids requested above */ 504 for (int idx = 0; idx < attrTempId.size(); idx++) { 505 /* check if media player provided requested attributes */ 506 String value = getAttrValue(attrTempId.get(idx), mediaItem, mediaController); 507 if (value != null) { 508 attrArray.add(value); 509 attrId.add(attrTempId.get(idx)); 510 } 511 } 512 513 /* copy filtered attr ids and attr values to response parameters */ 514 if (mItemAttrReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 515 attrIds = new int[attrId.size()]; 516 517 for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) 518 attrIds[attrIndex] = attrId.get(attrIndex); 519 520 attrValues = attrArray.toArray(new String[attrId.size()]); 521 522 /* create rsp object and send response */ 523 ItemAttrRsp rspObj = new ItemAttrRsp(AvrcpConstants.RSP_NO_ERROR, attrIds, attrValues); 524 mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, rspObj); 525 return; 526 } 527 } 528 529 private long getActiveQueueItemId(@Nullable MediaController controller) { 530 if (controller == null) return MediaSession.QueueItem.UNKNOWN_ID; 531 PlaybackState state = controller.getPlaybackState(); 532 if (state == null) return MediaSession.QueueItem.UNKNOWN_ID; 533 return state.getActiveQueueItemId(); 534 } 535 536 public void dump(StringBuilder sb, @Nullable MediaController mediaController) { 537 ProfileService.println(sb, "AddressedPlayer info:"); 538 ProfileService.println(sb, "mLastTrackIdSent: " + mLastTrackIdSent); 539 ProfileService.println(sb, "mNowPlayingList: " + mNowPlayingList); 540 List<MediaSession.QueueItem> queue = getNowPlayingList(mediaController); 541 ProfileService.println(sb, "Current Queue: " + queue.size() + " elements"); 542 long currentQueueId = getActiveQueueItemId(mediaController); 543 for (MediaSession.QueueItem item : queue) { 544 long itemId = item.getQueueId(); 545 ProfileService.println(sb, (itemId == currentQueueId ? "*" : " ") + item.toString()); 546 } 547 } 548} 549