OSDN Git Service

AVRCP: Gracefully handle missing item attributes
authorMarie Janssen <jamuraa@google.com>
Wed, 26 Apr 2017 03:11:27 +0000 (20:11 -0700)
committerMarie Janssen <jamuraa@google.com>
Thu, 27 Apr 2017 20:07:32 +0000 (13:07 -0700)
The source of our metadata sometimes doesn't even give us basic title
information, which causes a NullPointerException when we try to fill the
Displayable Name.

Fill it with "<unknown>" when we don't know the data.

Add more useful debugging for GetFolderItems and GetItemAttributes.

Test: Tested with Audi and Porche car kit
Bug: 37657532
Bug: 37718715
Change-Id: I183f2b9c5714ebacabc8093de1c2bc166e323fae
(cherry picked from commit 89728a4d50001ac76d05efa1c916711ef9f9c2b4)

src/com/android/bluetooth/Utils.java
src/com/android/bluetooth/avrcp/AddressedMediaPlayer.java
src/com/android/bluetooth/avrcp/Avrcp.java
src/com/android/bluetooth/avrcp/AvrcpHelperClasses.java
src/com/android/bluetooth/avrcp/BrowsedMediaPlayer.java

index 66d2172..43f4547 100644 (file)
@@ -98,6 +98,15 @@ final public class Utils {
         return converter.getInt(offset);
     }
 
