Merge tag 'android-6.0.0_r26' into HEAD

Android 6.0.0 release 26

* tag 'android-6.0.0_r26':
  Import translations. DO NOT MERGE
  Import translations. DO NOT MERGE

Change-Id: Ia9bff9db1e5371da310dacd01f2ff85f398be4c5
diff --git a/Android.mk b/Android.mk
index d2b7691..5102b27 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,8 +8,6 @@
 
 LOCAL_PACKAGE_NAME := Music
 
-LOCAL_SDK_VERSION := current
-
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2db6259..c2ba509 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -26,10 +26,12 @@
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
 
     <application android:icon="@drawable/app_music"
         android:label="@string/musicbrowserlabel"
         android:taskAffinity="android.task.music"
+        android:hardwareAccelerated="true"
         android:allowTaskReparenting="true"
         android:usesCleartextTraffic="true">
         <meta-data
diff --git a/src/com/android/music/MediaPlaybackActivity.java b/src/com/android/music/MediaPlaybackActivity.java
index 2f04978..9b97f71 100644
--- a/src/com/android/music/MediaPlaybackActivity.java
+++ b/src/com/android/music/MediaPlaybackActivity.java
@@ -87,6 +87,7 @@
     private Toast mToast;
     private int mTouchSlop;
     private ServiceToken mToken;
+    private boolean mReceiverUnregistered = false;
 
     public MediaPlaybackActivity()
     {
@@ -482,6 +483,8 @@
         IntentFilter f = new IntentFilter();
         f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
         f.addAction(MediaPlaybackService.META_CHANGED);
+        f.addAction(MediaPlaybackService.SHUFFLE_CHANGED);
+        f.addAction(MediaPlaybackService.REPEAT_CHANGED);
         registerReceiver(mStatusListener, new IntentFilter(f));
         updateTrackInfo();
         long next = refreshNow();
@@ -1253,6 +1256,43 @@
                 queueNextRefresh(1);
             } else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
                 setPauseButtonImage();
+            } else if (action.equals(MediaPlaybackService.SHUFFLE_CHANGED)) {
+                setShuffleButtonImage();
+            } else if (action.equals(MediaPlaybackService.REPEAT_CHANGED)) {
+                setRepeatButtonImage();
+            }
+        }
+    };
+
+    private BroadcastReceiver mScreenTimeoutListener = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+                if (mReceiverUnregistered) {
+                    IntentFilter f = new IntentFilter();
+                    f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
+                    f.addAction(MediaPlaybackService.META_CHANGED);
+                    f.addAction(MediaPlaybackService.SHUFFLE_CHANGED);
+                    f.addAction(MediaPlaybackService.REPEAT_CHANGED);
+                    registerReceiver(mStatusListener, f);
+                    mReceiverUnregistered = false;
+                }
+                paused = false;
+
+                if (mPosOverride > 0) {
+                    mPosOverride = -1;
+                }
+                updateTrackInfo();
+                long next = refreshNow();
+                queueNextRefresh(next);
+            } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                paused = false;
+
+                if (!mReceiverUnregistered) {
+                    mHandler.removeMessages(REFRESH);
+                    unregisterReceiver(mStatusListener);
+                    mReceiverUnregistered = true;
+                }
             }
         }
     };
diff --git a/src/com/android/music/MediaPlaybackService.java b/src/com/android/music/MediaPlaybackService.java
index 25d60c7..dbfab4c 100644
--- a/src/com/android/music/MediaPlaybackService.java
+++ b/src/com/android/music/MediaPlaybackService.java
@@ -47,6 +47,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.PowerManager.WakeLock;
 import android.provider.MediaStore;
 import android.util.Log;
@@ -59,6 +60,7 @@
 import java.lang.ref.WeakReference;
 import java.util.Random;
 import java.util.Vector;
