AddressedMediaPlayer.java revision d996f17ea97b2c592338bcfbc93056d0224da1cd
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.bluetooth.BluetoothAvrcp; 20import android.media.session.MediaSession; 21import android.media.session.MediaSession.QueueItem; 22import android.media.MediaDescription; 23import android.media.MediaMetadata; 24import android.os.Bundle; 25import android.util.Log; 26 27import java.nio.ByteBuffer; 28import java.util.List; 29import java.util.Arrays; 30import java.util.ArrayList; 31 32/************************************************************************************************* 33 * Provides functionality required for Addressed Media Player, like Now Playing List related 34 * browsing commands, control commands to the current addressed player(playItem, play, pause, etc) 35 * Acts as an Interface to communicate with media controller APIs for NowPlayingItems. 36 ************************************************************************************************/ 37 38public class AddressedMediaPlayer { 39 static private final String TAG = "AddressedMediaPlayer"; 40 static private final Boolean DEBUG = false; 41 42 private AvrcpMediaRspInterface mMediaInterface; 43 private NowPlayingListManager mNowPlayingListManager = new NowPlayingListManager(); 44 45 /* Now playing UID */ 46 private static final byte[] NOW_PLAYING_UID = {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 47 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00}; 48 49 public AddressedMediaPlayer(AvrcpMediaRspInterface _mediaInterface) { 50 mMediaInterface = _mediaInterface; 51 } 52 53 void cleanup() { 54 if (DEBUG) Log.v(TAG, "cleanup"); 55 mNowPlayingListManager = null; 56 mMediaInterface = null; 57 } 58 59 /* get now playing list from addressed player */ 60 void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd.FolderItemsCmd reqObj, 61 MediaController mediaController) { 62 List<QueueItem> tempItems; 63 List<MediaSession.QueueItem> mNowPlayingItems = mNowPlayingListManager.getNowPlayingList(); 64 if (DEBUG) Log.v(TAG, "getFolderItemsNowPlaying"); 65 66 if (mNowPlayingItems != null) { 67 // We already have the cached list sending the response to remote 68 if (DEBUG) Log.i(TAG, "sending cached now playing list"); 69 /* Filter attributes from cached NowPlayingList and send response */ 70 getFolderItemsFilterAttr(bdaddr, reqObj, mNowPlayingItems, 71 AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM, reqObj.mStartItem, reqObj.mEndItem); 72 } else if (mediaController == null) { 73 Log.e(TAG, "mediaController = null, sending internal error response"); 74 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, null); 75 } else { 76 // We don't have the cached list, fetching it from Media Controller 77 mNowPlayingItems = mediaController.getQueue(); 78 if (mNowPlayingItems == null) { 79 Log.w(TAG, "Received Now playing list is null from: " + 80 mediaController.getPackageName() + ", sending internal error response"); 81 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, null); 82 } else { 83 mNowPlayingListManager.setNowPlayingList(mNowPlayingItems); 84 getFolderItemsFilterAttr(bdaddr, reqObj, mNowPlayingItems, 85 AvrcpConstants.BTRC_SCOPE_NOW_PLAYING, reqObj.mStartItem, 86 reqObj.mEndItem); 87 } 88 } 89 } 90 91 /* get item attributes for item in now playing list */ 92 void getItemAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd itemAttr, 93 MediaController mediaController) { 94 int status = AvrcpConstants.RSP_NO_ERROR; 95 int idx; 96 long mediaID = ByteBuffer.wrap(itemAttr.mUid).getLong(); 97 List<MediaSession.QueueItem> mNowPlayingItems = mNowPlayingListManager.getNowPlayingList(); 98 99 /* checking if item attributes has been asked for now playing item or 100 * some other item with specific media id */ 101 if (Arrays.equals(itemAttr.mUid, NOW_PLAYING_UID)) { 102 if (DEBUG) Log.d(TAG, "getItemAttr: Remote requests for now playing contents:"); 103 104 // get the current playing song metadata and sending the queueitem. 105 if (mediaController != null) { 106 MediaMetadata metadata = mediaController.getMetadata(); 107 if (metadata != null) { 108 getItemAttrFilterAttr(bdaddr, itemAttr, getQueueItem(metadata)); 109 } else { 110 Log.e(TAG, "getItemAttr: metadata = null"); 111 status = AvrcpConstants.RSP_INTERNAL_ERR; 112 } 113 } else { 114 Log.e(TAG, "getItemAttr: mediaController = null"); 115 status = AvrcpConstants.RSP_INTERNAL_ERR; 116 } 117 } else if (mNowPlayingItems != null) { 118 if(DEBUG) printByteArray("getItemAttr-UID", itemAttr.mUid); 119 for (idx = 0; idx < mNowPlayingItems.size(); idx++) { 120 if (mediaID == mNowPlayingItems.get(idx).getQueueId()) { 121 getItemAttrFilterAttr(bdaddr, itemAttr, mNowPlayingItems.get(idx)); 122 break; 123 } 124 } 125 if (idx >= mNowPlayingItems.size()) { 126 Log.e(TAG, "getItemAttr: idx is more than now playing list: idx = " + idx 127 + ", now playing list size = " + mNowPlayingItems.size()); 128 status = AvrcpConstants.RSP_INV_ITEM; 129 } 130 } else { 131 Log.e(TAG, "getItemAttr: mNowPlayingItems is null!"); 132 status = AvrcpConstants.RSP_INTERNAL_ERR; 133 } 134 135 // sending error status in case of error 136 if (status != AvrcpConstants.RSP_NO_ERROR) { 137 mMediaInterface.getItemAttrRsp(bdaddr, status, null); 138 } 139 } 140 141 private MediaSession.QueueItem getQueueItem(MediaMetadata metadata) { 142 MediaDescription.Builder builder = new MediaDescription.Builder(); 143 144 // getting the media id 145 String mediaId = metadata.getDescription().getMediaId(); 146 if (mediaId != null) { 147 builder.setMediaId(mediaId); 148 if(DEBUG) Log.d(TAG, "Item mediaId = " + mediaId); 149 } 150 151 // getting the title 152 if (metadata.getDescription().getTitle() != null) { 153 String title = metadata.getDescription().getTitle().toString(); 154 builder.setTitle(title); 155 if(DEBUG) Log.d(TAG, "Item title = " + title); 156 } 157 158 // getting the metadata from the key-value pairs and filling to bundle 159 String artist = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST); 160 String album = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM); 161 String track_num = metadata.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER) + ""; 162 String num_tracks = metadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS) + ""; 163 String genre = metadata.getString(MediaMetadata.METADATA_KEY_GENRE); 164 String duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION) + ""; 165 166 Bundle bundle = fillBundle(artist, album, track_num, num_tracks, genre, duration); 167 builder.setExtras(bundle); 168 169 // building a queue item from the above metadata 170 MediaDescription desc = builder.build(); 171 return new QueueItem((desc), ByteBuffer.wrap(NOW_PLAYING_UID).getLong()); 172 } 173 174 private Bundle fillBundle(String artist, String album, String trackNum, String numTracks, 175 String genre, String playTime) { 176 177 Bundle bundle = new Bundle(); 178 179 bundle.putString(MediaMetadata.METADATA_KEY_ARTIST, artist); 180 bundle.putString(MediaMetadata.METADATA_KEY_ALBUM, album); 181 bundle.putString(MediaMetadata.METADATA_KEY_GENRE, genre); 182 bundle.putString(MediaMetadata.METADATA_KEY_NUM_TRACKS, numTracks); 183 bundle.putString(MediaMetadata.METADATA_KEY_DURATION, playTime); 184 bundle.putString(MediaMetadata.METADATA_KEY_TRACK_NUMBER, trackNum); 185 return bundle; 186 } 187 188 void updateNowPlayingList(List<MediaSession.QueueItem> queue){ 189 mNowPlayingListManager.setNowPlayingList(queue); 190 } 191 192 /* Instructs media player to play particular media item */ 193 void playItem(byte[] bdaddr, byte[] uid, byte scope, MediaController mediaController) { 194 long qid = ByteBuffer.wrap(uid).getLong(); 195 List<MediaSession.QueueItem> mNowPlayingItems = mNowPlayingListManager.getNowPlayingList(); 196 197 if (mediaController != null) { 198 MediaController.TransportControls mediaControllerCntrl = 199 mediaController.getTransportControls(); 200 if (DEBUG) Log.d(TAG, "Sending playID"); 201 202 if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { 203 int idx; 204 /* find the queueId of the mediaId to play */ 205 if (mNowPlayingItems != null) { 206 for (idx = 0; idx < mNowPlayingItems.size(); idx++) { 207 if (qid == mNowPlayingItems.get(idx).getQueueId()) { 208 mediaControllerCntrl.skipToQueueItem(qid); 209 break; 210 } 211 } 212 /* if mediaId is not found in nowplaying list */ 213 if (idx >= mNowPlayingItems.size()) { 214 Log.w(TAG, "item is not present in queue"); 215 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INV_ITEM); 216 } 217 } else { 218 Log.w(TAG, "nowPlayingItems is null"); 219 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); 220 } 221 } 222 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR); 223 } else { 224 Log.e(TAG, "mediaController is null"); 225 mMediaInterface.playItemRsp(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); 226 } 227 } 228 229 void getTotalNumOfItems(byte[] bdaddr, byte scope, MediaController mediaController) { 230 if (DEBUG) Log.d(TAG, "getTotalNumOfItems scope = " + scope); 231 List<MediaSession.QueueItem> mNowPlayingItems = mNowPlayingListManager.getNowPlayingList(); 232 if (mNowPlayingItems != null) { 233 // We already have the cached list sending the response to remote 234 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, 235 mNowPlayingItems.size()); 236 } else if (mediaController == null) { 237 Log.e(TAG, "mediaController is null"); 238 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, 239 AvrcpConstants.RSP_INTERNAL_ERR, 0, 0); 240 } else { 241 // We don't have the cached list, fetching it from Media Controller 242 mNowPlayingItems = mediaController.getQueue(); 243 if (mNowPlayingItems == null) { 244 Log.e(TAG, "mNowPlayingItems is null"); 245 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, 246 AvrcpConstants.RSP_INV_ITEM, 0, 0); 247 } else { 248 mNowPlayingListManager.setNowPlayingList(mediaController.getQueue()); 249 mMediaInterface.getTotalNumOfItemsRsp(bdaddr, 250 AvrcpConstants.RSP_NO_ERROR, 0, mNowPlayingItems.size()); 251 } 252 } 253 } 254 255 void sendTrackChangeWithId(int trackChangedNT, long trackNumber, 256 MediaController mediaController) { 257 if (DEBUG) Log.d(TAG, "sendTrackChangeWithId"); 258 try { 259 String mediaId = mediaController.getMetadata().getDescription().getMediaId(); 260 long qid = 0; 261 List<MediaSession.QueueItem> mNowPlayingItems = mNowPlayingListManager.getNowPlayingList(); 262 /* traverse now playing list for current playing item */ 263 for (QueueItem qitem : mNowPlayingItems) { 264 if (qitem.getDescription().getMediaId().equals(mediaId)) { 265 qid = qitem.getQueueId(); 266 if (DEBUG) Log.d(TAG, "sendTrackChangeWithId: Found matching qid= " + qid); 267 break; 268 } 269 } 270 /* for any item associated with NowPlaying, uid is queueId */ 271 byte[] uid = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array(); 272 if (DEBUG) printByteArray("trackChangedRsp", uid); 273 mMediaInterface.trackChangedRsp(trackChangedNT, uid); 274 } catch (NullPointerException e) { 275 Log.w(TAG, "Null Pointer while getting current track Uid from media player"); 276 sendTrackChangeRsp(trackChangedNT, trackNumber); 277 } 278 } 279 280 /* 281 * utility function to respond for track change when failed to get current track UID 282 * from media controller 283 */ 284 private void sendTrackChangeRsp(int trackChangedNT, long trackNumber) { 285 byte[] track = new byte[AvrcpConstants.TRACK_ID_SIZE]; 286 /* track is stored in big endian format */ 287 for (int idx = 0; idx < AvrcpConstants.TRACK_ID_SIZE; ++idx) { 288 if (trackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM 289 && trackNumber == -1) { 290 /* if no track is currently selected then return 0xFF in interim response */ 291 track[idx] = AvrcpConstants.NO_TRACK_SELECTED; 292 } else { 293 /* if Browsing is not supported and a track is selected, then return 0x00 */ 294 track[idx] = AvrcpConstants.TRACK_IS_SELECTED; 295 } 296 } 297 if (DEBUG) printByteArray("sendTrackChangeRsp", track); 298 mMediaInterface.trackChangedRsp(trackChangedNT, track); 299 } 300 301 /* 302 * helper method to check if startItem and endItem index is with range of 303 * MediaItem list. (Resultset containing all items in current path) 304 */ 305 private List<MediaSession.QueueItem> checkIndexOutofBounds(byte[] bdaddr, 306 List<MediaSession.QueueItem> children, int startItem, int endItem) { 307 try { 308 List<MediaSession.QueueItem> childrenSubList = 309 children.subList(startItem, Math.min(children.size(), endItem + 1)); 310 if (childrenSubList.isEmpty()) { 311 Log.i(TAG, "childrenSubList is empty."); 312 throw new IndexOutOfBoundsException(); 313 } 314 return childrenSubList; 315 } catch (IndexOutOfBoundsException ex) { 316 Log.i(TAG, "Index out of bounds start item =" + startItem + " end item = " 317 + Math.min(children.size(), endItem + 1)); 318 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null); 319 return null; 320 } catch (IllegalArgumentException ex) { 321 Log.i(TAG, "Index out of bounds start item =" + startItem + " > size"); 322 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null); 323 return null; 324 } 325 } 326 327 /* 328 * helper method to filter required attibutes before sending GetFolderItems 329 * response 330 */ 331 private void getFolderItemsFilterAttr(byte[] bdaddr, 332 AvrcpCmd.FolderItemsCmd mFolderItemsReqObj, 333 List<MediaSession.QueueItem> children, byte scope, int startItem, int endItem) { 334 if (DEBUG) Log.d(TAG, "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " 335 + endItem); 336 337 List<MediaSession.QueueItem> result_items = new ArrayList<MediaSession.QueueItem>(); 338 339 if (children != null) { 340 /* check for index out of bound errors */ 341 if ((result_items = checkIndexOutofBounds(bdaddr, children, startItem, endItem)) 342 == null) { 343 Log.w(TAG, "result_items is null."); 344 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null); 345 return; 346 } 347 FolderItemsData folderDataNative = new FolderItemsData(result_items.size()); 348 349 /* variables to temperorily add attrs */ 350 ArrayList<String> attrArray = new ArrayList<String>(); 351 ArrayList<Integer> attrId = new ArrayList<Integer>(); 352 353 for (int itemIndex = 0; itemIndex < result_items.size(); itemIndex++) { 354 // get the queue id 355 long qid = result_items.get(itemIndex).getQueueId(); 356 byte[] uid = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array(); 357 358 // get the array of uid from 2d to array 1D array 359 for (int idx = 0; idx < AvrcpConstants.UID_SIZE; idx++) { 360 folderDataNative.mItemUid[itemIndex * AvrcpConstants.UID_SIZE + idx] = uid[idx]; 361 } 362 363 /* Set display name for current item */ 364 folderDataNative.mDisplayNames[itemIndex] = result_items.get(itemIndex) 365 .getDescription().getTitle().toString(); 366 367 int maxAttributesRequested = 0; 368 boolean isAllAttribRequested = false; 369 /* check if remote requested for attributes */ 370 if (mFolderItemsReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 371 int attrCnt = 0; 372 373 /* add requested attr ids to a temp array */ 374 if (mFolderItemsReqObj.mNumAttr == AvrcpConstants.NUM_ATTR_ALL) { 375 isAllAttribRequested = true; 376 maxAttributesRequested = AvrcpConstants.MAX_NUM_ATTR; 377 } else { 378 /* get only the requested attribute ids from the request */ 379 maxAttributesRequested = mFolderItemsReqObj.mNumAttr; 380 } 381 382 /* lookup and copy values of attributes for ids requested above */ 383 for (int idx = 0; idx < maxAttributesRequested; idx++) { 384 /* check if media player provided requested attributes */ 385 String value = null; 386 387 int attribId = isAllAttribRequested ? (idx + 1) 388 : mFolderItemsReqObj.mAttrIDs[idx]; 389 if (attribId >= AvrcpConstants.ATTRID_TITLE 390 && attribId <= AvrcpConstants.ATTRID_PLAY_TIME) { 391 if ((value = getAttrValue(attribId, result_items, itemIndex)) 392 != null) { 393 attrArray.add(value); 394 attrId.add(attribId); 395 attrCnt++; 396 } 397 } else { 398 Log.w(TAG, "invalid attributed id is requested: " + attribId); 399 } 400 } 401 /* add num attr actually received from media player for a particular item */ 402 folderDataNative.mAttributesNum[itemIndex] = attrCnt; 403 } 404 } 405 406 /* copy filtered attr ids and attr values to response parameters */ 407 if (mFolderItemsReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 408 folderDataNative.mAttrIds = new int[attrId.size()]; 409 for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) 410 folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex); 411 folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]); 412 } 413 for (int attrIndex = 0; attrIndex < folderDataNative.mAttributesNum.length; attrIndex++) 414 if (DEBUG) Log.d(TAG, "folderDataNative.mAttributesNum" 415 + folderDataNative.mAttributesNum[attrIndex] + " attrIndex " + attrIndex); 416 417 /* create rsp object and send response to remote device */ 418 FolderItemsRsp rspObj = new FolderItemsRsp(AvrcpConstants.RSP_NO_ERROR, 419 Avrcp.sUIDCounter, scope, folderDataNative.mNumItems, 420 folderDataNative.mFolderTypes, folderDataNative.mPlayable, 421 folderDataNative.mItemTypes, folderDataNative.mItemUid, 422 folderDataNative.mDisplayNames, folderDataNative.mAttributesNum, 423 folderDataNative.mAttrIds, folderDataNative.mAttrValues); 424 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, rspObj); 425 } else { 426 Log.e(TAG, "Error: children are null in getFolderItemsFilterAttr"); 427 mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null); 428 return; 429 } 430 } 431 432 private String getAttrValue(int attr, List<MediaSession.QueueItem> resultItems, 433 int itemIndex) { 434 String attrValue = null; 435 try { 436 switch (attr) { 437 /* Title is mandatory attribute */ 438 case AvrcpConstants.ATTRID_TITLE: 439 attrValue = resultItems.get(itemIndex).getDescription().getTitle().toString(); 440 break; 441 442 case AvrcpConstants.ATTRID_ARTIST: 443 attrValue = resultItems.get(itemIndex).getDescription().getExtras() 444 .getString(MediaMetadata.METADATA_KEY_ARTIST); 445 break; 446 447 case AvrcpConstants.ATTRID_ALBUM: 448 attrValue = resultItems.get(itemIndex).getDescription().getExtras() 449 .getString(MediaMetadata.METADATA_KEY_ALBUM); 450 break; 451 452 case AvrcpConstants.ATTRID_TRACK_NUM: 453 attrValue = resultItems.get(itemIndex).getDescription().getExtras() 454 .getString(MediaMetadata.METADATA_KEY_TRACK_NUMBER); 455 break; 456 457 case AvrcpConstants.ATTRID_NUM_TRACKS: 458 attrValue = resultItems.get(itemIndex).getDescription().getExtras() 459 .getString(MediaMetadata.METADATA_KEY_NUM_TRACKS); 460 break; 461 462 case AvrcpConstants.ATTRID_GENRE: 463 attrValue = resultItems.get(itemIndex).getDescription().getExtras() 464 .getString(MediaMetadata.METADATA_KEY_GENRE); 465 break; 466 467 case AvrcpConstants.ATTRID_PLAY_TIME: 468 attrValue = resultItems.get(itemIndex).getDescription().getExtras() 469 .getString(MediaMetadata.METADATA_KEY_DURATION); 470 break; 471 472 default: 473 Log.e(TAG, "Unknown attribute ID"); 474 } 475 } catch (IndexOutOfBoundsException ex) { 476 Log.w(TAG, "getAttrValue: requested item index out of bounds"); 477 return null; 478 } catch (NullPointerException ex) { 479 Log.w(TAG, "getAttrValue: attr id not found in result"); 480 /* checking if attribute is title, then it is mandatory and cannot send null */ 481 if (attr == AvrcpConstants.ATTRID_TITLE) { 482 return "<Unknown Title>"; 483 } 484 return null; 485 } 486 if (DEBUG) Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + ", attr id:" + attr); 487 return attrValue; 488 } 489 490 private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd.ItemAttrCmd mItemAttrReqObj, 491 MediaSession.QueueItem mediaItem) { 492 /* Response parameters */ 493 int[] attrIds = null; /* array of attr ids */ 494 String[] attrValues = null; /* array of attr values */ 495 int attrCounter = 0; /* num attributes for each item */ 496 List<MediaSession.QueueItem> resultItems = new ArrayList<MediaSession.QueueItem>(); 497 resultItems.add(mediaItem); 498 /* variables to temperorily add attrs */ 499 ArrayList<String> attrArray = new ArrayList<String>(); 500 ArrayList<Integer> attrId = new ArrayList<Integer>(); 501 502 ArrayList<Integer> attrTempId = new ArrayList<Integer>(); 503 504 /* check if remote device has requested for attributes */ 505 if (mItemAttrReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 506 if (mItemAttrReqObj.mNumAttr == AvrcpConstants.NUM_ATTR_ALL) { 507 for (int idx = 1; idx < AvrcpConstants.MAX_NUM_ATTR; idx++) { 508 attrTempId.add(idx); /* attr id 0x00 is unused */ 509 } 510 } else { 511 /* get only the requested attribute ids from the request */ 512 for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) { 513 if (DEBUG) Log.d(TAG, "getAttrValue: attr id[" + idx + "] :" + 514 mItemAttrReqObj.mAttrIDs[idx]); 515 attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]); 516 } 517 } 518 519 if (DEBUG) Log.d(TAG, "getAttrValue: attr id list size:" + attrTempId.size()); 520 /* lookup and copy values of attributes for ids requested above */ 521 for (int idx = 0; idx < attrTempId.size(); idx++) { 522 /* check if media player provided requested attributes */ 523 String value = null; 524 if ((value = getAttrValue(attrTempId.get(idx), resultItems, 0)) != null) { 525 attrArray.add(value); 526 attrId.add(attrTempId.get(idx)); 527 attrCounter++; 528 } 529 } 530 attrTempId = null; 531 } 532 533 /* copy filtered attr ids and attr values to response parameters */ 534 if (mItemAttrReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) { 535 attrIds = new int[attrId.size()]; 536 537 for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) 538 attrIds[attrIndex] = attrId.get(attrIndex); 539 540 attrValues = attrArray.toArray(new String[attrId.size()]); 541 542 /* create rsp object and send response */ 543 ItemAttrRsp rspObj = new ItemAttrRsp(AvrcpConstants.RSP_NO_ERROR, 544 (byte)attrCounter, attrIds, attrValues); 545 mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, rspObj); 546 return; 547 } 548 } 549 550 void handlePassthroughCmd(int id, int keyState, byte[] bdAddr, 551 MediaController mediaController) { 552 553 if (mediaController != null) { 554 MediaController.TransportControls mediaControllerCntrl = 555 mediaController.getTransportControls(); 556 if (DEBUG) Log.v(TAG, "handlePassthroughCmd - id:" + id + " keyState:" + keyState); 557 if (keyState == AvrcpConstants.KEY_STATE_PRESS) { 558 switch (id) { 559 case BluetoothAvrcp.PASSTHROUGH_ID_REWIND: 560 mediaControllerCntrl.rewind(); 561 break; 562 case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR: 563 mediaControllerCntrl.fastForward(); 564 break; 565 case BluetoothAvrcp.PASSTHROUGH_ID_PLAY: 566 mediaControllerCntrl.play(); 567 break; 568 case BluetoothAvrcp.PASSTHROUGH_ID_PAUSE: 569 mediaControllerCntrl.pause(); 570 break; 571 case BluetoothAvrcp.PASSTHROUGH_ID_STOP: 572 mediaControllerCntrl.stop(); 573 break; 574 case BluetoothAvrcp.PASSTHROUGH_ID_FORWARD: 575 mediaControllerCntrl.skipToNext(); 576 break; 577 case BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD: 578 mediaControllerCntrl.skipToPrevious(); 579 break; 580 default: 581 Log.w(TAG, "unknown id:" + id + " keyState:" + keyState); 582 } 583 } else { 584 Log.i(TAG, "ignoring the release event for id:" + id + " keyState:" + keyState); 585 } 586 } else { 587 Log.e(TAG, "Unable to handlePassthroughCmd, mediaController is null!"); 588 } 589 } 590 591 private void printByteArray(String arrName, byte[] array) { 592 StringBuilder byteArray = new StringBuilder(arrName + ": 0x"); 593 594 for (int idx = 0; idx < array.length; idx++) { 595 byteArray.append(String.format(" %02x", array[idx])); 596 } 597 Log.d(TAG, byteArray + ""); 598 } 599 600} 601