+    public static String byteArrayToString(byte[] valueBuf) {
+        StringBuilder sb = new StringBuilder();
+        for (int idx = 0; idx < valueBuf.length; idx++) {
+            if (idx != 0) sb.append(" ");
+            sb.append(String.format("%02x", valueBuf[idx]));
+        }
+        return sb.toString();
+    }
+
     public static byte[] intToByteArray(int value) {
         ByteBuffer converter = ByteBuffer.allocate(4);
         converter.order(ByteOrder.nativeOrder());
index 61701e1..02d077f 100644 (file)
@@ -25,6 +25,8 @@ import android.media.MediaMetadata;
 import android.os.Bundle;
 import android.util.Log;
 
+import com.android.bluetooth.Utils;
+
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Arrays;
@@ -93,7 +95,7 @@ public class AddressedMediaPlayer {
             return;
         }
 
-        if (DEBUG) printByteArray("getItemAttr-UID", itemAttr.mUid);
+        if (DEBUG) Log.d(TAG, "getItemAttr-UID: 0x" + Utils.byteArrayToString(itemAttr.mUid));
         for (MediaSession.QueueItem item : items) {
             if (item.getQueueId() == mediaId) {
                 getItemAttrFilterAttr(bdaddr, itemAttr, item, mediaController);
@@ -170,8 +172,7 @@ public class AddressedMediaPlayer {
             if (current == null) bundle.putString(key, metadata.getString(key));
         }
         for (String key : longKeys) {
-            String current = bundle.getString(key);
-            if (current == null) bundle.putString(key, metadata.getLong(key) + "");
+            if (!bundle.containsKey(key)) bundle.putLong(key, metadata.getLong(key));
         }
         return bundle;
     }
@@ -255,7 +256,7 @@ public class AddressedMediaPlayer {
         }
         /* for any item associated with NowPlaying, uid is queueId */
         track = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array();
-        if (DEBUG) printByteArray("trackChangedRsp", track);
+        if (DEBUG) Log.d(TAG, "trackChangedRsp: 0x" + Utils.byteArrayToString(track));
         mMediaInterface.trackChangedRsp(trackChangedNT, track);
     }
 
@@ -316,8 +317,9 @@ public class AddressedMediaPlayer {
         ArrayList<Integer> attrId = new ArrayList<Integer>();
 
         for (int itemIndex = 0; itemIndex < result_items.size(); itemIndex++) {
+            MediaSession.QueueItem item = result_items.get(itemIndex);
             // get the queue id
-            long qid = result_items.get(itemIndex).getQueueId();
+            long qid = item.getQueueId();
             byte[] uid = ByteBuffer.allocate(AvrcpConstants.UID_SIZE).putLong(qid).array();
 
             // get the array of uid from 2d to array 1D array
@@ -327,7 +329,7 @@ public class AddressedMediaPlayer {
 
             /* Set display name for current item */
             folderDataNative.mDisplayNames[itemIndex] =
-                    result_items.get(itemIndex).getDescription().getTitle().toString();
+                    getAttrValue(AvrcpConstants.ATTRID_TITLE, item, mediaController);
 
             int maxAttributesRequested = 0;
             boolean isAllAttribRequested = false;
@@ -351,17 +353,11 @@ public class AddressedMediaPlayer {
 
                     int attribId =
                             isAllAttribRequested ? (idx + 1) : folderItemsReqObj.mAttrIDs[idx];
-                    if (attribId >= AvrcpConstants.ATTRID_TITLE
-                            && attribId <= AvrcpConstants.ATTRID_PLAY_TIME) {
-                        value = getAttrValue(
-                                attribId, result_items.get(itemIndex), mediaController);
-                        if (value != null) {
-                            attrArray.add(value);
-                            attrId.add(attribId);
-                            attrCnt++;
-                        }
-                    } else {
-                        Log.w(TAG, "invalid attribute id is requested: " + attribId);
+                    value = getAttrValue(attribId, item, mediaController);
+                    if (value != null) {
+                        attrArray.add(value);
+                        attrId.add(attribId);
+                        attrCnt++;
                     }
                 }
                 /* add num attr actually received from media player for a particular item */
@@ -403,6 +399,7 @@ public class AddressedMediaPlayer {
             PlaybackState state = mediaController.getPlaybackState();
             Bundle extras = desc.getExtras();
             if (state != null && (item.getQueueId() == state.getActiveQueueItemId())) {
+                if (DEBUG) Log.d(TAG, "getAttrValue: item is active, filling extra data");
                 extras = fillBundle(mediaController.getMetadata(), extras);
             }
             if (DEBUG) Log.d(TAG, "getAttrValue: item " + item + " : " + desc);
@@ -437,19 +434,21 @@ public class AddressedMediaPlayer {
                     break;
 
                 case AvrcpConstants.ATTRID_COVER_ART:
-                    Log.e(TAG, "Cover art attribute not supported");
-                    break;
+                    Log.e(TAG, "getAttrValue: Cover art attribute not supported");
+                    return null;
 
                 default:
-                    Log.e(TAG, "Unknown attribute ID");
+                    Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr);
+                    return null;
             }
         } catch (NullPointerException ex) {
             Log.w(TAG, "getAttrValue: attr id not found in result");
             /* checking if attribute is title, then it is mandatory and cannot send null */
             if (attr == AvrcpConstants.ATTRID_TITLE) {
-                return "<Unknown Title>";
+                attrValue = "<Unknown Title>";
+            } else {
+                return null;
             }
-            return null;
         }
         if (DEBUG) Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + ", attr id:" + attr);
         return attrValue;
@@ -511,13 +510,4 @@ public class AddressedMediaPlayer {
             return;
         }
     }
-
-    private void printByteArray(String arrName, byte[] array) {
-        StringBuilder byteArray = new StringBuilder(arrName + ": 0x");
-
-        for (int idx = 0; idx < array.length; idx++) {
-            byteArray.append(String.format(" %02x", array[idx]));
-        }
-        Log.d(TAG, byteArray + "");
-    }
 }
index 186ff6a..2c084d0 100644 (file)
@@ -703,8 +703,8 @@ public final class Avrcp {
                 break;
 
             case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
-                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_FOLDER_ITEMS");
                 AvrcpCmd.FolderItemsCmd folderObj = (AvrcpCmd.FolderItemsCmd) msg.obj;
+                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_FOLDER_ITEMS " + folderObj);
                 switch (folderObj.mScope) {
                     case AvrcpConstants.BTRC_SCOPE_PLAYER_LIST:
                         handleMediaPlayerListRsp(folderObj);
@@ -731,8 +731,9 @@ public final class Avrcp {
 
             case MSG_NATIVE_REQ_GET_ITEM_ATTR:
                 // msg object contains the item attribute object
-                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_ITEM_ATTR");
-                handleGetItemAttr((AvrcpCmd.ItemAttrCmd) msg.obj);
+                AvrcpCmd.ItemAttrCmd cmd = (AvrcpCmd.ItemAttrCmd) msg.obj;
+                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_ITEM_ATTR " + cmd);
+                handleGetItemAttr(cmd);
                 break;
 
             case MSG_NATIVE_REQ_SET_BR_PLAYER:
@@ -760,11 +761,13 @@ public final class Avrcp {
 
             case MSG_NATIVE_REQ_PLAY_ITEM:
             {
-                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_PLAY_ITEM");
                 Bundle data = msg.getData();
                 byte[] bdaddr = data.getByteArray("BdAddress");
                 byte[] uid = data.getByteArray("uid");
                 byte scope = data.getByte("scope");
+                if (DEBUG)
+                    Log.v(TAG, "MSG_NATIVE_REQ_PLAY_ITEM scope=" + scope + " id="
+                                    + Utils.byteArrayToString(uid));
                 handlePlayItemResponse(bdaddr, uid, scope);
                 break;
             }
@@ -991,7 +994,7 @@ public final class Avrcp {
         mHandler.sendMessage(msg);
     }
 
-    private void getElementAttrRequestFromNative(byte[] address,byte numAttr, int[] attrs) {
+    private void getElementAttrRequestFromNative(byte[] address, byte numAttr, int[] attrs) {
         if (DEBUG) Log.v(TAG, "getElementAttrRequestFromNative: numAttr=" + numAttr);
         AvrcpCmd avrcpCmdobj = new AvrcpCmd();
         AvrcpCmd.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs);
index 3b3f830..aabc60a 100644 (file)
@@ -18,6 +18,8 @@ package com.android.bluetooth.avrcp;
 
 import android.media.session.MediaSession;
 
+import com.android.bluetooth.Utils;
+
 import java.util.List;
 import java.util.Arrays;
 import java.util.ArrayDeque;
@@ -51,6 +53,19 @@ class AvrcpCmd {
             this.mNumAttr = numAttr;
             this.mAttrIDs = attrIds;
         }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("[FolderItemCmd: scope " + mScope);
+            sb.append(" start " + mStartItem);
+            sb.append(" end " + mEndItem);
+            sb.append(" numAttr " + mNumAttr);
+            sb.append(" attrs: ");
+            for (int i = 0; i < mNumAttr; i++) {
+                sb.append(mAttrIDs[i] + " ");
+            }
+            return sb.toString();
+        }
     }
 
     class ItemAttrCmd {
@@ -70,6 +85,18 @@ class AvrcpCmd {
             mNumAttr = numAttr;
             mAttrIDs = attrIDs;
         }
+
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("[ItemAttrCmd: scope " + mScope);
+            sb.append(" uid " + Utils.byteArrayToString(mUid));
+            sb.append(" numAttr " + mNumAttr);
+            sb.append(" attrs: ");
+            for (int i = 0; i < mNumAttr; i++) {
+                sb.append(mAttrIDs[i] + " ");
+            }
+            return sb.toString();
+        }
     }
 
     class ElementAttrCmd {
index 178bc9d..ddcb1a0 100644 (file)
@@ -24,6 +24,7 @@ import android.media.browse.MediaBrowser;
 import android.media.browse.MediaBrowser.MediaItem;
 import android.media.session.MediaSession;
 import android.media.session.MediaSession.QueueItem;
+import android.os.Bundle;
 import android.util.Log;
 
 import java.math.BigInteger;
@@ -336,68 +337,71 @@ class BrowsedMediaPlayer {
         mItemAttrReqObj = itemAttr;
 
         /* check if uid is valid by doing a lookup in hashmap */
-        if ((mediaID = byteToString(itemAttr.mUid)) == null) {
+        mediaID = byteToString(itemAttr.mUid);
+        if (mediaID == null) {
             Log.e(TAG, "uid is invalid");
             mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants.RSP_INV_ITEM, null);
             return;
         }
 
         /* check scope */
-        if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) {
-            if (mMediaBrowser != null) {
-                mMediaBrowser.subscribe(mediaID, itemAttrCb);
-            } else {
-                Log.e(TAG, "mMediaBrowser is null");
-                mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, null);
-            }
-        } else {
+        if (itemAttr.mScope != AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) {
             Log.e(TAG, "invalid scope");
             mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants.RSP_INV_SCOPE, null);
+            return;
+        }
+
+        if (mMediaBrowser == null) {
+            Log.e(TAG, "mMediaBrowser is null");
+            mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, null);
+            return;
         }
+
+        mMediaBrowser.subscribe(mediaID, itemAttrCb);
     }
 
     public void getTotalNumOfItems(byte scope) {
         if (DEBUG) Log.d(TAG, "getTotalNumOfItems scope = " + scope);
-        switch (scope) {
-            case AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM:
-                if (mFolderItems != null) {
-                     /* find num items using size of already cached folder items */
-                    mMediaInterface.getTotalNumOfItemsRsp(mBDAddr,
-                            AvrcpConstants.RSP_NO_ERROR, 0, mFolderItems.size());
-                } else {
-                    Log.e(TAG, "mFolderItems is null, sending internal error");
-                    /* folderitems were not fetched during change path */
-                    mMediaInterface.getTotalNumOfItemsRsp(mBDAddr,
-                            AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
-                }
-                break;
-            default:
-                Log.e(TAG, "getTotalNumOfItems error" + scope);
-                mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants.RSP_INV_SCOPE, 0, 0);
-                break;
+        if (scope != AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) {
+            Log.e(TAG, "getTotalNumOfItems error" + scope);
+            mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants.RSP_INV_SCOPE, 0, 0);
+            return;
+        }
+
+        if (mFolderItems == null) {
+            Log.e(TAG, "mFolderItems is null, sending internal error");
+            /* folderitems were not fetched during change path */
+            mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
+            return;
         }
+
+        /* find num items using size of already cached folder items */
+        mMediaInterface.getTotalNumOfItemsRsp(
+                mBDAddr, AvrcpConstants.RSP_NO_ERROR, 0, mFolderItems.size());
     }
 
     public void getFolderItemsVFS(AvrcpCmd.FolderItemsCmd reqObj) {
-        if (isPlayerConnected()) {
-            if (DEBUG) Log.d(TAG, "getFolderItemsVFS");
-            mFolderItemsReqObj = reqObj;
-
-            if (mFolderItems == null) {
-                /* Failed to fetch folder items from media player. Send error to remote device */
-                Log.e(TAG, "Failed to fetch folder items during getFolderItemsVFS");
-                mMediaInterface.folderItemsRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, null);
-            } else {
-                /* Filter attributes based on the request and send response to remote device */
-                getFolderItemsFilterAttr(mBDAddr, reqObj, mFolderItems,
-                        AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM,
-                        mFolderItemsReqObj.mStartItem, mFolderItemsReqObj.mEndItem);
-            }
-        } else {
+        if (!isPlayerConnected()) {
             Log.e(TAG, "unable to connect to media player, sending internal error");
             /* unable to connect to media player. Send error response to remote device */
             mMediaInterface.folderItemsRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, null);
+            return;
         }
+
+        if (DEBUG) Log.d(TAG, "getFolderItemsVFS");
+        mFolderItemsReqObj = reqObj;
+
+        if (mFolderItems == null) {
+            /* Failed to fetch folder items from media player. Send error to remote device */
+            Log.e(TAG, "Failed to fetch folder items during getFolderItemsVFS");
+            mMediaInterface.folderItemsRsp(mBDAddr, AvrcpConstants.RSP_INTERNAL_ERR, null);
+            return;
+        }
+
+        /* Filter attributes based on the request and send response to remote device */
+        getFolderItemsFilterAttr(mBDAddr, reqObj, mFolderItems,
+                AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM, mFolderItemsReqObj.mStartItem,
+                mFolderItemsReqObj.mEndItem);
     }
 
     /* Instructs media player to play particular media item */