+import java.util.HashMap;
 
 /**
  * Provides "background" audio playback capabilities, allowing the
@@ -81,9 +83,12 @@
     public static final int REPEAT_NONE = 0;
     public static final int REPEAT_CURRENT = 1;
     public static final int REPEAT_ALL = 2;
+    private HashMap<Byte, Boolean> mAttributePairs = new HashMap<Byte, Boolean>();
 
     public static final String PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
     public static final String META_CHANGED = "com.android.music.metachanged";
+    public static final String SHUFFLE_CHANGED = "org.codeaurora.music.shuffle";
+    public static final String REPEAT_CHANGED = "org.codeaurora.music.repeat";
     public static final String QUEUE_CHANGED = "com.android.music.queuechanged";
 
     public static final String SERVICECMD = "com.android.music.musicservicecommand";
@@ -94,11 +99,20 @@
     public static final String CMDPLAY = "play";
     public static final String CMDPREVIOUS = "previous";
     public static final String CMDNEXT = "next";
+    public static final String CMDGET = "get";
+    public static final String CMDSET = "set";
 
     public static final String TOGGLEPAUSE_ACTION = "com.android.music.musicservicecommand.togglepause";
     public static final String PAUSE_ACTION = "com.android.music.musicservicecommand.pause";
     public static final String PREVIOUS_ACTION = "com.android.music.musicservicecommand.previous";
     public static final String NEXT_ACTION = "com.android.music.musicservicecommand.next";
+    private static final String PLAYSTATUS_REQUEST = "org.codeaurora.android.music.playstatusrequest";
+    private static final String PLAYSTATUS_RESPONSE = "org.codeaurora.music.playstatusresponse";
+    private static final String PLAYERSETTINGS_REQUEST = "org.codeaurora.music.playersettingsrequest";
+    private static final String PLAYERSETTINGS_RESPONSE = "org.codeaurora.music.playersettingsresponse";
+    private static final String SET_ADDRESSED_PLAYER = "org.codeaurora.music.setaddressedplayer";
+    private static final String EXTRA_SHUFFLE_VAL = "shuffle";
+    private static final String EXTRA_REPEAT_VAL = "repeat";
 
     private static final int TRACK_ENDED = 1;
     private static final int RELEASE_WAKELOCK = 2;
@@ -107,8 +121,18 @@
     private static final int FADEDOWN = 5;
     private static final int FADEUP = 6;
     private static final int TRACK_WENT_TO_NEXT = 7;
+    private static final int ERROR = 8 ;
     private static final int MAX_HISTORY_SIZE = 100;
-    
+    private static final int DEFAULT_REPEAT_VAL = 0;
+    private static final int DEFAULT_SHUFFLE_VAL = 0;
+    private static final int SET_BROWSED_PLAYER = 1001;
+    private static final int SET_PLAY_ITEM = 1002;
+    private static final int GET_NOW_PLAYING_ENTRIES = 1003;
+
+    private static final int SCOPE_FILE_SYSTEM = 0x01;
+    private static final int SCOPE_NOW_PLAYING = 0x03;
+    private static final int INVALID_SONG_UID = 0xffffffff;
+
     private MultiPlayer mPlayer;
     private String mFileToPlay;
     private int mShuffleMode = SHUFFLE_NONE;
@@ -140,6 +164,7 @@
     private final static int PODCASTCOLIDX = 8;
     private final static int BOOKMARKCOLIDX = 9;
     private BroadcastReceiver mUnmountReceiver = null;
+    private BroadcastReceiver mA2dpReceiver = null;
     private WakeLock mWakeLock;
     private int mServiceStartId = -1;
     private boolean mServiceInUse = false;
@@ -149,6 +174,71 @@
     private boolean mQueueIsSaveable = true;
     // used to track what type of audio focus loss caused the playback to pause
     private boolean mPausedByTransientLossOfFocus = false;
+    private SetBrowsedPlayerMonitor mSetBrowsedPlayerMonitor;
+    private SetPlayItemMonitor mSetPlayItemMonitor;
+    private GetNowPlayingEntriesMonitor mGetNowPlayingEntriesMonitor;
+    public static final byte ATTRIBUTE_ALL = -1;
+    public static final byte ERROR_NOTSUPPORTED = -1;
+    public static final byte ATTRIBUTE_EQUALIZER = 1;
+    public static final byte ATTRIBUTE_REPEATMODE = 2;
+    public static final byte ATTRIBUTE_SHUFFLEMODE = 3;
+    public static final byte ATTRIBUTE_SCANMODE = 4;
+
+    private byte [] supportedAttributes = new byte[] {
+                          ATTRIBUTE_REPEATMODE,
+                          ATTRIBUTE_SHUFFLEMODE
+                          };
+
+    public static final byte VALUE_REPEATMODE_OFF = 1;
+    public static final byte VALUE_REPEATMODE_SINGLE = 2;
+    public static final byte VALUE_REPEATMODE_ALL = 3;
+    public static final byte VALUE_REPEATMODE_GROUP = 4;
+
+    private byte [] supportedRepeatValues = new byte [] {
+                            VALUE_REPEATMODE_OFF,
+                            VALUE_REPEATMODE_SINGLE,
+                            VALUE_REPEATMODE_ALL
+                            };
+
+    public static final byte VALUE_SHUFFLEMODE_OFF = 1;
+    public static final byte VALUE_SHUFFLEMODE_ALL = 2;
+    public static final byte VALUE_SHUFFLEMODE_GROUP = 3;
+
+    private byte [] supportedShuffleValues = new byte [] {
+                            VALUE_SHUFFLEMODE_OFF,
+                            VALUE_SHUFFLEMODE_ALL
+                            };
+
+    String [] AttrStr = new String[] {
+                                "",
+                                "Equalizer",
+                                "Repeat Mode",
+                                "Shuffle Mode",
+                                "Scan Mode"
+                                };
+
+    private byte [] unsupportedList = new byte [] {
+                                    0
+                                    };
+    private static final String EXTRA_GET_COMMAND = "commandExtra";
+    private static final String EXTRA_GET_RESPONSE = "Response";
+    private static final int GET_ATTRIBUTE_IDS = 0;
+    private static final int GET_VALUE_IDS = 1;
+    private static final int GET_ATTRIBUTE_TEXT = 2;
+    private static final int GET_VALUE_TEXT        = 3;
+    private static final int GET_ATTRIBUTE_VALUES = 4;
+    private static final int NOTIFY_ATTRIBUTE_VALUES = 5;
+    private static final int SET_ATTRIBUTE_VALUES  = 6;
+    private static final int GET_INVALID = 0xff;
+    private static final byte GET_ATTR_INVALID = 0x7f;
+
+    private static final String EXTRA_ATTRIBUTE_ID = "Attribute";
+    private static final String EXTRA_VALUE_STRING_ARRAY = "ValueStrings";
+    private static final String EXTRA_ATTRIB_VALUE_PAIRS = "AttribValuePairs";
+    private static final String EXTRA_ATTRIBUTE_STRING_ARRAY = "AttributeStrings";
+    private static final String EXTRA_VALUE_ID_ARRAY = "Values";
+    private static final String EXTRA_ATTIBUTE_ID_ARRAY = "Attributes";
+
 
     private SharedPreferences mPreferences;
     // We use this to distinguish between different cards when saving/restoring playlists.
@@ -339,15 +429,29 @@
         mCardId = MusicUtils.getCardId(this);
         
         registerExternalStorageListener();
+        registerA2dpServiceListener();
 
         // Needs to be done in this thread, since otherwise ApplicationContext.getPowerManager() crashes.
         mPlayer = new MultiPlayer();
         mPlayer.setHandler(mMediaplayerHandler);
 
+        mSetBrowsedPlayerMonitor = new SetBrowsedPlayerMonitor();
+        mRemoteControlClient.setBrowsedPlayerUpdateListener(mSetBrowsedPlayerMonitor);
+
+        mSetPlayItemMonitor = new SetPlayItemMonitor();
+        mRemoteControlClient.setPlayItemListener(mSetPlayItemMonitor);
+
+        mGetNowPlayingEntriesMonitor = new GetNowPlayingEntriesMonitor();
+        mRemoteControlClient.setNowPlayingEntriesUpdateListener(mGetNowPlayingEntriesMonitor);
+
         reloadQueue();
         notifyChange(QUEUE_CHANGED);
         notifyChange(META_CHANGED);
 
+        mAttributePairs.put(ATTRIBUTE_EQUALIZER, false);
+        mAttributePairs.put(ATTRIBUTE_REPEATMODE, true);
+        mAttributePairs.put(ATTRIBUTE_SHUFFLEMODE, true);
+
         IntentFilter commandFilter = new IntentFilter();
         commandFilter.addAction(SERVICECMD);
         commandFilter.addAction(TOGGLEPAUSE_ACTION);
@@ -397,6 +501,16 @@
             unregisterReceiver(mUnmountReceiver);
             mUnmountReceiver = null;
         }
+
+        if (mA2dpReceiver != null) {
+            unregisterReceiver(mA2dpReceiver);
+            mA2dpReceiver = null;
+        }
+
+        if (mAttributePairs != null) {
+            mAttributePairs.clear();
+        }
+
         mWakeLock.release();
         super.onDestroy();
     }
@@ -709,7 +823,71 @@
         stopSelf(mServiceStartId);
         return true;
     }
-    
+
+    private void getNowPlayingEntries() {
+        Log.i(LOGTAG,  "getNowPlayingEntries: num of items: " + mPlayListLen);
+        synchronized (mPlayList) {
+            long [] nowPlayingList = new long[mPlayListLen];
+            for (int count = 0; count < mPlayListLen; count++) {
+                nowPlayingList[count] = mPlayList[count];
+            }
+            mRemoteControlClient.updateNowPlayingEntries(nowPlayingList);
+        }
+    }
+
+    private void setBrowsedPlayer() {
+        Log.i(LOGTAG,  "setBrowsedPlayer");
+        Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+        Log.i(LOGTAG, "URI: " + uri);
+        mRemoteControlClient.updateFolderInfoBrowsedPlayer(uri.toString());
+    }
+
+    private void playItem(int scope, long playItemUid) {
+        boolean success = false;
+        Log.i(LOGTAG,  "playItem uid: " + playItemUid + " scope: " + scope);
+        if (playItemUid < 0) {
+            mRemoteControlClient.playItemResponse(success);
+            return;
+        } else if (scope == SCOPE_FILE_SYSTEM) {
+            success = openItem(playItemUid);
+        } else if (scope == SCOPE_NOW_PLAYING) {
+            for (int index = 0; index < mPlayListLen; index++) {
+                if (mPlayList[index] == playItemUid) {
+                    Log.i(LOGTAG, "Now Playing list contains UID at " + index);
+                    success = true;
+                    break;
+                }
+            }
+            if (success) {
+                success = openItem(playItemUid);
+            }
+        }
+        mRemoteControlClient.playItemResponse(success);
+    }
+
+    private boolean openItem (long playItemUid) {
+        boolean isSuccess = false;
+        if (mCursor != null) {
+            mCursor.close();
+            mCursor = null;
+        }
+        if (mPlayListLen == 0) {
+            Log.e(LOGTAG, "Playlist Length = 0");
+            return isSuccess;
+        }
+        stop(false);
+        mCursor = getCursorForId(playItemUid);
+        if (mCursor != null) {
+            long [] list = new long[] { playItemUid };
+            enqueue(list, NOW);
+            Log.i(LOGTAG, "Opened UID: " + playItemUid);
+            isSuccess = true;
+        } else {
+            Log.e(LOGTAG, "Cursor could not be fetched");
+        }
+        return isSuccess;
+    }
+
     private Handler mDelayedStopHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -726,6 +904,24 @@
         }
     };
 
+    private Handler mAvrcpHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SET_BROWSED_PLAYER:
+                    setBrowsedPlayer();
+                    break;
+                case SET_PLAY_ITEM:
+                    playItem(msg.arg1, ((Long)msg.obj).longValue());
+                    break;
+                case GET_NOW_PLAYING_ENTRIES:
+                    getNowPlayingEntries();
+                    break;
+                default:
+            }
+        }
+    };
+
     /**
      * Called when we receive a ACTION_MEDIA_EJECT notification.
      *
@@ -771,6 +967,69 @@
         }
     }
 
+    public void registerA2dpServiceListener() {
+        mA2dpReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                String cmd = intent.getStringExtra("command");
+                if (action.equals(SET_ADDRESSED_PLAYER)) {
+                    play(); // this ensures audio focus change is called and play the media
+                } else if (action.equals(PLAYSTATUS_REQUEST)) {
+                    notifyChange(PLAYSTATUS_RESPONSE);
+                } else if (PLAYERSETTINGS_REQUEST.equals(action)) {
+                    if (CMDGET.equals(cmd)) {
+                        int getCommand = intent.getIntExtra(EXTRA_GET_COMMAND,
+                                                            GET_INVALID);
+                        byte attribute;
+                        byte [] attrIds; byte [] valIds;
+                        switch (getCommand) {
+                            case GET_ATTRIBUTE_IDS:
+                                notifyAttributeIDs(PLAYERSETTINGS_RESPONSE);
+                            break;
+                            case GET_VALUE_IDS:
+                                attribute =
+                                    intent.getByteExtra(EXTRA_ATTRIBUTE_ID,
+                                                        GET_ATTR_INVALID);
+                                notifyValueIDs(PLAYERSETTINGS_RESPONSE, attribute);
+                            break;
+                            case GET_ATTRIBUTE_TEXT:
+                                attrIds = intent.getByteArrayExtra(
+                                                     EXTRA_ATTIBUTE_ID_ARRAY);
+                                notifyAttributesText(PLAYERSETTINGS_RESPONSE, attrIds);
+                            break;
+                            case GET_VALUE_TEXT:
+                                 attribute =
+                                 intent.getByteExtra(EXTRA_ATTRIBUTE_ID,
+                                                    GET_ATTR_INVALID);
+                                 valIds = intent.getByteArrayExtra(
+                                                     EXTRA_VALUE_ID_ARRAY);
+                                 notifyAttributeValuesText(
+                                     PLAYERSETTINGS_RESPONSE, attribute, valIds);
+                            break;
+                            case GET_ATTRIBUTE_VALUES:
+                                 notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+                                             mAttributePairs, GET_ATTRIBUTE_VALUES);
+                            break;
+                            default:
+                               Log.e(LOGTAG, "invalid getCommand"+getCommand);
+                            break;
+                        }
+                    } else if (CMDSET.equals(cmd)){
+                        byte[] attribValuePairs = intent.getByteArrayExtra(
+                                                    EXTRA_ATTRIB_VALUE_PAIRS);
+                        setValidAttributes(attribValuePairs);
+                    }
+                }
+            }
+        };
+        IntentFilter iFilter = new IntentFilter();
+        iFilter.addAction(SET_ADDRESSED_PLAYER);
+        iFilter.addAction(PLAYSTATUS_REQUEST);
+        iFilter.addAction(PLAYERSETTINGS_REQUEST);
+        registerReceiver(mA2dpReceiver, iFilter);
+    }
+
     /**
      * Notify the change-receivers that something has changed.
      * The intent that is sent contains the following data
@@ -801,19 +1060,35 @@
         sendStickyBroadcast(i);
 
         if (what.equals(PLAYSTATE_CHANGED)) {
-            mRemoteControlClient.setPlaybackState(isPlaying() ?
-                    RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
+            mRemoteControlClient.setPlaybackState((isPlaying() ?
+                    RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED),
+                    position() , RemoteControlClient.PLAYBACK_SPEED_1X);
         } else if (what.equals(META_CHANGED)) {
             RemoteControlClient.MetadataEditor ed = mRemoteControlClient.editMetadata(true);
             ed.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName());
             ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName());
-            ed.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName());
+            ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, getArtistName());
             ed.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration());
+            if ((mPlayList != null) && (mPlayPos >= 0) && (mPlayPos < mPlayList.length)) {
+                ed.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+                                                            mPlayList[mPlayPos]);
+            } else {
+                ed.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+                                                                INVALID_SONG_UID);
+            }
+            ed.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, mPlayPos);
+            try {
+                ed.putLong(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, mPlayListLen);
+            } catch (IllegalArgumentException e) {
+                Log.e(LOGTAG, "METADATA_KEY_NUM_TRACKS: failed: " + e);
+            }
             Bitmap b = MusicUtils.getArtwork(this, getAudioId(), getAlbumId(), false);
             if (b != null) {
                 ed.putBitmap(MetadataEditor.BITMAP_KEY_ARTWORK, b);
             }
             ed.apply();
+        } else if (what.equals(QUEUE_CHANGED)) {
+            mRemoteControlClient.updateNowPlayingContentChange();
         }
 
         if (what.equals(QUEUE_CHANGED)) {
@@ -1085,12 +1360,14 @@
     }
 
     private void setNextTrack() {
-        mNextPlayPos = getNextPosition(false);
-        if (mNextPlayPos >= 0) {
-            long id = mPlayList[mNextPlayPos];
-            mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
-        } else {
-            mPlayer.setNextDataSource(null);
+        if(SystemProperties.getBoolean("gapless.audio.decode", false)) {
+            mNextPlayPos = getNextPosition(false);
+            if (mNextPlayPos >= 0) {
+                long id = mPlayList[mNextPlayPos];
+                mPlayer.setNextDataSource(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
+            } else {
+                mPlayer.setNextDataSource(null);
+            }
         }
     }
 
@@ -1158,6 +1435,7 @@
                 AudioManager.AUDIOFOCUS_GAIN);
         mAudioManager.registerMediaButtonEventReceiver(new ComponentName(this.getPackageName(),
                 MediaButtonIntentReceiver.class.getName()));
+        mAudioManager.registerRemoteControlClient(mRemoteControlClient);
 
         if (mPlayer.isInitialized()) {
             // if we are at the end of the song, go to the next song first
@@ -1527,6 +1805,33 @@
         return false;
     }
 
+    private class SetBrowsedPlayerMonitor implements
+                        RemoteControlClient.OnSetBrowsedPlayerListener{
+        @Override
+        public void onSetBrowsedPlayer() {
+            Log.d(LOGTAG, "onSetBrowsedPlayer");
+            mAvrcpHandler.obtainMessage(SET_BROWSED_PLAYER).sendToTarget();
+        }
+    };
+
+    private class SetPlayItemMonitor implements
+                        RemoteControlClient.OnSetPlayItemListener{
+        @Override
+        public void onSetPlayItem(int scope, long uid) {
+            Log.d(LOGTAG, "onSetPlayItem");
+            mAvrcpHandler.obtainMessage(SET_PLAY_ITEM, scope, 0, new Long(uid)).sendToTarget();
+        }
+    };
+
+    private class GetNowPlayingEntriesMonitor implements
+                        RemoteControlClient.OnGetNowPlayingEntriesListener{
+        @Override
+        public void onGetNowPlayingEntries() {
+            Log.d(LOGTAG, "onGetNowPlayingEntries");
+            mAvrcpHandler.obtainMessage(GET_NOW_PLAYING_ENTRIES).sendToTarget();
+        }
+    };
+
     // A simple variation of Random that makes sure that the
     // value it returns is not equal to the value it returned
     // previously, unless the interval is 1.
@@ -1655,6 +1960,12 @@
     public void setShuffleMode(int shufflemode) {
         synchronized(this) {
             if (mShuffleMode == shufflemode && mPlayListLen > 0) {
+            /**
+             * Some carkits send Shuffle Values same as our current
+             * values. In such cases we need to respond back to ck
+             */
+                notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+                            mAttributePairs, SET_ATTRIBUTE_VALUES);
                 return;
             }
             mShuffleMode = shufflemode;
