Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 376955a7 authored by Marie Janssen's avatar Marie Janssen
Browse files

AVRCP: Gracefully handle missing item attributes

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 29a314e3)
parent 9e863de2
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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());
+21 −31
Original line number Diff line number Diff line
@@ -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,18 +353,12 @@ 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);
                    value = getAttrValue(attribId, item, mediaController);
                    if (value != null) {
                        attrArray.add(value);
                        attrId.add(attribId);
                        attrCnt++;
                    }
                    } else {
                        Log.w(TAG, "invalid attribute id is requested: " + attribId);
                    }
                }
                /* add num attr actually received from media player for a particular item */
                folderDataNative.mAttributesNum[itemIndex] = attrCnt;
@@ -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,20 +434,22 @@ 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;
            }
        }
        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 + "");
    }
}
+8 −5
Original line number Diff line number Diff line
@@ -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;
            }
+27 −0
Original line number Diff line number Diff line
@@ -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 {
+111 −122
Original line number Diff line number Diff line
@@ -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,50 +337,57 @@ 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:
        if (scope != AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) {
            Log.e(TAG, "getTotalNumOfItems error" + scope);
            mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants.RSP_INV_SCOPE, 0, 0);
                break;
            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 (!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;

@@ -387,17 +395,13 @@ class BrowsedMediaPlayer {
            /* 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 {
            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);
            }
        } else {
            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);
        }
                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,14 +470,21 @@ 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) {
        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 */
            if ((result_items = checkIndexOutofBounds(bdaddr, children, startItem, endItem)) == null) {
        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;
@@ -488,30 +497,29 @@ class BrowsedMediaPlayer {

        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) {
            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;
@@ -535,17 +543,12 @@ class BrowsedMediaPlayer {

                    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) {
                    value = getAttrValue(attribId, result_items.get(itemIndex));
                    if (value != null) {
                        attrArray.add(value);
                        attrId.add(attribId);
                        attrCnt++;
                    }
                    } else {
                        Log.d(TAG, "invalid attributed id is requested: " + attribId);
                    }
                }
                /* add num attr actually received from media player for a particular item */
                folderDataNative.mAttributesNum[itemIndex] = attrCnt;
@@ -553,7 +556,7 @@ class BrowsedMediaPlayer {
        }

        /* copy filtered attr ids and attr values to response parameters */
            if (mFolderItemsReqObj.mNumAttr != AvrcpConstants.NUM_ATTR_NONE) {
        if (attrId.size() > 0) {
            folderDataNative.mAttrIds = new int[attrId.size()];
            for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++)
                folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
@@ -561,76 +564,64 @@ class BrowsedMediaPlayer {
        }

        /* 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,
        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;
        }
    }

    public static String getAttrValue(int attr, List<MediaBrowser.MediaItem> resultItems,
            int itemIndex) {

    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");
            }
        } catch (IndexOutOfBoundsException ex) {
            Log.w(TAG, "getAttrValue: requested item index out of bounds");
                    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;
            }
        }
        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++;