@@ -453,11 +457,9 @@ class BrowsedMediaPlayer {
         } catch (IndexOutOfBoundsException ex) {
             Log.w(TAG, "Index out of bounds start item ="+ startItem + " end item = "+
                     Math.min(children.size(), endItem + 1));
-            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null);
             return null;
         } catch (IllegalArgumentException ex) {
             Log.i(TAG, "Index out of bounds start item =" + startItem + " > size");
-            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null);
             return null;
         }
     }
@@ -468,50 +470,56 @@ class BrowsedMediaPlayer {
      */
     public void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd.FolderItemsCmd mFolderItemsReqObj,
             List<MediaBrowser.MediaItem> children, byte scope, long startItem, long endItem) {
-        if (DEBUG) Log.d(TAG, "getFolderItemsFilterAttr: startItem =" + startItem +
-            ", endItem = " + endItem);
+        if (DEBUG)
+            Log.d(TAG,
+                    "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem);
 
         List<MediaBrowser.MediaItem> result_items = new ArrayList<MediaBrowser.MediaItem>();
 
-        if (children != null) {
-            /* check for index out of bound errors */
-            if ((result_items = checkIndexOutofBounds(bdaddr, children, startItem, endItem)) == null) {
-               Log.w(TAG, "result_items is null.");
-               mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null);
-               return;
+        if (children == null) {
+            Log.e(TAG, "Error: children are null in getFolderItemsFilterAttr");
+            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null);
+            return;
+        }
+
+        /* check for index out of bound errors */
+        result_items = checkIndexOutofBounds(bdaddr, children, startItem, endItem);
+        if (result_items == null) {
+            Log.w(TAG, "result_items is null.");
+            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null);
+            return;
         }
         FolderItemsData folderDataNative = new FolderItemsData(result_items.size());
 
-       /* variables to temperorily add attrs */
+        /* variables to temperorily add attrs */
         ArrayList<String> attrArray = new ArrayList<String>();
         ArrayList<Integer> attrId = new ArrayList<Integer>();
 
         for (int itemIndex = 0; itemIndex < result_items.size(); itemIndex++) {
             /* item type. Needs to be set by media player */
-            if ((result_items.get(itemIndex).getFlags() &
-                MediaBrowser.MediaItem.FLAG_BROWSABLE) != 0) {
-                    folderDataNative.mItemTypes[itemIndex] = AvrcpConstants.BTRC_ITEM_FOLDER;
+            MediaBrowser.MediaItem item = result_items.get(itemIndex);
+            int flags = item.getFlags();
+            if ((flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) != 0) {
+                folderDataNative.mItemTypes[itemIndex] = AvrcpConstants.BTRC_ITEM_FOLDER;
             } else {
                 folderDataNative.mItemTypes[itemIndex] = AvrcpConstants.BTRC_ITEM_MEDIA;
             }
 
             /* set playable */
-            if ((result_items.get(itemIndex).getFlags()
-                & MediaBrowser.MediaItem.FLAG_PLAYABLE) != 0) {
+            if ((flags & MediaBrowser.MediaItem.FLAG_PLAYABLE) != 0) {
                 folderDataNative.mPlayable[itemIndex] = AvrcpConstants.ITEM_PLAYABLE;
             } else {
                 folderDataNative.mPlayable[itemIndex] = AvrcpConstants.ITEM_NOT_PLAYABLE;
             }
             /* set uid for current item */
-            byte[] uid =
-                    stringToByte(result_items.get(itemIndex).getDescription().getMediaId());
+            byte[] uid = stringToByte(item.getDescription().getMediaId());
             for (int idx = 0; idx < AvrcpConstants.UID_SIZE; idx++) {
                 folderDataNative.mItemUid[itemIndex * AvrcpConstants.UID_SIZE + idx] = uid[idx];
             }
 
             /* Set display name for current item */
             folderDataNative.mDisplayNames[itemIndex] =
-                    result_items.get(itemIndex).getDescription().getTitle().toString();
+                    getAttrValue(AvrcpConstants.ATTRID_TITLE, item);
 
             int maxAttributesRequested = 0;
             boolean isAllAttribRequested = false;
@@ -524,114 +532,97 @@ class BrowsedMediaPlayer {
                     isAllAttribRequested = true;
                     maxAttributesRequested = AvrcpConstants.MAX_NUM_ATTR;
                 } else {
-                /* get only the requested attribute ids from the request */
+                    /* get only the requested attribute ids from the request */
                     maxAttributesRequested = mFolderItemsReqObj.mNumAttr;
                 }
 
                 /* lookup and copy values of attributes for ids requested above */
                 for (int idx = 0; idx < maxAttributesRequested; idx++) {
-                /* check if media player provided requested attributes */
+                    /* check if media player provided requested attributes */
                     String value = null;
 
                     int attribId = isAllAttribRequested ? (idx + 1) :
                             mFolderItemsReqObj.mAttrIDs[idx];
-                    if(attribId >= AvrcpConstants.ATTRID_TITLE &&
-                        attribId <= AvrcpConstants.ATTRID_PLAY_TIME) {
-                        if ((value = getAttrValue(attribId, result_items,
-                            itemIndex)) != null) {
-                            attrArray.add(value);
-                            attrId.add(attribId);
-                            attrCnt++;
-                        }
-                    } else {
-                        Log.d(TAG, "invalid attributed id is requested: " + attribId);
+                    value = getAttrValue(attribId, result_items.get(itemIndex));
+                    if (value != null) {
+                        attrArray.add(value);
+                        attrId.add(attribId);
+                        attrCnt++;
                     }
                 }
                 /* add num attr actually received from media player for a particular item */
                 folderDataNative.mAttributesNum[itemIndex] = attrCnt;
-                }
-            }
-
-            /* copy filtered attr ids and attr values to response parameters */
-            if (mFolderItemsReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) {
-                folderDataNative.mAttrIds = new int[attrId.size()];
-                for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++)
-                    folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
-                folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]);
             }