@@ -1666,12 +1977,17 @@
                     openCurrentAndNext();
                     play();
                     notifyChange(META_CHANGED);
+                    notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+                            mAttributePairs, SET_ATTRIBUTE_VALUES);
                     return;
                 } else {
                     // failed to build a list of files to shuffle
                     mShuffleMode = SHUFFLE_NONE;
                 }
             }
+            notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+                            mAttributePairs, SET_ATTRIBUTE_VALUES);
+            notifyChange(SHUFFLE_CHANGED);
             saveQueue(false);
         }
     }
@@ -1683,6 +1999,9 @@
         synchronized(this) {
             mRepeatMode = repeatmode;
             setNextTrack();
+            notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+                            mAttributePairs, SET_ATTRIBUTE_VALUES);
+            notifyChange(REPEAT_CHANGED);
             saveQueue(false);
         }
     }
@@ -1861,6 +2180,240 @@
     }
 
     /**
+     * Returns the player supported attribute IDs.
+     */
+    private void notifyAttributeIDs(String what) {
+        Intent i = new Intent(what);
+        i.putExtra(EXTRA_GET_RESPONSE, GET_ATTRIBUTE_IDS);
+        i.putExtra(EXTRA_ATTIBUTE_ID_ARRAY, supportedAttributes);
+        Log.e(LOGTAG, "notifying attributes");
+        sendBroadcast(i);
+    }
+
+    /**
+     * Returns the player supported value IDs for given attrib.
+     */
+    private void notifyValueIDs(String what, byte attribute) {
+        Intent intent = new Intent(what);
+        intent.putExtra(EXTRA_GET_RESPONSE, GET_VALUE_IDS);
+        intent.putExtra(EXTRA_ATTRIBUTE_ID, attribute);
+        switch (attribute) {
+            case ATTRIBUTE_REPEATMODE:
+                intent.putExtra(EXTRA_VALUE_ID_ARRAY, supportedRepeatValues);
+            break;
+            case ATTRIBUTE_SHUFFLEMODE:
+                intent.putExtra(EXTRA_VALUE_ID_ARRAY, supportedShuffleValues);
+            break;
+            default:
+                Log.e(LOGTAG,"unsupported attribute"+attribute);
+                intent.putExtra(EXTRA_VALUE_ID_ARRAY, unsupportedList);
+            break;
+        }
+        sendBroadcast(intent);
+    }
+
+    /**
+     * Returns the player supported attrib text for given IDs.
+     */
+    private void notifyAttributesText(String what, byte [] attrIds) {
+        String [] AttribStrings = new String [attrIds.length];
+        Intent intent = new Intent(what);
+        intent.putExtra(EXTRA_GET_RESPONSE, GET_ATTRIBUTE_TEXT);
+        for (int i = 0; i < attrIds.length; i++) {
+            if (attrIds[i] >= AttrStr.length) {
+                Log.e(LOGTAG, "attrib id is"+attrIds[i]+"which is not supported");
+                AttribStrings[i] = "";
+            } else {
+                AttribStrings[i] = AttrStr[attrIds[i]];
+            }
+        }
+        intent.putExtra(EXTRA_ATTRIBUTE_STRING_ARRAY, AttribStrings);
+        sendBroadcast(intent);
+    }
+
+    /**
+     * Returns the player supported value text for given IDs.
+     */
+    private void notifyAttributeValuesText(String what, int attribute,
+                                           byte [] valIds) {
+        Intent intent = new Intent(what);
+        String [] ValueStrings = new String [valIds.length];
+        intent.putExtra(EXTRA_GET_RESPONSE,GET_VALUE_TEXT);
+        intent.putExtra(EXTRA_ATTRIBUTE_ID, attribute);
+        Log.e(LOGTAG, "attrib is "+ attribute);
+        String [] valueStrs = null;
+        switch (attribute) {
+            case ATTRIBUTE_REPEATMODE:
+                valueStrs = new String[] {
+                                             "",
+                                             getString(R.string.repeat_off_notif),
+                                             getString(R.string.repeat_current_notif),
+                                             getString(R.string.repeat_all_notif),
+                                          };
+            break;
+            case ATTRIBUTE_SHUFFLEMODE:
+                valueStrs = new String[] {
+                                           "",
+                                           getString(R.string.shuffle_off_notif),
+                                           getString(R.string.shuffle_on_notif),
+                                          };
+            break;
+        }
+        for (int i = 0; i < valIds.length; i++) {
+            if ((valueStrs == null) ||
+                (valIds[i] >= valueStrs.length)) {
+                Log.e(LOGTAG, "value id is" + valIds[i] + "which is not supported");
+                ValueStrings[i] = "";
+            } else {
+                ValueStrings[i] = valueStrs[valIds[i]];
+            }
+        }
+        intent.putExtra(EXTRA_VALUE_STRING_ARRAY, ValueStrings);
+        sendBroadcast(intent);
+    }
+
+    /**
+     * Returns the player current values for given attrib IDs.
+     */
+    private void notifyAttributeValues(String what, HashMap<Byte, Boolean> attrIds, int extra) {
+        Intent intent = new Intent(what);
+        intent.putExtra(EXTRA_GET_RESPONSE, extra);
+        int j = 0;
+        byte [] retValarray = new byte [attrIds.size()*2];
+        for (int i = 0; i < attrIds.size()*2; i++) {
+            retValarray[i] = 0x0;
+        }
+
+        for (Byte attribute : attrIds.keySet()) {
+            if(attrIds.get(attribute)) {
+                retValarray[j] = attribute;
+                if (attribute == ATTRIBUTE_REPEATMODE) {
+                    retValarray[j+1] = getMappingRepeatVal(mRepeatMode);
+                } else if (attribute == ATTRIBUTE_SHUFFLEMODE) {
+                    retValarray[j+1] = getMappingShuffleVal(mShuffleMode);
+                }
+                j += 2;
+            } else {
+                retValarray[j] = attribute;
+                retValarray[j+1] = ERROR_NOTSUPPORTED;
+                j += 2;
+            }
+        }
+        intent.putExtra(EXTRA_ATTRIB_VALUE_PAIRS, retValarray);
+        sendBroadcast(intent);
+    }
+
+    /**
+     * Sets the values to current player for given attrib IDs.
+     */
+    private void setValidAttributes(byte [] attribValuePairs) {
+        byte attrib, value;
+
+        for (int i = 0; i < (attribValuePairs.length-1); i += 2) {
+           attrib = attribValuePairs[i];
+           value = attribValuePairs[i+1];
+           switch(attrib) {
+                case ATTRIBUTE_REPEATMODE:
+                    if (isValidRepeatMode(value)) {
+                        setRepeatMode(getMappingRepeatMode(value));
+                    }
+                break;
+                case ATTRIBUTE_SHUFFLEMODE:
+                    if (isValidShuffleMode(value)) {
+                        setShuffleMode(getMappingShuffleMode(value));
+                    }
+                break;
+                default:
+                   Log.e(LOGTAG,"Unknown attribute"+attrib);
+                   notifyAttributeValues(PLAYERSETTINGS_RESPONSE,
+                            mAttributePairs, SET_ATTRIBUTE_VALUES);
+                break;
+           }
+        }
+    }
+
+    byte getMappingRepeatVal (int repeatMode) {
+        switch (repeatMode) {
+            case REPEAT_NONE:
+                return VALUE_REPEATMODE_OFF;
+            case REPEAT_CURRENT:
+                return VALUE_REPEATMODE_SINGLE;
+            case REPEAT_ALL:
+                return VALUE_REPEATMODE_ALL;
+            default:
+                return VALUE_REPEATMODE_OFF;
+        }
+    }
+
+    byte getMappingShuffleVal (int shuffleMode) {
+        switch (shuffleMode) {
+            case SHUFFLE_NONE:
+                return VALUE_SHUFFLEMODE_OFF;
+            case SHUFFLE_NORMAL:
+                return VALUE_SHUFFLEMODE_ALL;
+            case SHUFFLE_AUTO:
+                return VALUE_SHUFFLEMODE_ALL;
+            default:
+                return VALUE_SHUFFLEMODE_OFF;
+        }
+    }
+
+    int getMappingRepeatMode (byte repeatVal) {
+        switch (repeatVal) {
+            case VALUE_REPEATMODE_OFF:
+                return REPEAT_NONE;
+            case VALUE_REPEATMODE_SINGLE:
+                return REPEAT_CURRENT;
+            case VALUE_REPEATMODE_ALL:
+            case VALUE_REPEATMODE_GROUP:
+                return REPEAT_ALL;
+            default:
+                return REPEAT_NONE;
+        }
+    }
+
+    int getMappingShuffleMode (byte shuffleVal) {
+        switch (shuffleVal) {
+            case VALUE_SHUFFLEMODE_OFF:
+                return SHUFFLE_NONE;
+            case VALUE_SHUFFLEMODE_ALL:
+            case VALUE_SHUFFLEMODE_GROUP:
+                return SHUFFLE_NORMAL;
+            default:
+                return SHUFFLE_NONE;
+        }
+    }
+
+    /**
+     * Validates the value with CMDSET for Repeat mode.
+     */
+    private boolean isValidRepeatMode(byte value) {
+        if (value == 0) {
+            return false;
+        }
+        value--;
+        if ((value >= REPEAT_NONE) && ( value <= REPEAT_ALL)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Validates the value with CMDSET for Shuffle mode.
+     */
+    private boolean isValidShuffleMode(byte value) {
+        if (value == 0) {
+            return false;
+        }
+        value--;
+        // check the mapping for local suffle and argument
+        if ((value >= SHUFFLE_NONE) && ( value <= SHUFFLE_AUTO)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Provides a unified interface for dealing with midi files and
      * other media files.
      */