+        }
 
-            /* create rsp object and send response to remote device */
-            FolderItemsRsp rspObj = new FolderItemsRsp(AvrcpConstants.RSP_NO_ERROR,
-                    Avrcp.sUIDCounter, scope, folderDataNative.mNumItems,
-                    folderDataNative.mFolderTypes, folderDataNative.mPlayable,
-                    folderDataNative.mItemTypes,folderDataNative.mItemUid,
-                    folderDataNative.mDisplayNames, folderDataNative.mAttributesNum,
-                    folderDataNative.mAttrIds, folderDataNative.mAttrValues);
-            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, rspObj);
-        } else {
-            Log.e(TAG, "Error: children are null in getFolderItemsFilterAttr");
-            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_INV_RANGE, null);
-            return;
+        /* copy filtered attr ids and attr values to response parameters */
+        if (attrId.size() > 0) {
+            folderDataNative.mAttrIds = new int[attrId.size()];
+            for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++)
+                folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
+            folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]);
         }
-    }
 
-    public static String getAttrValue(int attr, List<MediaBrowser.MediaItem> resultItems,
-            int itemIndex) {
+        /* create rsp object and send response to remote device */
+        FolderItemsRsp rspObj = new FolderItemsRsp(AvrcpConstants.RSP_NO_ERROR, Avrcp.sUIDCounter,
+                scope, folderDataNative.mNumItems, folderDataNative.mFolderTypes,
+                folderDataNative.mPlayable, folderDataNative.mItemTypes, folderDataNative.mItemUid,
+                folderDataNative.mDisplayNames, folderDataNative.mAttributesNum,
+                folderDataNative.mAttrIds, folderDataNative.mAttrValues);
+        mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants.RSP_NO_ERROR, rspObj);
+    }
 
+    public static String getAttrValue(int attr, MediaBrowser.MediaItem item) {
         String attrValue = null;
         try {
+            MediaDescription desc = item.getDescription();
+            Bundle extras = desc.getExtras();
             switch (attr) {
                 /* Title is mandatory attribute */
                 case AvrcpConstants.ATTRID_TITLE:
-                    attrValue = resultItems.get(itemIndex).getDescription().getTitle().toString();
+                    attrValue = desc.getTitle().toString();
                     break;
+
                 case AvrcpConstants.ATTRID_ARTIST:
-                    attrValue = resultItems.get(itemIndex).getDescription().getExtras()
-                            .getString(MediaMetadata.METADATA_KEY_ARTIST);
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_ARTIST);
                     break;
 
                 case AvrcpConstants.ATTRID_ALBUM:
-                    attrValue = resultItems.get(itemIndex).getDescription().getExtras()
-                            .getString(MediaMetadata.METADATA_KEY_ALBUM);
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_ALBUM);
                     break;
 
                 case AvrcpConstants.ATTRID_TRACK_NUM:
-                    attrValue = resultItems.get(itemIndex).getDescription().getExtras()
-                            .getString(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
                     break;
 
                 case AvrcpConstants.ATTRID_NUM_TRACKS:
-                    attrValue = resultItems.get(itemIndex).getDescription().getExtras()
-                            .getString(MediaMetadata.METADATA_KEY_NUM_TRACKS);
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_NUM_TRACKS);
                     break;
 
                 case AvrcpConstants.ATTRID_GENRE:
-                    attrValue = resultItems.get(itemIndex).getDescription().getExtras()
-                            .getString(MediaMetadata.METADATA_KEY_GENRE);
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
 
                 case AvrcpConstants.ATTRID_PLAY_TIME:
-                    attrValue = resultItems.get(itemIndex).getDescription().getExtras()
-                            .getString(MediaMetadata.METADATA_KEY_DURATION);
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_DURATION);
 
                 case AvrcpConstants.ATTRID_COVER_ART:
-                    Log.e(TAG, "Cover art attribute not supported");
-                    break;
+                    Log.e(TAG, "getAttrValue: Cover art attribute not supported");
+                    return null;
 
                 default:
-                    Log.e(TAG, "Unknown attribute ID");
+                    Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr);
+                    return null;
             }
-        } catch (IndexOutOfBoundsException ex) {
-            Log.w(TAG, "getAttrValue: requested item index out of bounds");
-            return null;
         } catch (NullPointerException ex) {
             Log.w(TAG, "getAttrValue: attr id not found in result");
             /* checking if attribute is title, then it is mandatory and cannot send null */
             if (attr == AvrcpConstants.ATTRID_TITLE) {
-                return "<Unknown Title>";
+                attrValue = "<Unknown Title>";
+            } else {
+                return null;
             }
-            return null;
         }
-        if(DEBUG) Log.d(TAG, "getAttrValue: attrvalue = "+ attrValue + "attr id:" + attr);
+        if (DEBUG) Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + "attr id:" + attr);
         return attrValue;
     }
 
@@ -641,8 +632,6 @@ class BrowsedMediaPlayer {
         int[] attrIds = null; /* array of attr ids */
         String[] attrValues = null; /* array of attr values */
         int attrCounter = 0; /* num attributes for each item */
-        List<MediaBrowser.MediaItem> resultItems = new ArrayList<MediaBrowser.MediaItem>();
-        resultItems.add(mediaItem);
         /* variables to temperorily add attrs */
         ArrayList<String> attrArray = new ArrayList<String>();
         ArrayList<Integer> attrId = new ArrayList<Integer>();
@@ -672,8 +661,8 @@ class BrowsedMediaPlayer {
             /* lookup and copy values of attributes for ids requested above */
             for (int idx = 0; idx < attrTempId.size(); idx++) {
                 /* check if media player provided requested attributes */
-                String value = null;
-                if ((value = getAttrValue(attrTempId.get(idx), resultItems, 0)) != null) {
+                String value = getAttrValue(attrTempId.get(idx), mediaItem);
+                if (value != null) {
                     attrArray.add(value);
                     attrId.add(attrTempId.get(idx));
                     attrCounter++;