Merge "Don't add title margins during to Toolbar layout if titles don't fit" into lmp-dev
diff --git a/Android.mk b/Android.mk
index 3edaefc..35bb66c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -325,8 +325,6 @@
 	media/java/android/media/IRemoteVolumeObserver.aidl \
 	media/java/android/media/IRingtonePlayer.aidl \
 	media/java/android/media/IVolumeController.aidl \
-	media/java/android/media/browse/IMediaBrowserService.aidl \
-	media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl \
 	media/java/android/media/projection/IMediaProjection.aidl \
 	media/java/android/media/projection/IMediaProjectionCallback.aidl \
 	media/java/android/media/projection/IMediaProjectionManager.aidl \
@@ -346,6 +344,8 @@
 	media/java/android/media/tv/ITvInputServiceCallback.aidl \
 	media/java/android/media/tv/ITvInputSession.aidl \
 	media/java/android/media/tv/ITvInputSessionCallback.aidl \
+	media/java/android/service/media/IMediaBrowserService.aidl \
+	media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl \
 	telecomm/java/com/android/internal/telecomm/IVideoCallback.aidl \
 	telecomm/java/com/android/internal/telecomm/IVideoProvider.aidl \
 	telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 43b5d9b..84c175c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3609,8 +3609,7 @@
     method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap);
     method public boolean clearApplicationUserData();
     method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
-    method public int getAppTaskThumbnailHeight();
-    method public int getAppTaskThumbnailWidth();
+    method public android.util.Size getAppTaskThumbnailSize();
     method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
     method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
     method public int getLargeMemoryClass();
@@ -14922,6 +14921,31 @@
     ctor public MediaCryptoException(java.lang.String);
   }
 
+  public class MediaDescription implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public android.net.Uri getIconUri();
+    method public java.lang.String getMediaId();
+    method public java.lang.CharSequence getSubtitle();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static class MediaDescription.Builder {
+    ctor public MediaDescription.Builder();
+    method public android.media.MediaDescription build();
+    method public android.media.MediaDescription.Builder setDescription(java.lang.CharSequence);
+    method public android.media.MediaDescription.Builder setExtras(android.os.Bundle);
+    method public android.media.MediaDescription.Builder setIconBitmap(android.graphics.Bitmap);
+    method public android.media.MediaDescription.Builder setIconUri(android.net.Uri);
+    method public android.media.MediaDescription.Builder setMediaId(java.lang.String);
+    method public android.media.MediaDescription.Builder setSubtitle(java.lang.CharSequence);
+    method public android.media.MediaDescription.Builder setTitle(java.lang.CharSequence);
+  }
+
   public final class MediaDrm {
     ctor public MediaDrm(java.util.UUID) throws android.media.UnsupportedSchemeException;
     method public void closeSession(byte[]);
@@ -15100,7 +15124,7 @@
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
     method public android.graphics.Bitmap getBitmap(java.lang.String);
-    method public android.media.MediaMetadata.Description getDescription();
+    method public android.media.MediaDescription getDescription();
     method public long getLong(java.lang.String);
     method public android.media.Rating getRating(java.lang.String);
     method public java.lang.String getString(java.lang.String);
@@ -15128,6 +15152,7 @@
     field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
     field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
     field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final java.lang.String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
     field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
     field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
     field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
@@ -15148,14 +15173,6 @@
     method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
   }
 
-  public final class MediaMetadata.Description {
-    method public java.lang.CharSequence getDescription();
-    method public android.graphics.Bitmap getIcon();
-    method public android.net.Uri getIconUri();
-    method public java.lang.CharSequence getSubtitle();
-    method public java.lang.CharSequence getTitle();
-  }
-
   public abstract deprecated class MediaMetadataEditor {
     method public synchronized void addEditableKey(int);
     method public abstract void apply();
@@ -15706,6 +15723,7 @@
   }
 
   public class Ringtone {
+    method public android.media.AudioAttributes getAudioAttributes();
     method public deprecated int getStreamType();
     method public java.lang.String getTitle(android.content.Context);
     method public boolean isPlaying();
@@ -16241,7 +16259,6 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
-    method public void loadIcon(android.net.Uri, int, int, android.media.browse.MediaBrowser.IconCallback);
     method public void subscribe(android.net.Uri, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(android.net.Uri);
   }
@@ -16253,27 +16270,12 @@
     method public void onConnectionSuspended();
   }
 
-  public static abstract class MediaBrowser.IconCallback {
-    ctor public MediaBrowser.IconCallback();
-    method public void onError(android.net.Uri);
-    method public void onIconLoaded(android.net.Uri, android.graphics.Bitmap);
-  }
-
-  public static abstract class MediaBrowser.SubscriptionCallback {
-    ctor public MediaBrowser.SubscriptionCallback();
-    method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowserItem>);
-    method public void onError(android.net.Uri);
-  }
-
-  public final class MediaBrowserItem implements android.os.Parcelable {
+  public static class MediaBrowser.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowser.MediaItem(int, android.media.MediaDescription);
     method public int describeContents();
-    method public android.os.Bundle getExtras();
+    method public android.media.MediaDescription getDescription();
     method public int getFlags();
-    method public int getIconResourceId();
-    method public android.net.Uri getIconUri();
-    method public java.lang.CharSequence getSummary();
-    method public java.lang.CharSequence getTitle();
-    method public android.net.Uri getUri();
+    method public java.lang.String getMediaId();
     method public boolean isBrowsable();
     method public boolean isPlayable();
     method public void writeToParcel(android.os.Parcel, int);
@@ -16282,37 +16284,10 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
-  public static final class MediaBrowserItem.Builder {
-    ctor public MediaBrowserItem.Builder(android.net.Uri, int, java.lang.CharSequence);
-    method public android.media.browse.MediaBrowserItem build();
-    method public android.media.browse.MediaBrowserItem.Builder setExtras(android.os.Bundle);
-    method public android.media.browse.MediaBrowserItem.Builder setIconResourceId(int);
-    method public android.media.browse.MediaBrowserItem.Builder setIconUri(android.net.Uri);
-    method public android.media.browse.MediaBrowserItem.Builder setSummary(java.lang.CharSequence);
-  }
-
-  public abstract class MediaBrowserService extends android.app.Service {
-    ctor public MediaBrowserService();
-    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public android.media.session.MediaSession.Token getSessionToken();
-    method public void notifyChildrenChanged(android.net.Uri);
-    method public android.os.IBinder onBind(android.content.Intent);
-    method public abstract android.media.browse.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
-    method public abstract void onLoadChildren(android.net.Uri, android.media.browse.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowserItem>>);
-    method public abstract void onLoadIcon(android.net.Uri, int, int, android.media.browse.MediaBrowserService.Result<android.graphics.Bitmap>);
-    method public void setSessionToken(android.media.session.MediaSession.Token);
-    field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
-  }
-
-  public static final class MediaBrowserService.BrowserRoot {
-    ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
-    method public android.os.Bundle getExtras();
-    method public android.net.Uri getRootUri();
-  }
-
-  public class MediaBrowserService.Result {
-    method public void detach();
-    method public void sendResult(T);
+  public static abstract class MediaBrowser.SubscriptionCallback {
+    ctor public MediaBrowser.SubscriptionCallback();
+    method public void onChildrenLoaded(android.net.Uri, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onError(android.net.Uri);
   }
 
 }
@@ -16407,7 +16382,7 @@
     method public java.lang.String getPackageName();
     method public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
     method public android.media.session.PlaybackState getPlaybackState();
-    method public java.util.List<android.media.session.MediaSession.Item> getQueue();
+    method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
     method public java.lang.CharSequence getQueueTitle();
     method public int getRatingType();
     method public android.app.PendingIntent getSessionActivity();
@@ -16424,7 +16399,7 @@
     method public void onExtrasChanged(android.os.Bundle);
     method public void onMetadataChanged(android.media.MediaMetadata);
     method public void onPlaybackStateChanged(android.media.session.PlaybackState);
-    method public void onQueueChanged(java.util.List<android.media.session.MediaSession.Item>);
+    method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void onQueueTitleChanged(java.lang.CharSequence);
     method public void onSessionDestroyed();
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
@@ -16444,16 +16419,16 @@
     method public void fastForward();
     method public void pause();
     method public void play();
+    method public void playFromMediaId(java.lang.String, android.os.Bundle);
     method public void playFromSearch(java.lang.String, android.os.Bundle);
-    method public void playUri(android.net.Uri, android.os.Bundle);
     method public void rewind();
     method public void seekTo(long);
     method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
-    method public void skipToItem(long);
     method public void skipToNext();
     method public void skipToPrevious();
+    method public void skipToQueueItem(long);
     method public void stop();
   }
 
@@ -16474,7 +16449,7 @@
     method public void setPlaybackState(android.media.session.PlaybackState);
     method public void setPlaybackToLocal(android.media.AudioAttributes);
     method public void setPlaybackToRemote(android.media.VolumeProvider);
-    method public void setQueue(java.util.List<android.media.session.MediaSession.Item>);
+    method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void setQueueTitle(java.lang.CharSequence);
     method public void setSessionActivity(android.app.PendingIntent);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
@@ -16489,34 +16464,27 @@
     method public boolean onMediaButtonEvent(android.content.Intent);
     method public void onPause();
     method public void onPlay();
+    method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
-    method public void onPlayUri(android.net.Uri, android.os.Bundle);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
-    method public void onSkipToItem(long);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
     method public void onStop();
   }
 
-  public static final class MediaSession.Item implements android.os.Parcelable {
+  public static final class MediaSession.QueueItem implements android.os.Parcelable {
+    ctor public MediaSession.QueueItem(android.media.MediaDescription, long);
     method public int describeContents();
-    method public android.os.Bundle getExtras();
-    method public long getId();
-    method public android.media.MediaMetadata getMetadata();
-    method public android.net.Uri getUri();
+    method public android.media.MediaDescription getDescription();
+    method public long getQueueId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int UNKNOWN_ID = -1; // 0xffffffff
   }
 
-  public static final class MediaSession.Item.Builder {
-    ctor public MediaSession.Item.Builder(android.media.MediaMetadata, long, android.net.Uri);
-    method public android.media.session.MediaSession.Item build();
-    method public android.media.session.MediaSession.Item.Builder setExtras(android.os.Bundle);
-  }
-
   public static final class MediaSession.Token implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -16537,6 +16505,7 @@
   public final class PlaybackState implements android.os.Parcelable {
     method public int describeContents();
     method public long getActions();
+    method public long getActiveQueueItemId();
     method public long getBufferedPosition();
     method public java.util.List<android.media.session.PlaybackState.CustomAction> getCustomActions();
     method public java.lang.CharSequence getErrorMessage();
@@ -16548,15 +16517,15 @@
     field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
     field public static final long ACTION_PAUSE = 2L; // 0x2L
     field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
     field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
     field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
-    field public static final long ACTION_PLAY_URI = 1024L; // 0x400L
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
-    field public static final long ACTION_SKIP_TO_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
@@ -16570,6 +16539,7 @@
     field public static final int STATE_REWINDING = 5; // 0x5
     field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
     field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
@@ -16580,7 +16550,7 @@
     method public android.media.session.PlaybackState.Builder addCustomAction(android.media.session.PlaybackState.CustomAction);
     method public android.media.session.PlaybackState build();
     method public android.media.session.PlaybackState.Builder setActions(long);
-    method public android.media.session.PlaybackState.Builder setActiveItem(long);
+    method public android.media.session.PlaybackState.Builder setActiveQueueItemId(long);
     method public android.media.session.PlaybackState.Builder setBufferedPosition(long);
     method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
     method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
@@ -27096,6 +27066,33 @@
 
 }
 
+package android.service.media {
+
+  public abstract class MediaBrowserService extends android.app.Service {
+    ctor public MediaBrowserService();
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.media.session.MediaSession.Token getSessionToken();
+    method public void notifyChildrenChanged(android.net.Uri);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.service.media.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+    method public abstract void onLoadChildren(android.net.Uri, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
+    method public void setSessionToken(android.media.session.MediaSession.Token);
+    field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserService.BrowserRoot {
+    ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
+    method public android.os.Bundle getExtras();
+    method public android.net.Uri getRootUri();
+  }
+
+  public class MediaBrowserService.Result {
+    method public void detach();
+    method public void sendResult(T);
+  }
+
+}
+
 package android.service.notification {
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -27106,9 +27103,11 @@
     method public final void cancelNotifications(java.lang.String[]);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
+    method public final int getCurrentInterruptionFilter();
     method public final int getCurrentListenerHints();
     method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
     method public android.os.IBinder onBind(android.content.Intent);
+    method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
     method public void onListenerHintsChanged(int);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification);
@@ -27116,13 +27115,12 @@
     method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+    method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
-    field public static final int HINTS_NONE = 0; // 0x0
-    field public static final int HINT_HOST_DISABLE_EFFECTS = 4; // 0x4
-    field public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1; // 0x1
-    field public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3; // 0x3
-    field public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2; // 0x2
-    field public static final int HOST_INTERRUPTION_LEVEL_MASK = 3; // 0x3
+    field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
+    field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
+    field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
   }
 
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 74ccbc2..6e77e13 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -161,17 +161,17 @@
     LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
             "Error constructing dalvik cache : %s", strerror(errno));
 
-    int result = mkdir(dalvikCacheDir, 0771);
+    int result = mkdir(dalvikCacheDir, 0711);
     LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),
             "Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno));
 
     // We always perform these steps because the directory might
     // already exist, with wider permissions and a different owner
     // than we'd like.
-    result = chown(dalvikCacheDir, AID_SYSTEM, AID_SYSTEM);
+    result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT);
     LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno));
 
-    result = chmod(dalvikCacheDir, 0771);
+    result = chmod(dalvikCacheDir, 0711);
     LOG_ALWAYS_FATAL_IF((result < 0),
             "Error changing dalvik-cache permissions : %s", strerror(errno));
 }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ffb9c95..bc54055 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -52,6 +52,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
+import android.util.Size;
 import android.util.Slog;
 
 import java.io.FileDescriptor;
@@ -1026,24 +1027,13 @@
     }
 
     /**
-     * Return the current design width for {@link AppTask} thumbnails, for use
+     * Return the current design dimensions for {@link AppTask} thumbnails, for use
      * with {@link #addAppTask}.
      */
-    public int getAppTaskThumbnailWidth() {
+    public Size getAppTaskThumbnailSize() {
         synchronized (this) {
             ensureAppTaskThumbnailSizeLocked();
-            return mAppTaskThumbnailSize.x;
-        }
-    }
-
-    /**
-     * Return the current design height for {@link AppTask} thumbnails, for use
-     * with {@link #addAppTask}.
-     */
-    public int getAppTaskThumbnailHeight() {
-        synchronized (this) {
-            ensureAppTaskThumbnailSizeLocked();
-            return mAppTaskThumbnailSize.y;
+            return new Size(mAppTaskThumbnailSize.x, mAppTaskThumbnailSize.y);
         }
     }
 
@@ -1072,9 +1062,9 @@
      * set on it.
      * @param description Optional additional description information.
      * @param thumbnail Thumbnail to use for the recents entry.  Should be the size given by
-     * {@link #getAppTaskThumbnailWidth()} and {@link #getAppTaskThumbnailHeight()}.  If the
-     * bitmap is not that exact size, it will be recreated in your process, probably in a way
-     * you don't like, before the recents entry is added.
+     * {@link #getAppTaskThumbnailSize()}.  If the bitmap is not that exact size, it will be
+     * recreated in your process, probably in a way you don't like, before the recents entry
+     * is added.
      *
      * @return Returns the task id of the newly added app task, or -1 if the add failed.  The
      * most likely cause of failure is that there is no more room for more tasks for your app.
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 1e556d6..f79d32b 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -135,6 +135,9 @@
                             mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
                 }
                 break;
+            case BUTTON_NEGATIVE:
+                cancel();
+                break;
         }
     }
 
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 75ecbd9..5a6898d 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -327,7 +327,7 @@
                 public void run() {
                     if (mAnimations++ < MIN_ANIMATION_FRAMES) {
                         getDecor().postOnAnimation(this);
-                    } else {
+                    } else if (mResultReceiver != null) {
                         mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
                         mResultReceiver = null; // all done sending messages.
                     }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 07e9a94..214f50c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -61,6 +61,8 @@
     ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
     void requestHintsFromListener(in INotificationListener token, int hints);
     int getHintsFromListener(in INotificationListener token);
+    void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
+    int getInterruptionFilterFromListener(in INotificationListener token);
 
     ComponentName getEffectsSuppressor();
 
diff --git a/core/java/android/app/backup/BackupHelper.java b/core/java/android/app/backup/BackupHelper.java
index e3f0d54..7cbbbc3 100644
--- a/core/java/android/app/backup/BackupHelper.java
+++ b/core/java/android/app/backup/BackupHelper.java
@@ -37,10 +37,9 @@
  */
 public interface BackupHelper {
     /**
-     * Based on <code>oldState</code>, determine which of the files from the
-     * application's data directory need to be backed up, write them to
-     * <code>data</code>, and fill in <code>newState</code> with the state as it
-     * exists now.
+     * Based on <code>oldState</code>, determine what application content
+     * needs to be backed up, write it to <code>data</code>, and fill in
+     * <code>newState</code> with the complete state as it exists now.
      * <p>
      * Implementing this method is much like implementing
      * {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 513d222..d75dfe6 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -721,6 +721,13 @@
             checkIfCameraClosedOrInError();
 
             mDeviceHandler.post(mCallOnBusy);
+
+            // If already idle, just do a busy->idle transition immediately, don't actually
+            // flush.
+            if (mIdle) {
+                mDeviceHandler.post(mCallOnIdle);
+                return;
+            }
             try {
                 LongParcelable lastFrameNumberRef = new LongParcelable();
                 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 95d1351..ae11f47 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2113,6 +2113,13 @@
         public static final String VOLUME_MASTER_MUTE = "volume_master_mute";
 
         /**
+         * Microphone mute (int 1 = mute, 0 = not muted).
+         *
+         * @hide
+         */
+        public static final String MICROPHONE_MUTE = "microphone_mute";
+
+        /**
          * Whether the notifications should use the ring volume (value of 1) or
          * a separate notification volume (value of 0). In most cases, users
          * will have this enabled so the notification and ringer volumes will be
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 93b2d3b..8ca9b6c 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -29,4 +29,5 @@
             in NotificationRankingUpdate update);
     void onNotificationRankingUpdate(in NotificationRankingUpdate update);
     void onListenerHintsChanged(int hints);
-}
\ No newline at end of file
+    void onInterruptionFilterChanged(int interruptionFilter);
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 450b9a7..a544b2d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -58,26 +58,28 @@
     private final String TAG = NotificationListenerService.class.getSimpleName()
             + "[" + getClass().getSimpleName() + "]";
 
-    /** {@link #getCurrentListenerHints() Listener hints} constant - default state. */
-    public static final int HINTS_NONE = 0;
+    /**
+     * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+     *     Normal interruption filter.
+     */
+    public static final int INTERRUPTION_FILTER_ALL = 1;
 
-    /** Bitmask range for {@link #getCurrentListenerHints() Listener hints} host interruption level
-     * constants.  */
-    public static final int HOST_INTERRUPTION_LEVEL_MASK = 0x3;
+    /**
+     * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+     *     Priority interruption filter.
+     */
+    public static final int INTERRUPTION_FILTER_PRIORITY = 2;
 
-    /** {@link #getCurrentListenerHints() Listener hints} constant - Normal interruption level. */
-    public static final int HINT_HOST_INTERRUPTION_LEVEL_ALL = 1;
-
-    /** {@link #getCurrentListenerHints() Listener hints} constant - Priority interruption level. */
-    public static final int HINT_HOST_INTERRUPTION_LEVEL_PRIORITY = 2;
-
-    /** {@link #getCurrentListenerHints() Listener hints} constant - No interruptions level. */
-    public static final int HINT_HOST_INTERRUPTION_LEVEL_NONE = 3;
+    /**
+     * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
+     *     No interruptions filter.
+     */
+    public static final int INTERRUPTION_FILTER_NONE = 3;
 
     /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
      * should disable notification sound, vibrating and other visual or aural effects.
-     * This does not change the interruption level, only the effects. **/
-    public static final int HINT_HOST_DISABLE_EFFECTS = 1 << 2;
+     * This does not change the interruption filter, only the effects. **/
+    public static final int HINT_HOST_DISABLE_EFFECTS = 1;
 
     private INotificationListenerWrapper mWrapper = null;
     private RankingMap mRankingMap;
@@ -197,6 +199,17 @@
         // optional
     }
 
+    /**
+     * Implement this method to be notified when the
+     * {@link #getCurrentInterruptionFilter() interruption filter} changed.
+     *
+     * @param interruptionFilter The current
+     *     {@link #getCurrentInterruptionFilter() interruption filter}.
+     */
+    public void onInterruptionFilterChanged(int interruptionFilter) {
+        // optional
+    }
+
     private final INotificationManager getNotificationInterface() {
         if (mNoMan == null) {
             mNoMan = INotificationManager.Stub.asInterface(
@@ -345,15 +358,42 @@
      * shared across all listeners or a feature the notification host does not support or refuses
      * to grant.
      *
-     * @return One or more of the HINT_ constants.
+     * @return Zero or more of the HINT_ constants.
      */
     public final int getCurrentListenerHints() {
-        if (!isBound()) return HINTS_NONE;
+        if (!isBound()) return 0;
         try {
             return getNotificationInterface().getHintsFromListener(mWrapper);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
-            return HINTS_NONE;
+            return 0;
+        }
+    }
+
+    /**
+     * Gets the current notification interruption filter active on the host.
+     *
+     * <p>
+     * The interruption filter defines which notifications are allowed to interrupt the user
+     * (e.g. via sound &amp; vibration) and is applied globally. Listeners can find out whether
+     * a specific notification matched the interruption filter via
+     * {@link Ranking#matchesInterruptionFilter()}.
+     * <p>
+     * The current filter may differ from the previously requested filter if the notification host
+     * does not support or refuses to apply the requested filter, or if another component changed
+     * the filter in the meantime.
+     * <p>
+     * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
+     *
+     * @return One of the INTERRUPTION_FILTER_ constants, or 0 on errors.
+     */
+    public final int getCurrentInterruptionFilter() {
+        if (!isBound()) return 0;
+        try {
+            return getNotificationInterface().getHintsFromListener(mWrapper);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+            return 0;
         }
     }
 
@@ -361,7 +401,7 @@
      * Sets the desired {@link #getCurrentListenerHints() listener hints}.
      *
      * <p>
-     * This is merely a request, the host may or not choose to take action depending
+     * This is merely a request, the host may or may not choose to take action depending
      * on other listener requests or other global state.
      * <p>
      * Listen for updates using {@link #onListenerHintsChanged(int)}.
@@ -378,6 +418,27 @@
     }
 
     /**
+     * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
+     *
+     * <p>
+     * This is merely a request, the host may or may not choose to apply the requested
+     * interruption filter depending on other listener requests or other global state.
+     * <p>
+     * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
+     *
+     * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
+     */
+    public final void requestInterruptionFilter(int interruptionFilter) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface()
+                    .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
      * Returns current ranking information.
      *
      * <p>
@@ -514,6 +575,15 @@
                 Log.w(TAG, "Error running onListenerHintsChanged", t);
             }
         }
+
+        @Override
+        public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
+            try {
+                NotificationListenerService.this.onInterruptionFilterChanged(interruptionFilter);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onInterruptionFilterChanged", t);
+            }
+        }
     }
 
     private void applyUpdate(NotificationRankingUpdate update) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index aecf488..e82057c 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -262,6 +262,8 @@
             int fit = paraStart;
             float fitWidth = w;
             int fitAscent = 0, fitDescent = 0, fitTop = 0, fitBottom = 0;
+            // same as fitWidth but not including any trailing whitespace
+            float fitWidthGraphing = w;
 
             boolean hasTabOrEmoji = false;
             boolean hasTab = false;
@@ -346,6 +348,9 @@
 
                     if (w <= width || isSpaceOrTab) {
                         fitWidth = w;
+                        if (!isSpaceOrTab) {
+                            fitWidthGraphing = w;
+                        }
                         fit = j + 1;
 
                         if (fmTop < fitTop)
@@ -365,7 +370,7 @@
                                 breakOpp[breakOppIndex] == j - paraStart + 1;
 
                         if (isLineBreak) {
-                            okWidth = w;
+                            okWidth = fitWidthGraphing;
                             ok = j + 1;
 
                             if (fitTop < okTop)
@@ -426,6 +431,7 @@
                         j = here - 1; // restart j-span loop from here, compensating for the j++
                         ok = fit = here;
                         w = 0;
+                        fitWidthGraphing = w;
                         fitAscent = fitDescent = fitTop = fitBottom = 0;
                         okAscent = okDescent = okTop = okBottom = 0;
 
@@ -842,7 +848,7 @@
     void prepare() {
         mMeasured = MeasuredText.obtain();
     }
-    
+
     void finish() {
         mMeasured = MeasuredText.recycle(mMeasured);
     }
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 59ba71f..bd52e71 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1656,7 +1656,7 @@
                 WindowId windowId = sceneRoot.getWindowId();
                 for (int i = numOldAnims - 1; i >= 0; i--) {
                     AnimationInfo info = runningAnimators.valueAt(i);
-                    if (info.view != null && windowId.equals(info.windowId)) {
+                    if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
                         Animator anim = runningAnimators.keyAt(i);
                         anim.pause();
                     }
@@ -1689,7 +1689,7 @@
                 WindowId windowId = sceneRoot.getWindowId();
                 for (int i = numOldAnims - 1; i >= 0; i--) {
                     AnimationInfo info = runningAnimators.valueAt(i);
-                    if (info.view != null && windowId.equals(info.windowId)) {
+                    if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
                         Animator anim = runningAnimators.keyAt(i);
                         anim.resume();
                     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 764eea7..ca08ecc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -277,7 +277,11 @@
                 final int saveCount = canvas.save();
                 canvas.translate(mInsetLeft, mInsetTop);
                 callbacks.onHardwarePreDraw(canvas);
+
+                canvas.insertReorderBarrier();
                 canvas.drawRenderNode(view.getDisplayList());
+                canvas.insertInorderBarrier();
+
                 callbacks.onHardwarePostDraw(canvas);
                 canvas.restoreToCount(saveCount);
                 mRootNodeNeedsUpdate = false;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2c7ea3e..9b6f200 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1304,12 +1304,22 @@
     public abstract int getVolumeControlStream();
 
     /**
+     * Sets a {@link MediaController} to send media keys and volume changes to.
+     * If set, this should be preferred for all media keys and volume requests
+     * sent to this window.
+     *
+     * @param controller The controller for the session which should receive
+     *            media keys and volume changes.
      * @see android.app.Activity#setMediaController(android.media.session.MediaController)
      */
     public void setMediaController(MediaController controller) {
     }
 
     /**
+     * Gets the {@link MediaController} that was previously set.
+     *
+     * @return The controller which should receive events.
+     * @see #setMediaController(android.media.session.MediaController)
      * @see android.app.Activity#getMediaController()
      */
     public MediaController getMediaController() {
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 7123b9a..ef8c006 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseBooleanArray;
@@ -645,6 +647,23 @@
             super.onInitializeAccessibilityNodeInfo(info);
             info.setCanOpenPopup(true);
         }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            super.onLayout(changed, left, top, right, bottom);
+
+            // Set up the hotspot bounds to be centered on the image.
+            final Drawable d = getDrawable();
+            final Drawable bg = getBackground();
+            if (d != null && bg != null) {
+                final Rect bounds = d.getBounds();
+                final int height = bottom - top;
+                final int offset = (height - bounds.width()) / 2;
+                final int hotspotLeft = bounds.left - offset;
+                final int hotspotRight = bounds.right + offset;
+                bg.setHotspotBounds(hotspotLeft, 0, hotspotRight, height);
+            }
+        }
     }
 
     private class OverflowPopup extends MenuPopupHelper {
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index add0de3..3b16aba 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -84,8 +84,8 @@
  * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor
  * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
  * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
- * @attr ref android.R.styleable#AutoCompleteTextView_dropDownVerticalOffset
- * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHorizontalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
  */
 public class AutoCompleteTextView extends EditText implements Filter.FilterListener {
     static final boolean DEBUG = false;
@@ -129,7 +129,7 @@
     }
 
     public AutoCompleteTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, com.android.internal.R.attr.autoCompleteTextViewStyle);
+        this(context, attrs, R.attr.autoCompleteTextViewStyle);
     }
 
     public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -140,23 +140,17 @@
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mPopup = new ListPopupWindow(context, attrs,
-                com.android.internal.R.attr.autoCompleteTextViewStyle);
+        mPopup = new ListPopupWindow(context, attrs, defStyleAttr, defStyleRes);
         mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
         mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
 
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                com.android.internal.R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.AutoCompleteTextView, defStyleAttr, defStyleRes);
 
-        mThreshold = a.getInt(
-                R.styleable.AutoCompleteTextView_completionThreshold, 2);
+        mThreshold = a.getInt(R.styleable.AutoCompleteTextView_completionThreshold, 2);
 
         mPopup.setListSelector(a.getDrawable(R.styleable.AutoCompleteTextView_dropDownSelector));
-        mPopup.setVerticalOffset((int)
-                a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f));
-        mPopup.setHorizontalOffset((int)
-                a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f));
-        
+
         // Get the anchor's id now, but the view won't be ready, so wait to actually get the
         // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later.
         // Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return
@@ -166,11 +160,9 @@
         
         // For dropdown width, the developer can specify a specific width, or MATCH_PARENT
         // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view).
-        mPopup.setWidth(a.getLayoutDimension(
-                R.styleable.AutoCompleteTextView_dropDownWidth,
+        mPopup.setWidth(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth,
                 ViewGroup.LayoutParams.WRAP_CONTENT));
-        mPopup.setHeight(a.getLayoutDimension(
-                R.styleable.AutoCompleteTextView_dropDownHeight,
+        mPopup.setHeight(a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight,
                 ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView,
@@ -372,6 +364,8 @@
      * <p>Sets the vertical offset used for the auto-complete drop-down list.</p>
      * 
      * @param offset the vertical offset
+     *
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
      */
     public void setDropDownVerticalOffset(int offset) {
         mPopup.setVerticalOffset(offset);
@@ -381,6 +375,8 @@
      * <p>Gets the vertical offset used for the auto-complete drop-down list.</p>
      * 
      * @return the vertical offset
+     *
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
      */
     public int getDropDownVerticalOffset() {
         return mPopup.getVerticalOffset();
@@ -390,6 +386,8 @@
      * <p>Sets the horizontal offset used for the auto-complete drop-down list.</p>
      * 
      * @param offset the horizontal offset
+     *
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
      */
     public void setDropDownHorizontalOffset(int offset) {
         mPopup.setHorizontalOffset(offset);
@@ -399,6 +397,8 @@
      * <p>Gets the horizontal offset used for the auto-complete drop-down list.</p>
      * 
      * @return the horizontal offset
+     *
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
      */
     public int getDropDownHorizontalOffset() {
         return mPopup.getHorizontalOffset();
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 6a514ba..3c186e3 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.database.DataSetObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -40,6 +41,7 @@
 import android.view.ViewParent;
 import android.view.animation.AccelerateDecelerateInterpolator;
 
+import com.android.internal.R;
 import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
 
 import java.util.Locale;
@@ -208,6 +210,18 @@
      */
     public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mContext = context;
+
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ListPopupWindow,
+                defStyleAttr, defStyleRes);
+        mDropDownHorizontalOffset = a.getDimensionPixelOffset(
+                R.styleable.ListPopupWindow_dropDownHorizontalOffset, 0);
+        mDropDownVerticalOffset = a.getDimensionPixelOffset(
+                R.styleable.ListPopupWindow_dropDownVerticalOffset, 0);
+        if (mDropDownVerticalOffset != 0) {
+            mDropDownVerticalOffsetSet = true;
+        }
+        a.recycle();
+
         mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
         // Set the default layout direction to match the default locale one
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 602f955..56bdb9b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -176,7 +176,7 @@
                 RemoteViewsAdapter adapter;
                 final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                 if ((adapter = mAdapter.get()) != null) {
-                    mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent);
+                    mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
                 } else {
                     Slog.w(TAG, "unbind: adapter was null");
                 }
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 9914800..98d52ff 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -49,14 +49,14 @@
  *
  * <p>See the <a href="{@docRoot}guide/topics/ui/controls/spinner.html">Spinners</a> guide.</p>
  *
- * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
  * @attr ref android.R.styleable#Spinner_dropDownSelector
- * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
  * @attr ref android.R.styleable#Spinner_dropDownWidth
  * @attr ref android.R.styleable#Spinner_gravity
  * @attr ref android.R.styleable#Spinner_popupBackground
  * @attr ref android.R.styleable#Spinner_prompt
  * @attr ref android.R.styleable#Spinner_spinnerMode
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
+ * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
  */
 @Widget
 public class Spinner extends AbsSpinner implements OnClickListener {
@@ -209,17 +209,6 @@
                     ViewGroup.LayoutParams.WRAP_CONTENT);
             popup.setBackgroundDrawable(a.getDrawable(
                     com.android.internal.R.styleable.Spinner_popupBackground));
-            final int verticalOffset = a.getDimensionPixelOffset(
-                    com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0);
-            if (verticalOffset != 0) {
-                popup.setVerticalOffset(verticalOffset);
-            }
-
-            final int horizontalOffset = a.getDimensionPixelOffset(
-                    com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0);
-            if (horizontalOffset != 0) {
-                popup.setHorizontalOffset(horizontalOffset);
-            }
 
             mPopup = popup;
             mForwardingListener = new ForwardingListener(this) {
@@ -303,7 +292,7 @@
      *
      * @param pixels Vertical offset in pixels
      *
-     * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
      */
     public void setDropDownVerticalOffset(int pixels) {
         mPopup.setVerticalOffset(pixels);
@@ -315,7 +304,7 @@
      *
      * @return Vertical offset in pixels
      *
-     * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownVerticalOffset
      */
     public int getDropDownVerticalOffset() {
         return mPopup.getVerticalOffset();
@@ -327,7 +316,7 @@
      *
      * @param pixels Horizontal offset in pixels
      *
-     * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
      */
     public void setDropDownHorizontalOffset(int pixels) {
         mPopup.setHorizontalOffset(pixels);
@@ -339,7 +328,7 @@
      *
      * @return Horizontal offset in pixels
      *
-     * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
+     * @attr ref android.R.styleable#ListPopupWindow_dropDownHorizontalOffset
      */
     public int getDropDownHorizontalOffset() {
         return mPopup.getHorizontalOffset();
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index ee406bd..eae4427 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -993,6 +993,6 @@
         } catch (RemoteException e) {
             Log.w(TAG, "RemoteException:", e);
         }
-        return null;
+        return new BatteryStatsImpl();
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 299b0e6..69cdbff 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2487,6 +2487,16 @@
         addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
     }
 
+    public void noteCurrentTimeChangedLocked() {
+        final long currentTime = System.currentTimeMillis();
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        recordCurrentTimeChangeLocked(currentTime, elapsedRealtime, uptime);
+        if (isStartClockTimeValid()) {
+            mStartClockTime = currentTime;
+        }
+    }
+
     public void noteProcessStartLocked(String name, int uid) {
         uid = mapUid(uid);
         if (isOnBattery()) {
@@ -4060,7 +4070,20 @@
         }
     }
 
+    boolean isStartClockTimeValid() {
+        return mStartClockTime > 365*24*60*60*1000L;
+    }
+
     @Override public long getStartClockTime() {
+        if (!isStartClockTimeValid()) {
+            // If the last clock time we got was very small, then we hadn't had a real
+            // time yet, so try to get it again.
+            mStartClockTime = System.currentTimeMillis();
+            if (isStartClockTimeValid()) {
+                recordCurrentTimeChangeLocked(mStartClockTime, SystemClock.elapsedRealtime(),
+                        SystemClock.uptimeMillis());
+            }
+        }
         return mStartClockTime;
     }
 
@@ -6799,6 +6822,16 @@
         }
     }
 
+    private void recordCurrentTimeChangeLocked(final long currentTime, final long elapsedRealtimeMs,
+            final long uptimeMs) {
+        if (mRecordingHistory) {
+            mHistoryCur.currentTime = currentTime;
+            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+                    mHistoryCur);
+            mHistoryCur.currentTime = 0;
+        }
+    }
+
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
@@ -8004,6 +8037,10 @@
     public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
         pullPendingStateUpdatesLocked();
 
+        // Pull the clock time.  This may update the time and make a new history entry
+        // if we had originally pulled a time before the RTC was set.
+        long startClockTime = getStartClockTime();
+
         final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
         final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
 
@@ -8014,7 +8051,7 @@
         out.writeInt(mStartCount);
         out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
-        out.writeLong(mStartClockTime);
+        out.writeLong(startClockTime);
         out.writeString(mStartPlatformVersion);
         out.writeString(mEndPlatformVersion);
         mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
@@ -8453,6 +8490,10 @@
         // Need to update with current kernel wake lock counts.
         pullPendingStateUpdatesLocked();
 
+        // Pull the clock time.  This may update the time and make a new history entry
+        // if we had originally pulled a time before the RTC was set.
+        long startClockTime = getStartClockTime();
+
         final long uSecUptime = SystemClock.uptimeMillis() * 1000;
         final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryRealtime = mOnBatteryTimeBase.getRealtime(uSecRealtime);
@@ -8463,7 +8504,7 @@
         writeHistory(out, true, false);
 
         out.writeInt(mStartCount);
-        out.writeLong(mStartClockTime);
+        out.writeLong(startClockTime);
         out.writeString(mStartPlatformVersion);
         out.writeString(mEndPlatformVersion);
         out.writeLong(mUptime);
@@ -8588,6 +8629,10 @@
     public void prepareForDumpLocked() {
         // Need to retrieve current kernel wake lock stats before printing.
         pullPendingStateUpdatesLocked();
+
+        // Pull the clock time.  This may update the time and make a new history entry
+        // if we had originally pulled a time before the RTC was set.
+        getStartClockTime();
     }
 
     public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
index 016f0ff..08e1060 100644
--- a/core/res/res/drawable/btn_borderless_material.xml
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -14,10 +14,8 @@
      limitations under the License.
 -->
 
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-       android:inset="@dimen/control_inset_material">
-    <ripple android:color="?attr/colorControlHighlight">
-        <item android:id="@id/mask"
-              android:drawable="@drawable/btn_default_mtrl_shape" />
-    </ripple>
-</inset>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorControlHighlight">
+    <item android:id="@id/mask"
+          android:drawable="@drawable/btn_default_mtrl_shape" />
+</ripple>
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
index d00a348..ed2b5aa 100644
--- a/core/res/res/drawable/btn_default_material.xml
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -14,9 +14,7 @@
      limitations under the License.
 -->
 
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-       android:inset="@dimen/control_inset_material">
-    <ripple android:color="?attr/colorControlHighlight">
-        <item android:drawable="@drawable/btn_default_mtrl_shape" />
-    </ripple>
-</inset>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorControlHighlight">
+    <item android:drawable="@drawable/btn_default_mtrl_shape" />
+</ripple>
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
index 9235c76..6d0f7f8 100644
--- a/core/res/res/drawable/btn_default_mtrl_shape.xml
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -15,12 +15,18 @@
 -->
 
 <!-- Used as the canonical button shape. -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <corners android:radius="@dimen/control_corner_material" />
-    <solid android:color="?attr/colorButtonNormal" />
-    <padding android:top="@dimen/control_padding_material"
-             android:bottom="@dimen/control_padding_material"
-             android:left="@dimen/control_padding_material"
-             android:right="@dimen/control_padding_material" />
-</shape>
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="@dimen/button_inset_horizontal_material"
+       android:insetTop="@dimen/button_inset_vertical_material"
+       android:insetRight="@dimen/button_inset_horizontal_material"
+       android:insetBottom="@dimen/button_inset_vertical_material">
+    <shape android:shape="rectangle">
+        <corners android:radius="@dimen/control_corner_material" />
+        <solid android:color="?attr/colorButtonNormal" />
+        <padding android:left="@dimen/button_padding_horizontal_material"
+                 android:top="@dimen/button_padding_vertical_material"
+                 android:right="@dimen/button_padding_horizontal_material"
+                 android:bottom="@dimen/button_padding_vertical_material" />
+    </shape>
+</inset>
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
index 9726782..f91d4cc 100644
--- a/core/res/res/drawable/btn_toggle_material.xml
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -15,7 +15,10 @@
 -->
 
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-       android:inset="@dimen/control_inset_material">
+       android:insetLeft="@dimen/button_inset_horizontal_material"
+       android:insetTop="@dimen/button_inset_vertical_material"
+       android:insetRight="@dimen/button_inset_horizontal_material"
+       android:insetBottom="@dimen/button_inset_vertical_material">
     <layer-list android:paddingMode="stack">
         <item>
             <ripple android:color="?attr/colorControlHighlight">
@@ -25,10 +28,10 @@
                         <corners android:topLeftRadius="@dimen/control_corner_material"
                                  android:topRightRadius="@dimen/control_corner_material"/>
                         <solid android:color="?attr/colorButtonNormal" />
-                        <padding android:top="@dimen/control_padding_material"
-                                 android:bottom="@dimen/control_padding_material"
-                                 android:left="@dimen/control_padding_material"
-                                 android:right="@dimen/control_padding_material" />
+                        <padding android:left="@dimen/button_padding_horizontal_material"
+                                 android:top="@dimen/button_padding_vertical_material"
+                                 android:right="@dimen/button_padding_horizontal_material"
+                                 android:bottom="@dimen/button_padding_vertical_material" />
                     </shape>
                 </item>
             </ripple>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cf1794b..9edcca2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4172,10 +4172,6 @@
         <attr name="completionThreshold" format="integer" min="1" />
         <!-- Selector in a drop down list. -->
         <attr name="dropDownSelector" format="reference|color" />
-        <!-- Amount of pixels by which the drop down should be offset vertically. -->
-        <attr name="dropDownVerticalOffset" format="dimension" />
-        <!-- Amount of pixels by which the drop down should be offset horizontally. -->
-        <attr name="dropDownHorizontalOffset" format="dimension" />
         <!-- View to anchor the auto-complete dropdown to. If not specified, the text view itself
              is used. -->
         <attr name="dropDownAnchor" format="reference" />
@@ -4223,6 +4219,12 @@
         <!-- Whether the popup window should overlap its anchor view. -->
         <attr name="overlapAnchor" format="boolean" />
     </declare-styleable>
+    <declare-styleable name="ListPopupWindow">
+        <!-- Amount of pixels by which the drop down should be offset vertically. -->
+        <attr name="dropDownVerticalOffset" format="dimension" />
+        <!-- Amount of pixels by which the drop down should be offset horizontally. -->
+        <attr name="dropDownHorizontalOffset" format="dimension" />
+    </declare-styleable>
     <declare-styleable name="ViewAnimator">
         <!-- Identifier for the animation to use when a view is shown. -->
         <attr name="inAnimation" format="reference" />
@@ -4281,12 +4283,6 @@
         <attr name="popupBackground" />
         <!-- Window elevation to use for the dropdown in spinnerMode="dropdown". -->
         <attr name="popupElevation" />
-        <!-- Vertical offset from the spinner widget for positioning the dropdown in
-             spinnerMode="dropdown". -->
-        <attr name="dropDownVerticalOffset" />
-        <!-- Horizontal offset from the spinner widget for positioning the dropdown
-             in spinnerMode="dropdown". -->
-        <attr name="dropDownHorizontalOffset" />
         <!-- Width of the dropdown in spinnerMode="dropdown". -->
         <attr name="dropDownWidth" />
         <!-- Reference to a layout to use for displaying a prompt in the dropdown for
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 972ae5e..f5c9299 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -33,7 +33,6 @@
 
     <dimen name="action_button_min_width_material">48dp</dimen>
     <dimen name="action_button_min_height_material">48dp</dimen>
-    <dimen name="action_overflow_min_width_material">36dp</dimen>
 
     <dimen name="text_size_display_4_material">112sp</dimen>
     <dimen name="text_size_display_3_material">56sp</dimen>
@@ -64,6 +63,13 @@
     <dimen name="button_elevation_material">1dp</dimen>
     <!-- Z translation to apply when button is pressed -->
     <dimen name="button_pressed_z_material">2dp</dimen>
+    <!-- Default insets (outer padding) around buttons -->
+    <dimen name="button_inset_vertical_material">6dp</dimen>
+    <dimen name="button_inset_horizontal_material">@dimen/control_inset_material</dimen>
+    <!-- Default inner padding within buttons -->
+    <dimen name="button_padding_vertical_material">@dimen/control_padding_material</dimen>
+    <dimen name="button_padding_horizontal_material">8dp</dimen>
+
     <!-- Default insets (outer padding) around controls -->
     <dimen name="control_inset_material">4dp</dimen>
     <!-- Default inner padding within controls -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d6224da..f1ec5d2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3642,9 +3642,9 @@
     <!-- The message text for the SMS short code confirmation dialog. [CHAR LIMIT=NONE] -->
     <string name="sms_short_code_confirm_message">&lt;b><xliff:g id="app_name">%1$s</xliff:g>&lt;/b> would like to send a message to &lt;b><xliff:g id="dest_address">%2$s</xliff:g>&lt;/b>.</string>
     <!-- Message details for the SMS short code confirmation dialog (possible premium short code). [CHAR LIMIT=NONE] -->
-    <string name="sms_short_code_details">This <font fgcolor="#ffffb060">may cause charges</font> on your mobile account.</string>
+    <string name="sms_short_code_details">This <b>may cause charges</b> on your mobile account.</string>
     <!-- Message details for the SMS short code confirmation dialog (premium short code). [CHAR LIMIT=NONE] -->
-    <string name="sms_premium_short_code_details"><font fgcolor="#ffffb060">This will cause charges on your mobile account.</font></string>
+    <string name="sms_premium_short_code_details"><b>This will cause charges on your mobile account.</b></string>
     <!-- Text of the approval button for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
     <string name="sms_short_code_confirm_allow">Send</string>
     <!-- Text of the cancel button for the SMS short code confirmation dialog. [CHAR LIMIT=30] -->
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2bb52c9..e783cd6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -306,7 +306,7 @@
     </style>
 
     <style name="TextAppearance.Material.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Menu" />
-    <style name="TextAppearance.Material.Widget.TextView.SpinnerItem" />
+    <style name="TextAppearance.Material.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Menu" />
 
     <style name="TextAppearance.Material.Widget.DropDownItem" parent="TextAppearance.Material.Menu">
         <item name="textColor">?attr/textColorPrimaryDisableOnly</item>
@@ -744,6 +744,7 @@
         <item name="popupElevation">@dimen/floating_window_z</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
+        <item name="overlapAnchor">true</item>
         <item name="dropDownWidth">wrap_content</item>
         <item name="popupPromptView">@layout/simple_dropdown_hint</item>
         <item name="gravity">start|center_vertical</item>
@@ -827,6 +828,7 @@
 
     <style name="Widget.Material.PopupMenu.Overflow">
         <item name="overlapAnchor">true</item>
+        <item name="dropDownHorizontalOffset">-4dip</item>
     </style>
 
     <style name="Widget.Material.ActionButton" parent="Widget.ActionButton">
@@ -835,6 +837,8 @@
         <item name="gravity">center</item>
         <item name="scaleType">center</item>
         <item name="maxLines">2</item>
+        <item name="paddingStart">0dp</item>
+        <item name="paddingEnd">0dp</item>
     </style>
 
     <style name="Widget.Material.ActionButton.CloseMode">
@@ -845,8 +849,9 @@
         <item name="src">@drawable/ic_menu_moreoverflow_material</item>
         <item name="background">?attr/actionBarItemBackground</item>
         <item name="contentDescription">@string/action_menu_overflow_description</item>
-        <item name="minWidth">@dimen/action_overflow_min_width_material</item>
+        <item name="minWidth">@dimen/action_button_min_width_material</item>
         <item name="minHeight">@dimen/action_button_min_height_material</item>
+        <item name="paddingEnd">12dp</item>
         <item name="scaleType">center</item>
     </style>
 
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 11568d2..c65efe4 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1557,7 +1557,7 @@
     };
     const char16_t* valueToString(const Res_value* value, size_t stringBlock,
                                   char16_t tmpBuffer[TMP_BUFFER_SIZE],
-                                  size_t* outLen);
+                                  size_t* outLen) const;
 
     struct bag_entry {
         ssize_t stringBlock;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 3f014ef..90879dd 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3728,7 +3728,7 @@
 
 const char16_t* ResTable::valueToString(
     const Res_value* value, size_t stringBlock,
-    char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
+    char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen) const
 {
     if (!value) {
         return NULL;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 72786eb0..6a92a6e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -34,6 +34,7 @@
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
+#include "renderthread/CanvasContext.h"
 
 namespace android {
 namespace uirenderer {
@@ -208,6 +209,13 @@
     if (info.renderer && mLayer->deferredUpdateScheduled) {
         info.renderer->pushLayerUpdate(mLayer);
     }
+
+    if (CC_UNLIKELY(info.canvasContext)) {
+        // If canvasContext is not null that means there are prefetched layers
+        // that need to be accounted for. That might be us, so tell CanvasContext
+        // that this layer is in the tree and should not be destroyed.
+        info.canvasContext->markLayerInUse(this);
+    }
 }
 
 void RenderNode::prepareTreeImpl(TreeInfo& info) {
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index e78d8bd..ae6ea94 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -26,6 +26,10 @@
 namespace android {
 namespace uirenderer {
 
+namespace renderthread {
+class CanvasContext;
+}
+
 class OpenGLRenderer;
 class RenderState;
 
@@ -59,6 +63,7 @@
         , renderState(renderState)
         , renderer(NULL)
         , errorHandler(NULL)
+        , canvasContext(NULL)
     {}
 
     explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
@@ -69,6 +74,7 @@
         , renderState(clone.renderState)
         , renderer(clone.renderer)
         , errorHandler(clone.errorHandler)
+        , canvasContext(clone.canvasContext)
     {}
 
     const TraversalMode mode;
@@ -89,6 +95,8 @@
     // layer updates or similar. May be NULL.
     OpenGLRenderer* renderer;
     ErrorHandler* errorHandler;
+    // TODO: Remove this? May be NULL
+    renderthread::CanvasContext* canvasContext;
 
     struct Out {
         Out()
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d9fa0bc..ecfedf6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,6 +16,7 @@
 
 #include "CanvasContext.h"
 
+#include <algorithm>
 #include <private/hwui/DrawGlInfo.h>
 #include <strings.h>
 
@@ -53,6 +54,7 @@
     destroyCanvasAndSurface();
     mRenderThread.removeFrameCallback(this);
     delete mAnimationContext;
+    freePrefetechedLayers();
 }
 
 void CanvasContext::destroyCanvasAndSurface() {
@@ -142,10 +144,17 @@
 
     info.damageAccumulator = &mDamageAccumulator;
     info.renderer = mCanvas;
+    if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
+        info.canvasContext = this;
+    }
     mAnimationContext->startFrame();
     mRootRenderNode->prepareTree(info);
     mAnimationContext->runRemainingAnimations(info);
 
+    if (info.canvasContext) {
+        freePrefetechedLayers();
+    }
+
     int runningBehind = 0;
     // TODO: This query is moderately expensive, investigate adding some sort
     // of fast-path based off when we last called eglSwapBuffers() as well as
@@ -249,6 +258,26 @@
     thread.renderState().invokeFunctor(functor, mode, NULL);
 }
 
+void CanvasContext::markLayerInUse(RenderNode* node) {
+    if (mPrefetechedLayers.erase(node)) {
+        node->decStrong(0);
+    }
+}
+
+static void destroyPrefetechedNode(RenderNode* node) {
+    ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
+    node->destroyHardwareResources();
+    node->decStrong(0);
+}
+
+void CanvasContext::freePrefetechedLayers() {
+    if (mPrefetechedLayers.size()) {
+        requireGlContext();
+        std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
+        mPrefetechedLayers.clear();
+    }
+}
+
 void CanvasContext::buildLayer(RenderNode* node) {
     ATRACE_CALL();
     if (!mEglManager.hasEglContext() || !mCanvas) {
@@ -270,6 +299,9 @@
     node->setPropertyFieldsDirty(RenderNode::GENERIC);
 
     mCanvas->flushLayerUpdates();
+
+    node->incStrong(0);
+    mPrefetechedLayers.insert(node);
 }
 
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 749da1b..7c27190 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -17,6 +17,8 @@
 #ifndef CANVASCONTEXT_H_
 #define CANVASCONTEXT_H_
 
+#include <set>
+
 #include <cutils/compiler.h>
 #include <EGL/egl.h>
 #include <SkBitmap.h>
@@ -71,6 +73,7 @@
 
     void buildLayer(RenderNode* node);
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+    void markLayerInUse(RenderNode* node);
 
     void destroyHardwareResources();
     static void trimMemory(RenderThread& thread, int level);
@@ -99,6 +102,8 @@
 
     void requireGlContext();
 
+    void freePrefetechedLayers();
+
     RenderThread& mRenderThread;
     EglManager& mEglManager;
     sp<ANativeWindow> mNativeWindow;
@@ -114,6 +119,8 @@
     const sp<RenderNode> mRootRenderNode;
 
     DrawProfiler mProfiler;
+
+    std::set<RenderNode*> mPrefetechedLayers;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 37f8e60..e030cdb 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EglContext"
-
 #include "EglManager.h"
 
 #include <cutils/log.h>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e69c456..e2770b4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1688,11 +1688,19 @@
 
     /**
      * Return a new audio session identifier not associated with any player or effect.
-     * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect}
-     * objects or specify a session for speech synthesis in
-     * {@link android.speech.tts.TextToSpeech.Engine}.
+     * An audio session identifier is a system wide unique identifier for a set of audio streams
+     * (one or more mixed together).
+     * <p>The primary use of the audio session ID is to associate audio effects to audio players,
+     * such as {@link MediaPlayer} or {@link AudioTrack}: all audio effects sharing the same audio
+     * session ID will be applied to the mixed audio content of the players that share the same
+     * audio session.
+     * <p>This method can for instance be used when creating one of the
+     * {@link android.media.audiofx.AudioEffect} objects to define the audio session of the effect,
+     * or to specify a session for a speech synthesis utterance
+     * in {@link android.speech.tts.TextToSpeech.Engine}.
      * @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the
-     *   system failed to generate a new session.
+     *   system failed to generate a new session, a condition in which audio playback or recording
+     *   will subsequently fail as well.
      */
     public int generateAudioSessionId() {
         int session = AudioSystem.newAudioSessionId();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 3fea688..d002924 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -200,6 +200,7 @@
     private static final int MSG_UNLOAD_SOUND_EFFECTS = 20;
     private static final int MSG_SYSTEM_READY = 21;
     private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = 22;
+    private static final int MSG_PERSIST_MICROPHONE_MUTE = 23;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -873,6 +874,10 @@
         AudioSystem.setMasterMute(masterMute);
         broadcastMasterMuteStatus(masterMute);
 
+        boolean microphoneMute =
+                System.getIntForUser(cr, System.MICROPHONE_MUTE, 0, UserHandle.USER_CURRENT) == 1;
+        AudioSystem.muteMicrophone(microphoneMute);
+
         // Each stream will read its own persisted settings
 
         // Broadcast the sticky intent
@@ -1447,17 +1452,15 @@
         if (mUseFixedVolume) {
             return;
         }
-
         if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
             return;
         }
-
         if (state != AudioSystem.getMasterMute()) {
             AudioSystem.setMasterMute(state);
             // Post a persist master volume msg
             sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
-                    : 0, 0, null, PERSIST_DELAY);
+                    : 0, UserHandle.getCallingUserId(), null, PERSIST_DELAY);
             sendMasterMuteUpdate(state, flags);
         }
     }
@@ -1563,6 +1566,9 @@
         }
 
         AudioSystem.muteMicrophone(on);
+        // Post a persist microphone msg.
+        sendMsg(mAudioHandler, MSG_PERSIST_MICROPHONE_MUTE, SENDMSG_REPLACE, on ? 1
+                : 0, UserHandle.getCallingUserId(), null, PERSIST_DELAY);
     }
 
     /** @see AudioManager#getRingerMode() */
@@ -3834,7 +3840,6 @@
 
         @Override
         public void handleMessage(Message msg) {
-
             switch (msg.what) {
 
                 case MSG_SET_DEVICE_VOLUME:
@@ -3866,7 +3871,7 @@
                     Settings.System.putIntForUser(mContentResolver,
                                                  Settings.System.VOLUME_MASTER_MUTE,
                                                  msg.arg1,
-                                                 UserHandle.USER_CURRENT);
+                                                 msg.arg2);
                     break;
 
                 case MSG_PERSIST_RINGER_MODE:
@@ -4061,6 +4066,12 @@
                             Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, musicActiveMs,
                             UserHandle.USER_CURRENT);
                     break;
+                case MSG_PERSIST_MICROPHONE_MUTE:
+                    Settings.System.putIntForUser(mContentResolver,
+                                                 Settings.System.MICROPHONE_MUTE,
+                                                 msg.arg1,
+                                                 msg.arg2);
+                    break;
             }
         }
     }
diff --git a/media/java/android/media/MediaDescription.aidl b/media/java/android/media/MediaDescription.aidl
new file mode 100644
index 0000000..6f934f7
--- /dev/null
+++ b/media/java/android/media/MediaDescription.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable MediaDescription;
diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java
new file mode 100644
index 0000000..4399c0d
--- /dev/null
+++ b/media/java/android/media/MediaDescription.java
@@ -0,0 +1,276 @@
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Size;
+
+/**
+ * A simple set of metadata for a media item suitable for display. This can be
+ * created using the Builder or retrieved from existing metadata using
+ * {@link MediaMetadata#getDescription()}.
+ */
+public class MediaDescription implements Parcelable {
+    /**
+     * A unique persistent id for the content or null.
+     */
+    private final String mMediaId;
+    /**
+     * A primary title suitable for display or null.
+     */
+    private final CharSequence mTitle;
+    /**
+     * A subtitle suitable for display or null.
+     */
+    private final CharSequence mSubtitle;
+    /**
+     * A description suitable for display or null.
+     */
+    private final CharSequence mDescription;
+    /**
+     * A bitmap icon suitable for display or null.
+     */
+    private final Bitmap mIcon;
+    /**
+     * A Uri for an icon suitable for display or null.
+     */
+    private final Uri mIconUri;
+    /**
+     * Extras for opaque use by apps/system.
+     */
+    private final Bundle mExtras;
+
+    private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle,
+            CharSequence description, Bitmap icon, Uri iconUri, Bundle extras) {
+        mMediaId = mediaId;
+        mTitle = title;
+        mSubtitle = subtitle;
+        mDescription = description;
+        mIcon = icon;
+        mIconUri = iconUri;
+        mExtras = extras;
+    }
+
+    private MediaDescription(Parcel in) {
+        mMediaId = in.readString();
+        mTitle = in.readCharSequence();
+        mSubtitle = in.readCharSequence();
+        mDescription = in.readCharSequence();
+        mIcon = in.readParcelable(null);
+        mIconUri = in.readParcelable(null);
+        mExtras = in.readBundle();
+    }
+
+    /**
+     * Returns the media id or null. See
+     * {@link MediaMetadata#METADATA_KEY_MEDIA_ID}.
+     */
+    public @Nullable String getMediaId() {
+        return mMediaId;
+    }
+
+    /**
+     * Returns a title suitable for display or null.
+     *
+     * @return A title or null.
+     */
+    public @Nullable CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Returns a subtitle suitable for display or null.
+     *
+     * @return A subtitle or null.
+     */
+    public @Nullable CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Returns a description suitable for display or null.
+     *
+     * @return A description or null.
+     */
+    public @Nullable CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Returns a bitmap icon suitable for display or null.
+     *
+     * @return An icon or null.
+     */
+    public @Nullable Bitmap getIconBitmap() {
+        return mIcon;
+    }
+
+    /**
+     * Returns a Uri for an icon suitable for display or null.
+     *
+     * @return An icon uri or null.
+     */
+    public @Nullable Uri getIconUri() {
+        return mIconUri;
+    }
+
+    /**
+     * Returns any extras that were added to the description.
+     *
+     * @return A bundle of extras or null.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mMediaId);
+        dest.writeCharSequence(mTitle);
+        dest.writeCharSequence(mSubtitle);
+        dest.writeCharSequence(mDescription);
+        dest.writeParcelable(mIcon, flags);
+        dest.writeParcelable(mIconUri, flags);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public String toString() {
+        return mTitle + ", " + mSubtitle + ", " + mDescription;
+    }
+
+    public static final Parcelable.Creator<MediaDescription> CREATOR =
+            new Parcelable.Creator<MediaDescription>() {
+                @Override
+                public MediaDescription createFromParcel(Parcel in) {
+                    return new MediaDescription(in);
+                }
+
+                @Override
+                public MediaDescription[] newArray(int size) {
+                    return new MediaDescription[size];
+                }
+            };
+
+    /**
+     * Builder for {@link MediaDescription} objects.
+     */
+    public static class Builder {
+        private String mMediaId;
+        private CharSequence mTitle;
+        private CharSequence mSubtitle;
+        private CharSequence mDescription;
+        private Bitmap mIcon;
+        private Uri mIconUri;
+        private Bundle mExtras;
+
+        /**
+         * Creates an initially empty builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the media id.
+         *
+         * @param mediaId The unique id for the item or null.
+         * @return this
+         */
+        public Builder setMediaId(@Nullable String mediaId) {
+            mMediaId = mediaId;
+            return this;
+        }
+
+        /**
+         * Sets the title.
+         *
+         * @param title A title suitable for display to the user or null.
+         * @return this
+         */
+        public Builder setTitle(@Nullable CharSequence title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the subtitle.
+         *
+         * @param subtitle A subtitle suitable for display to the user or null.
+         * @return this
+         */
+        public Builder setSubtitle(@Nullable CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Sets the description.
+         *
+         * @param description A description suitable for display to the user or
+         *            null.
+         * @return this
+         */
+        public Builder setDescription(@Nullable CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Sets the icon.
+         *
+         * @param icon A {@link Bitmap} icon suitable for display to the user or
+         *            null.
+         * @return this
+         */
+        public Builder setIconBitmap(@Nullable Bitmap icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the icon uri.
+         *
+         * @param iconUri A {@link Uri} for an icon suitable for display to the
+         *            user or null.
+         * @return this
+         */
+        public Builder setIconUri(@Nullable Uri iconUri) {
+            mIconUri = iconUri;
+            return this;
+        }
+
+        /**
+         * Sets a bundle of extras.
+         *
+         * @param extras The extras to include with this description or null.
+         * @return this
+         */
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        public MediaDescription build() {
+            return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri,
+                    mExtras);
+        }
+    }
+}
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 74f7a96..b4e6033 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -17,15 +17,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.text.format.Time;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Size;
 import android.util.SparseArray;
 
 import java.util.Set;
@@ -119,7 +126,9 @@
     public static final String METADATA_KEY_ART = "android.media.metadata.ART";
 
     /**
-     * The artwork for the media as a Uri.
+     * The artwork for the media as a Uri formatted String. The artwork can be
+     * loaded using a combination of {@link ContentResolver#openInputStream} and
+     * {@link BitmapFactory#decodeStream}.
      */
     public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
 
@@ -130,7 +139,10 @@
     public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
 
     /**
-     * The artwork for the album of the media's original source as a Uri.
+     * The artwork for the album of the media's original source as a Uri
+     * formatted String. The artwork can be loaded using a combination of
+     * {@link ContentResolver#openInputStream} and
+     * {@link BitmapFactory#decodeStream}.
      */
     public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
 
@@ -181,13 +193,26 @@
             = "android.media.metadata.DISPLAY_ICON";
 
     /**
-     * An icon or thumbnail that is suitable for display to the user. When
-     * displaying more information for media described by this metadata the
-     * display description should be preferred to other fields when present.
+     * A Uri formatted String for an icon or thumbnail that is suitable for
+     * display to the user. When displaying more information for media described
+     * by this metadata the display description should be preferred to other
+     * fields when present. The icon can be loaded using a combination of
+     * {@link ContentResolver#openInputStream} and
+     * {@link BitmapFactory#decodeStream}.
      */
     public static final String METADATA_KEY_DISPLAY_ICON_URI
             = "android.media.metadata.DISPLAY_ICON_URI";
 
+    /**
+     * A String key for identifying the content. This value is specific to the
+     * service providing the content. If used, this should be a persistent
+     * unique key for the underlying content. It may be used with
+     * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
+     * to initiate playback when provided by a {@link MediaBrowser} connected to
+     * the same app.
+     */
+    public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+
     private static final String[] PREFERRED_DESCRIPTION_ORDER = {
             METADATA_KEY_TITLE,
             METADATA_KEY_ARTIST,
@@ -277,7 +302,7 @@
     }
 
     private final Bundle mBundle;
-    private Description mDescription;
+    private MediaDescription mDescription;
 
     private MediaMetadata(Bundle bundle) {
         mBundle = new Bundle(bundle);
@@ -406,11 +431,13 @@
      *
      * @return A simple description of this metadata.
      */
-    public @NonNull Description getDescription() {
+    public @NonNull MediaDescription getDescription() {
         if (mDescription != null) {
             return mDescription;
         }
 
+        String mediaId = getString(METADATA_KEY_MEDIA_ID);
+
         CharSequence[] text = new CharSequence[3];
         Bitmap icon = null;
         Uri iconUri = null;
@@ -454,7 +481,15 @@
             }
         }
 
-        mDescription = new Description(text[0], text[1], text[2], icon, iconUri);
+        MediaDescription.Builder bob = new MediaDescription.Builder();
+        bob.setMediaId(mediaId);
+        bob.setTitle(text[0]);
+        bob.setSubtitle(text[1]);
+        bob.setDescription(text[2]);
+        bob.setIconBitmap(icon);
+        bob.setIconUri(iconUri);
+        mDescription = bob.build();
+
         return mDescription;
     }
 
@@ -668,90 +703,4 @@
             return new MediaMetadata(mBundle);
         }
     }
-
-    /**
-     * A simple form of the metadata that can be used for display.
-     */
-    public final class Description {
-        /**
-         * A primary title suitable for display or null.
-         */
-        private final CharSequence mTitle;
-        /**
-         * A subtitle suitable for display or null.
-         */
-        private final CharSequence mSubtitle;
-        /**
-         * A description suitable for display or null.
-         */
-        private final CharSequence mDescription;
-        /**
-         * A bitmap icon suitable for display or null.
-         */
-        private final Bitmap mIcon;
-        /**
-         * A Uri for an icon suitable for display or null.
-         */
-        private final Uri mIconUri;
-
-        /**
-         * Returns the best available title or null.
-         *
-         * @return A title or null.
-         */
-        public @Nullable CharSequence getTitle() {
-            return mTitle;
-        }
-
-        /**
-         * Returns the best available subtitle or null.
-         *
-         * @return A subtitle or null.
-         */
-        public @Nullable CharSequence getSubtitle() {
-            return mSubtitle;
-        }
-
-        /**
-         * Returns the best available description or null.
-         *
-         * @return A description or null.
-         */
-        public @Nullable CharSequence getDescription() {
-            return mDescription;
-        }
-
-        /**
-         * Returns the best available icon or null.
-         *
-         * @return An icon or null.
-         */
-        public @Nullable Bitmap getIcon() {
-            return mIcon;
-        }
-
-        /**
-         * Returns the best available icon Uri or null.
-         *
-         * @return An icon uri or null.
-         */
-        public @Nullable Uri getIconUri() {
-            return mIconUri;
-        }
-
-        private Description(CharSequence title, CharSequence subtitle, CharSequence description,
-                Bitmap icon, Uri iconUri) {
-            mTitle = title;
-            mSubtitle = subtitle;
-            mDescription = description;
-            mIcon = icon;
-            mIconUri = iconUri;
-        }
-
-        @Override
-        public String toString() {
-            return mTitle + ", " + mSubtitle + ", " + mDescription;
-        }
-    }
-
 }
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index a221104..7d075ba 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -115,6 +115,15 @@
     }
 
     /**
+     * Returns the {@link AudioAttributes} used by this object.
+     * @return the {@link AudioAttributes} that were set with
+     *     {@link #setAudioAttributes(AudioAttributes)} or the default attributes if none were set.
+     */
+    public AudioAttributes getAudioAttributes() {
+        return mAudioAttributes;
+    }
+
+    /**
      * Returns a human-presentable title for ringtone. Looks in media
      * content provider. If not in either, uses the filename
      * 
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 1c6d81f..debaf45 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -16,6 +16,7 @@
 
 package android.media.browse;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -23,26 +24,27 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.pm.ParceledListSlice;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
+import android.media.MediaDescription;
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
+import android.service.media.MediaBrowserService;
+import android.service.media.IMediaBrowserService;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
-import java.util.Set;
 
 /**
  * Browses media content offered by a link MediaBrowserService.
@@ -67,8 +69,6 @@
     private final Handler mHandler = new Handler();
     private final ArrayMap<Uri,Subscription> mSubscriptions =
             new ArrayMap<Uri, MediaBrowser.Subscription>();
-    private final SparseArray<IconRequest> mIconRequests =
-            new SparseArray<IconRequest>();
 
     private int mState = CONNECT_STATE_DISCONNECTED;
     private MediaServiceConnection mServiceConnection;
@@ -77,7 +77,6 @@
     private Uri mRootUri;
     private MediaSession.Token mMediaSessionToken;
     private Bundle mExtras;
-    private int mNextSeq;
 
     /**
      * Creates a media browser for the specified media browse service.
@@ -363,49 +362,6 @@
     }
 
     /**
-     * Loads the icon of a media item.
-     *
-     * @param uri The uri of the Icon.
-     * @param width The preferred width of the icon in dp.
-     * @param height The preferred width of the icon in dp.
-     * @param callback The callback to receive the icon.
-     */
-    public void loadIcon(final @NonNull Uri uri, final int width, final int height,
-            final @NonNull IconCallback callback) {
-        if (uri == null) {
-            throw new IllegalArgumentException("Icon uri cannot be null");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("Icon callback cannot be null");
-        }
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                for (int i = 0; i < mIconRequests.size(); i++) {
-                    IconRequest existingRequest = mIconRequests.valueAt(i);
-                    if (existingRequest.isSameRequest(uri, width, height)) {
-                        existingRequest.addCallback(callback);
-                        return;
-                    }
-                }
-                final int seq = mNextSeq++;
-                IconRequest request = new IconRequest(seq, uri, width, height);
-                request.addCallback(callback);
-                mIconRequests.put(seq, request);
-                if (mState == CONNECT_STATE_CONNECTED) {
-                    try {
-                        mServiceBinder.loadIcon(seq, uri, width, height, mServiceCallbacks);
-                    } catch (RemoteException e) {
-                        // Process is crashing.  We will disconnect, and upon reconnect we will
-                        // automatically reload the icons. So nothing to do here.
-                        Log.d(TAG, "loadIcon failed with RemoteException uri=" + uri);
-                    }
-                }
-            }
-        });
-    }
-
-    /**
      * For debugging.
      */
     private static String getStateLabel(int state) {
@@ -461,18 +417,6 @@
                         Log.d(TAG, "addSubscription failed with RemoteException parentUri=" + uri);
                     }
                 }
-
-                for (int i = 0; i < mIconRequests.size(); i++) {
-                    IconRequest request = mIconRequests.valueAt(i);
-                    try {
-                        mServiceBinder.loadIcon(request.mSeq, request.mUri,
-                                request.mWidth, request.mHeight, mServiceCallbacks);
-                    } catch (RemoteException e) {
-                        // Process is crashing.  We will disconnect, and upon reconnect we will
-                        // automatically reload. So nothing to do here.
-                        Log.d(TAG, "loadIcon failed with RemoteException request=" + request);
-                    }
-                }
             }
         });
     }
@@ -515,7 +459,7 @@
                     return;
                 }
 
-                List<MediaBrowserItem> data = list.getList();
+                List<MediaItem> data = list.getList();
                 if (DBG) {
                     Log.d(TAG, "onLoadChildren for " + mServiceComponent + " uri=" + uri);
                 }
@@ -539,32 +483,6 @@
         });
     }
 
-    private final void onLoadIcon(final IMediaBrowserServiceCallbacks callback,
-            final int seqNum, final Bitmap bitmap) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                // Check that there hasn't been a disconnect or a different
-                // ServiceConnection.
-                if (!isCurrent(callback, "onLoadIcon")) {
-                    return;
-                }
-
-                IconRequest request = mIconRequests.get(seqNum);
-                if (request == null) {
-                    Log.d(TAG, "onLoadIcon called for seqNum=" + seqNum + " request="
-                            + request + " but the request is not registered");
-                    return;
-                }
-                mIconRequests.delete(seqNum);
-                for (IconCallback IconCallback : request.getCallbacks()) {
-                    IconCallback.onIconLoaded(request.mUri, bitmap);
-                }
-            }
-        });
-    }
-
-
     /**
      * Return true if {@code callback} is the current ServiceCallbacks.  Also logs if it's not.
      */
@@ -600,6 +518,126 @@
         Log.d(TAG, "  mMediaSessionToken=" + mMediaSessionToken);
     }
 
+    public static class MediaItem implements Parcelable {
+        private final int mFlags;
+        private final MediaDescription mDescription;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
+        public @interface Flags { }
+
+        /**
+         * Flag: Indicates that the item has children of its own.
+         */
+        public static final int FLAG_BROWSABLE = 1 << 0;
+
+        /**
+         * Flag: Indicates that the item is playable.
+         * <p>
+         * The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
+         * to start playing it.
+         * </p>
+         */
+        public static final int FLAG_PLAYABLE = 1 << 1;
+
+        /**
+         * Create a new MediaItem for use in browsing media.
+         *
+         * @param flags The flags for this item.
+         * @param description The description of the media, which must include a
+         *            media id.
+         */
+        public MediaItem(@Flags int flags, @NonNull MediaDescription description) {
+            if (description == null) {
+                throw new IllegalArgumentException("description cannot be null");
+            }
+            if (TextUtils.isEmpty(description.getMediaId())) {
+                throw new IllegalArgumentException("description must have a non-empty media id");
+            }
+            mFlags = flags;
+            mDescription = description;
+        }
+
+        /**
+         * Private constructor.
+         */
+        private MediaItem(Parcel in) {
+            mFlags = in.readInt();
+            mDescription = MediaDescription.CREATOR.createFromParcel(in);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(mFlags);
+            mDescription.writeToParcel(out, flags);
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("MediaItem{");
+            sb.append("mFlags=").append(mFlags);
+            sb.append(", mDescription=").append(mDescription);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        public static final Parcelable.Creator<MediaItem> CREATOR =
+                new Parcelable.Creator<MediaItem>() {
+                    @Override
+                    public MediaItem createFromParcel(Parcel in) {
+                        return new MediaItem(in);
+                    }
+
+                    @Override
+                    public MediaItem[] newArray(int size) {
+                        return new MediaItem[size];
+                    }
+                };
+
+        /**
+         * Gets the flags of the item.
+         */
+        public @Flags int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Returns whether this item is browsable.
+         * @see #FLAG_BROWSABLE
+         */
+        public boolean isBrowsable() {
+            return (mFlags & FLAG_BROWSABLE) != 0;
+        }
+
+        /**
+         * Returns whether this item is playable.
+         * @see #FLAG_PLAYABLE
+         */
+        public boolean isPlayable() {
+            return (mFlags & FLAG_PLAYABLE) != 0;
+        }
+
+        /**
+         * Returns the description of the media.
+         */
+        public @NonNull MediaDescription getDescription() {
+            return mDescription;
+        }
+
+        /**
+         * Returns the media id for this item.
+         */
+        public @NonNull String getMediaId() {
+            return mDescription.getMediaId();
+        }
+    }
+
 
     /**
      * Callbacks for connection related events.
@@ -632,7 +670,7 @@
          * Called when the list of children is loaded or updated.
          */
         public void onChildrenLoaded(@NonNull Uri parentUri,
-                                     @NonNull List<MediaBrowserItem> children) {
+                                     @NonNull List<MediaItem> children) {
         }
 
         /**
@@ -647,87 +685,6 @@
     }
 
     /**
-     * Callbacks for icon loading.
-     */
-    public static abstract class IconCallback {
-        /**
-         * Called when the icon is loaded.
-         */
-        public void onIconLoaded(@NonNull Uri uri, @NonNull Bitmap bitmap) {
-        }
-
-        /**
-         * Called when the Uri doesn’t exist or the bitmap cannot be loaded.
-         */
-        public void onError(@NonNull Uri uri) {
-        }
-    }
-
-    private static class IconRequest {
-        final int mSeq;
-        final Uri mUri;
-        final int mWidth;
-        final int mHeight;
-        final List<IconCallback> mCallbacks;
-
-        /**
-         * Constructs an icon request.
-         * @param seq The unique sequence number assigned to the request by the media browser.
-         * @param uri The Uri for the icon.
-         * @param width The width for the icon.
-         * @param height The height for the icon.
-         */
-        IconRequest(int seq, @NonNull Uri uri, int width, int height) {
-            if (uri == null) {
-                throw new IllegalArgumentException("Icon uri cannot be null");
-            }
-            this.mSeq = seq;
-            this.mUri = uri;
-            this.mWidth = width;
-            this.mHeight = height;
-            mCallbacks = new ArrayList<IconCallback>();
-        }
-
-        /**
-         * Adds a callback to the icon request.
-         * If the callback already exists, it will not be added again.
-         */
-        public void addCallback(@NonNull IconCallback callback) {
-            if (callback == null) {
-                throw new IllegalArgumentException("callback cannot be null in IconRequest");
-            }
-            if (!mCallbacks.contains(callback)) {
-                mCallbacks.add(callback);
-            }
-        }
-
-        /**
-         * Checks if the icon request has the same uri, width, and height as the given values.
-         */
-        public boolean isSameRequest(@Nullable Uri uri, int width, int height) {
-            return Objects.equals(mUri, uri) && mWidth == width && mHeight == height;
-        }
-
-        @Override
-        public String toString() {
-            final StringBuilder sb = new StringBuilder("IconRequest{");
-            sb.append("uri=").append(mUri);
-            sb.append(", width=").append(mWidth);
-            sb.append(", height=").append(mHeight);
-            sb.append(", seq=").append(mSeq);
-            sb.append('}');
-            return sb.toString();
-        }
-
-        /**
-         * Gets an unmodifiable view of the list of callbacks associated with the request.
-         */
-        public List<IconCallback> getCallbacks() {
-            return Collections.unmodifiableList(mCallbacks);
-        }
-    }
-
-    /**
      * ServiceConnection to the other app.
      */
     private class MediaServiceConnection implements ServiceConnection {
@@ -809,7 +766,7 @@
             }
             return true;
         }
-    };
+    }
 
     /**
      * Callbacks from the service.
@@ -852,14 +809,6 @@
                 mediaBrowser.onLoadChildren(this, uri, list);
             }
         }
-
-        @Override
-        public void onLoadIcon(final int seqNum, final Bitmap bitmap) {
-            MediaBrowser mediaBrowser = mMediaBrowser.get();
-            if (mediaBrowser != null) {
-                mediaBrowser.onLoadIcon(this, seqNum, bitmap);
-            }
-        }
     }
 
     private static class Subscription {
diff --git a/media/java/android/media/browse/MediaBrowserItem.java b/media/java/android/media/browse/MediaBrowserItem.java
deleted file mode 100644
index 47ec46b..0000000
--- a/media/java/android/media/browse/MediaBrowserItem.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.browse;
-
-import android.annotation.DrawableRes;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.net.Uri;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Describes a media item in the list of items offered by a {@link MediaBrowserService}.
- */
-public final class MediaBrowserItem implements Parcelable {
-    private final Uri mUri;
-    private final int mFlags;
-    private final CharSequence mTitle;
-    private final CharSequence mSummary;
-    private final Uri mIconUri;
-    private final int mIconResourceId;
-    private final Bundle mExtras;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
-    public @interface Flags { }
-
-    /**
-     * Flag: Indicates that the item has children of its own.
-     */
-    public static final int FLAG_BROWSABLE = 1 << 0;
-
-    /**
-     * Flag: Indicates that the item is playable.
-     * <p>
-     * The Uri of this item may be passed to link android.media.session.MediaController#play(Uri)
-     * to start playing it.
-     * </p>
-     */
-    public static final int FLAG_PLAYABLE = 1 << 1;
-
-    /**
-     * Initialize a MediaBrowserItem object.
-     */
-    private MediaBrowserItem(@NonNull Uri uri, int flags, @NonNull CharSequence title,
-            CharSequence summary, @Nullable Uri iconUri, int iconResourceId, Bundle extras) {
-        if (uri == null) {
-            throw new IllegalArgumentException("uri can not be null");
-        }
-        if (title == null) {
-            throw new IllegalArgumentException("title can not be null");
-        }
-        mUri = uri;
-        mFlags = flags;
-        mTitle = title;
-        mSummary = summary;
-        mIconUri = iconUri;
-        mIconResourceId = iconResourceId;
-        mExtras = extras;
-    }
-
-    /**
-     * Private constructor.
-     */
-    private MediaBrowserItem(Parcel in) {
-        mUri = Uri.CREATOR.createFromParcel(in);
-        mFlags = in.readInt();
-        mTitle = in.readCharSequence();
-        if (in.readInt() != 0) {
-            mSummary = in.readCharSequence();
-        } else {
-            mSummary = null;
-        }
-        if (in.readInt() != 0) {
-            mIconUri = Uri.CREATOR.createFromParcel(in);
-        } else {
-            mIconUri = null;
-        }
-        mIconResourceId = in.readInt();
-        if (in.readInt() != 0) {
-            mExtras = Bundle.CREATOR.createFromParcel(in);
-        } else {
-            mExtras = null;
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        mUri.writeToParcel(out, flags);
-        out.writeInt(mFlags);
-        out.writeCharSequence(mTitle);
-        if (mSummary != null) {
-            out.writeInt(1);
-            out.writeCharSequence(mSummary);
-        } else {
-            out.writeInt(0);
-        }
-        if (mIconUri != null) {
-            out.writeInt(1);
-            mIconUri.writeToParcel(out, flags);
-        } else {
-            out.writeInt(0);
-        }
-        out.writeInt(mIconResourceId);
-        if (mExtras != null) {
-            out.writeInt(1);
-            mExtras.writeToParcel(out, flags);
-        } else {
-            out.writeInt(0);
-        }
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("MediaBrowserItem{");
-        sb.append("mUri=").append(mUri);
-        sb.append(", mFlags=").append(mFlags);
-        sb.append(", mTitle=").append(mTitle);
-        sb.append(", mSummary=").append(mSummary);
-        sb.append(", mIconUri=").append(mIconUri);
-        sb.append(", mIconResourceId=").append(mIconResourceId);
-        sb.append('}');
-        return sb.toString();
-    }
-
-    public static final Parcelable.Creator<MediaBrowserItem> CREATOR =
-            new Parcelable.Creator<MediaBrowserItem>() {
-                @Override
-                public MediaBrowserItem createFromParcel(Parcel in) {
-                    return new MediaBrowserItem(in);
-                }
-
-                @Override
-                public MediaBrowserItem[] newArray(int size) {
-                    return new MediaBrowserItem[size];
-                }
-            };
-
-    /**
-     * Gets the Uri of the item.
-     */
-    public @NonNull Uri getUri() {
-        return mUri;
-    }
-
-    /**
-     * Gets the flags of the item.
-     */
-    public @Flags int getFlags() {
-        return mFlags;
-    }
-
-    /**
-     * Returns whether this item is browsable.
-     * @see #FLAG_BROWSABLE
-     */
-    public boolean isBrowsable() {
-        return (mFlags & FLAG_BROWSABLE) != 0;
-    }
-
-    /**
-     * Returns whether this item is playable.
-     * @see #FLAG_PLAYABLE
-     */
-    public boolean isPlayable() {
-        return (mFlags & FLAG_PLAYABLE) != 0;
-    }
-
-    /**
-     * Gets the title of the item.
-     * @more
-     * The title will be shown as the first line of text when
-     * describing each item to the user.
-     */
-    public @NonNull CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Gets summary of the item, or null if none.
-     * @more
-     * The summary will be shown as the second line of text when
-     * describing each item to the user.
-     */
-    public @Nullable CharSequence getSummary() {
-        return mSummary;
-    }
-
-    /**
-     * Gets the Uri of the icon.
-     */
-    public @Nullable Uri getIconUri() {
-        return mIconUri;
-    }
-
-    /**
-     * Gets the resource id of the icon.
-     */
-    public @DrawableRes int getIconResourceId() {
-        return mIconResourceId;
-    }
-
-    /**
-     * Gets additional service-specified extras about the
-     * item or its content, or null if none.
-     */
-    public @Nullable Bundle getExtras() {
-        return mExtras;
-    }
-
-    /**
-     * Builder for {@link MediaBrowserItem} objects.
-     */
-    public static final class Builder {
-        private final Uri mUri;
-        private final int mFlags;
-        private final CharSequence mTitle;
-        private CharSequence mSummary;
-        private Uri mIconUri;
-        private int mIconResourceId;
-        private Bundle mExtras;
-
-        /**
-         * Creates an item builder.
-         */
-        public Builder(@NonNull Uri uri, @Flags int flags, @NonNull CharSequence title) {
-            if (uri == null) {
-                throw new IllegalArgumentException("uri can not be null");
-            }
-            if (title == null) {
-                throw new IllegalArgumentException("title can not be null");
-            }
-            mUri = uri;
-            mFlags = flags;
-            mTitle = title;
-        }
-
-        /**
-         * Sets summary of the item, or null if none.
-         */
-        public @NonNull Builder setSummary(@Nullable CharSequence summary) {
-            mSummary = summary;
-            return this;
-        }
-
-        /**
-         * Sets the uri of the icon.
-         * <p>
-         * Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be called.
-         * If both are specified, the resource id will be used to load the icon.
-         * </p>
-         */
-        public @NonNull Builder setIconUri(@Nullable Uri iconUri) {
-            mIconUri = iconUri;
-            return this;
-        }
-
-        /**
-         * Sets the resource id of the icon.
-         * <p>
-         * Either {@link #setIconUri(Uri)} or {@link #setIconResourceId(int)} should be specified.
-         * If both are specified, the resource id will be used to load the icon.
-         * </p>
-         */
-        public @NonNull Builder setIconResourceId(@DrawableRes int ResourceId) {
-            mIconResourceId = ResourceId;
-            return this;
-        }
-
-        /**
-         * Sets additional service-specified extras about the
-         * item or its content.
-         */
-        public @NonNull Builder setExtras(@Nullable Bundle extras) {
-            mExtras = extras;
-            return this;
-        }
-
-        /**
-         * Builds the item.
-         */
-        public @NonNull MediaBrowserItem build() {
-            return new MediaBrowserItem(mUri, mFlags, mTitle, mSummary, mIconUri,
-                    mIconResourceId, mExtras);
-        }
-    }
-}
-
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 9911129..49087b0 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -30,7 +30,7 @@
 
     // These callbacks are for the TransportPerformer
     void onPlay();
-    void onPlayUri(in Uri uri, in Bundle extras);
+    void onPlayFromMediaId(String uri, in Bundle extras);
     void onPlayFromSearch(String query, in Bundle extras);
     void onSkipToTrack(long id);
     void onPause();
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 6b80477..d684688 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -51,9 +51,9 @@
 
     // These commands are for the TransportControls
     void play();
-    void playUri(in Uri uri, in Bundle extras);
+    void playFromMediaId(String uri, in Bundle extras);
     void playFromSearch(String string, in Bundle extras);
-    void skipToTrack(long id);
+    void skipToQueueItem(long id);
     void pause();
     void stop();
     void next();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index d7baaa9..9641f83 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -173,7 +173,7 @@
      *
      * @return The current play queue or null.
      */
-    public @Nullable List<MediaSession.Item> getQueue() {
+    public @Nullable List<MediaSession.QueueItem> getQueue() {
         try {
             ParceledListSlice queue = mSessionBinder.getQueue();
             if (queue != null) {
@@ -538,9 +538,9 @@
          * @param queue A list of items in the current play queue. It should
          *            include the currently playing item as well as previous and
          *            upcoming items if applicable.
-         * @see MediaSession.Item
+         * @see MediaSession.QueueItem
          */
-        public void onQueueChanged(@Nullable List<MediaSession.Item> queue) {
+        public void onQueueChanged(@Nullable List<MediaSession.QueueItem> queue) {
         }
 
         /**
@@ -594,18 +594,19 @@
         /**
          * Request that the player start playback for a specific {@link Uri}.
          *
-         * @param uri The uri of the requested media.
+         * @param mediaId The uri of the requested media.
          * @param extras Optional extras that can include extra information about the media item
          *               to be played.
          */
-        public void playUri(Uri uri, Bundle extras) {
-            if (uri == null) {
-                throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
+        public void playFromMediaId(String mediaId, Bundle extras) {
+            if (TextUtils.isEmpty(mediaId)) {
+                throw new IllegalArgumentException(
+                        "You must specify a non-empty String for playFromMediaId.");
             }
             try {
-                mSessionBinder.playUri(uri, extras);
+                mSessionBinder.playFromMediaId(mediaId, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
             }
         }
 
@@ -631,9 +632,9 @@
          * Play an item with a specific id in the play queue. If you specify an
          * id that is not in the play queue, the behavior is undefined.
          */
-        public void skipToItem(long id) {
+        public void skipToQueueItem(long id) {
             try {
-                mSessionBinder.skipToTrack(id);
+                mSessionBinder.skipToQueueItem(id);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
             }
@@ -904,7 +905,7 @@
 
         @Override
         public void onQueueChanged(ParceledListSlice parceledQueue) {
-            List<MediaSession.Item> queue = parceledQueue.getList();
+            List<MediaSession.QueueItem> queue = parceledQueue.getList();
             MediaController controller = mController.get();
             if (controller != null) {
                 controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
@@ -960,7 +961,7 @@
                     mCallback.onMetadataChanged((MediaMetadata) msg.obj);
                     break;
                 case MSG_UPDATE_QUEUE:
-                    mCallback.onQueueChanged((List<MediaSession.Item>) msg.obj);
+                    mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
                     break;
                 case MSG_UPDATE_QUEUE_TITLE:
                     mCallback.onQueueTitleChanged((CharSequence) msg.obj);
diff --git a/media/java/android/media/session/MediaSession.aidl b/media/java/android/media/session/MediaSession.aidl
index 0ad58c4..f657cef 100644
--- a/media/java/android/media/session/MediaSession.aidl
+++ b/media/java/android/media/session/MediaSession.aidl
@@ -16,4 +16,4 @@
 package android.media.session;
 
 parcelable MediaSession.Token;
-parcelable MediaSession.Track;
\ No newline at end of file
+parcelable MediaSession.QueueItem;
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1670097..711831b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
+import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -38,6 +39,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
+import android.service.media.MediaBrowserService;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -180,18 +182,26 @@
      * @param handler The handler that events should be posted on.
      */
     public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
-        if (callback == null) {
-            mCallback = null;
-            return;
-        }
         synchronized (mLock) {
-            if (mCallback != null && mCallback.mCallback == callback) {
-                Log.w(TAG, "Tried to set same callback, ignoring");
+            if (callback == null) {
+                if (mCallback != null) {
+                    mCallback.mCallback.mSession = null;
+                }
+                mCallback = null;
                 return;
             }
+            if (mCallback != null) {
+                if (mCallback.mCallback == callback) {
+                    Log.w(TAG, "Tried to set same callback, ignoring");
+                    return;
+                }
+                // We're changing callbacks, clear the session from the old one.
+                mCallback.mCallback.mSession = null;
+            }
             if (handler == null) {
                 handler = new Handler();
             }
+            callback.mSession = this;
             CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
                     callback);
             mCallback = msgHandler;
@@ -418,9 +428,9 @@
      *
      * @param queue A list of items in the play queue.
      */
-    public void setQueue(@Nullable List<Item> queue) {
+    public void setQueue(@Nullable List<QueueItem> queue) {
         try {
-            mBinder.setQueue(new ParceledListSlice<Item>(queue));
+            mBinder.setQueue(new ParceledListSlice<QueueItem>(queue));
         } catch (RemoteException e) {
             Log.wtf("Dead object in setQueue.", e);
         }
@@ -478,8 +488,8 @@
         postToCallback(CallbackMessageHandler.MSG_PLAY);
     }
 
-    private void dispatchPlayUri(Uri uri, Bundle extras) {
-        postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
+    private void dispatchPlayFromMediaId(String mediaId, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
     }
 
     private void dispatchPlayFromSearch(String query, Bundle extras) {
@@ -744,9 +754,10 @@
         }
 
         /**
-         * Override to handle requests to play a specific {@link Uri}.
+         * Override to handle requests to play a specific mediaId that was
+         * provided by your app's {@link MediaBrowserService}.
          */
-        public void onPlayUri(Uri uri, Bundle extras) {
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
         }
 
         /**
@@ -759,7 +770,7 @@
          * Override to handle requests to play an item with a given id from the
          * play queue.
          */
-        public void onSkipToItem(long id) {
+        public void onSkipToQueueItem(long id) {
         }
 
         /**
@@ -824,10 +835,6 @@
          */
         public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
         }
-
-        private void setSession(MediaSession session) {
-            mSession = session;
-        }
     }
 
     /**
@@ -872,10 +879,10 @@
         }
 
         @Override
-        public void onPlayUri(Uri uri, Bundle extras) {
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayUri(uri, extras);
+                session.dispatchPlayFromMediaId(mediaId, extras);
             }
         }
 
@@ -990,125 +997,59 @@
     }
 
     /**
-     * A single item that is part of the play queue. It contains information
-     * necessary to display a single item in the queue.
+     * A single item that is part of the play queue. It contains a description
+     * of the item and its id in the queue.
      */
-    public static final class Item implements Parcelable {
+    public static final class QueueItem implements Parcelable {
         /**
          * This id is reserved. No items can be explicitly asigned this id.
          */
         public static final int UNKNOWN_ID = -1;
 
-        private final MediaMetadata mMetadata;
+        private final MediaDescription mDescription;
         private final long mId;
-        private final Uri mUri;
-        private final Bundle mExtras;
 
         /**
-         * Create a new {@link MediaSession.Item}.
+         * Create a new {@link MediaSession.QueueItem}.
          *
-         * @param metadata The metadata for this item.
+         * @param description The {@link MediaDescription} for this item.
          * @param id An identifier for this item. It must be unique within the
-         *            play queue.
-         * @param uri The uri for this item.
-         * @param extras A bundle of extras that can be used to add extra
-         *            information about this item.
+         *            play queue and cannot be {@link #UNKNOWN_ID}.
          */
-        private Item(MediaMetadata metadata, long id, Uri uri, Bundle extras) {
-            mMetadata = metadata;
+        public QueueItem(MediaDescription description, long id) {
+            if (description == null) {
+                throw new IllegalArgumentException("Description cannot be null.");
+            }
+            if (id == UNKNOWN_ID) {
+                throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
+            }
+            mDescription = description;
             mId = id;
-            mUri = uri;
-            mExtras = extras;
         }
 
-        private Item(Parcel in) {
-            mMetadata = MediaMetadata.CREATOR.createFromParcel(in);
+        private QueueItem(Parcel in) {
+            mDescription = MediaDescription.CREATOR.createFromParcel(in);
             mId = in.readLong();
-            mUri = Uri.CREATOR.createFromParcel(in);
-            mExtras = in.readBundle();
         }
 
         /**
-         * Get the metadata for this item.
+         * Get the description for this item.
          */
-        public MediaMetadata getMetadata() {
-            return mMetadata;
+        public MediaDescription getDescription() {
+            return mDescription;
         }
 
         /**
-         * Get the id for this item.
+         * Get the queue id for this item.
          */
-        public long getId() {
+        public long getQueueId() {
             return mId;
         }
 
-        /**
-         * Get the Uri for this item.
-         */
-        public Uri getUri() {
-            return mUri;
-        }
-
-        /**
-         * Get the extras for this item.
-         */
-        public Bundle getExtras() {
-            return mExtras;
-        }
-
-        /**
-         * Builder for {@link MediaSession.Item} objects.
-         */
-        public static final class Builder {
-            private final MediaMetadata mMetadata;
-            private final long mId;
-            private final Uri mUri;
-
-            private Bundle mExtras;
-
-            /**
-             * Create a builder with the metadata, id, and uri already set.
-             */
-            public Builder(MediaMetadata metadata, long id, Uri uri) {
-                if (metadata == null) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-null MediaMetadata to build an Item.");
-                }
-                if (uri == null) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-null Uri to build an Item.");
-                }
-                if (id == UNKNOWN_ID) {
-                    throw new IllegalArgumentException(
-                            "You must specify an id other than UNKNOWN_ID to build an Item.");
-                }
-                mMetadata = metadata;
-                mId = id;
-                mUri = uri;
-            }
-
-            /**
-             * Set optional extras for the item.
-             */
-            public MediaSession.Item.Builder setExtras(Bundle extras) {
-                mExtras = extras;
-                return this;
-            }
-
-            /**
-             * Create the {@link Item}.
-             */
-            public MediaSession.Item build() {
-                return new MediaSession.Item(mMetadata, mId, mUri, mExtras);
-            }
-        }
-
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            mMetadata.writeToParcel(dest, flags);
+            mDescription.writeToParcel(dest, flags);
             dest.writeLong(mId);
-            mUri.writeToParcel(dest, flags);
-            dest.writeBundle(mExtras);
         }
 
         @Override
@@ -1116,28 +1057,24 @@
             return 0;
         }
 
-        public static final Creator<MediaSession.Item> CREATOR
-                = new Creator<MediaSession.Item>() {
+        public static final Creator<MediaSession.QueueItem> CREATOR = new Creator<MediaSession.QueueItem>() {
 
             @Override
-            public MediaSession.Item createFromParcel(Parcel p) {
-                return new MediaSession.Item(p);
+            public MediaSession.QueueItem createFromParcel(Parcel p) {
+                return new MediaSession.QueueItem(p);
             }
 
             @Override
-            public MediaSession.Item[] newArray(int size) {
-                return new MediaSession.Item[size];
+            public MediaSession.QueueItem[] newArray(int size) {
+                return new MediaSession.QueueItem[size];
             }
         };
 
         @Override
         public String toString() {
-            return "MediaSession.Item {" +
-                    "Metadata=" + mMetadata +
-                    ", Id=" + mId +
-                    ", Uri=" + mUri +
-                    ", Extras=" + mExtras +
-                    " }";
+            return "MediaSession.QueueItem {" +
+                    "Description=" + mDescription +
+                    ", Id=" + mId + " }";
         }
     }
 
@@ -1156,7 +1093,7 @@
     private class CallbackMessageHandler extends Handler {
 
         private static final int MSG_PLAY = 1;
-        private static final int MSG_PLAY_URI = 2;
+        private static final int MSG_PLAY_MEDIA_ID = 2;
         private static final int MSG_PLAY_SEARCH = 3;
         private static final int MSG_SKIP_TO_ITEM = 4;
         private static final int MSG_PAUSE = 5;
@@ -1202,14 +1139,14 @@
                 case MSG_PLAY:
                     mCallback.onPlay();
                     break;
-                case MSG_PLAY_URI:
-                    mCallback.onPlayUri((Uri) msg.obj, msg.getData());
+                case MSG_PLAY_MEDIA_ID:
+                    mCallback.onPlayFromMediaId((String) msg.obj, msg.getData());
                     break;
                 case MSG_PLAY_SEARCH:
                     mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
                     break;
                 case MSG_SKIP_TO_ITEM:
-                    mCallback.onSkipToItem((Long) msg.obj);
+                    mCallback.onSkipToQueueItem((Long) msg.obj);
                 case MSG_PAUSE:
                     mCallback.onPause();
                     break;
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 2ca97dd..267d1ff 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -36,95 +36,95 @@
     private static final String TAG = "PlaybackState";
 
     /**
-     * Indicates this performer supports the stop command.
+     * Indicates this session supports the stop command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_STOP = 1 << 0;
 
     /**
-     * Indicates this performer supports the pause command.
+     * Indicates this session supports the pause command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_PAUSE = 1 << 1;
 
     /**
-     * Indicates this performer supports the play command.
+     * Indicates this session supports the play command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_PLAY = 1 << 2;
 
     /**
-     * Indicates this performer supports the rewind command.
+     * Indicates this session supports the rewind command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_REWIND = 1 << 3;
 
     /**
-     * Indicates this performer supports the previous command.
+     * Indicates this session supports the previous command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
 
     /**
-     * Indicates this performer supports the next command.
+     * Indicates this session supports the next command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
 
     /**
-     * Indicates this performer supports the fast forward command.
+     * Indicates this session supports the fast forward command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_FAST_FORWARD = 1 << 6;
 
     /**
-     * Indicates this performer supports the set rating command.
+     * Indicates this session supports the set rating command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_SET_RATING = 1 << 7;
 
     /**
-     * Indicates this performer supports the seek to command.
+     * Indicates this session supports the seek to command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_SEEK_TO = 1 << 8;
 
     /**
-     * Indicates this performer supports the play/pause toggle command.
+     * Indicates this session supports the play/pause toggle command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_PLAY_PAUSE = 1 << 9;
 
     /**
-     * Indicates this performer supports the play from uri command.
+     * Indicates this session supports the play from media id command.
      *
      * @see Builder#setActions(long)
      */
-    public static final long ACTION_PLAY_URI = 1 << 10;
+    public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10;
 
     /**
-     * Indicates this performer supports the play from search command.
+     * Indicates this session supports the play from search command.
      *
      * @see Builder#setActions(long)
      */
     public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
 
     /**
-     * Indicates this performer supports the skip to item command.
+     * Indicates this session supports the skip to queue item command.
      *
      * @see Builder#setActions(long)
      */
-    public static final long ACTION_SKIP_TO_ITEM = 1 << 12;
+    public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
 
     /**
      * This is the default playback state and indicates that no media has been
@@ -211,6 +211,14 @@
     public final static int STATE_SKIPPING_TO_NEXT = 10;
 
     /**
+     * State indicating the player is currently skipping to a specific item in
+     * the queue.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
+
+    /**
      * Use this value for the position to indicate the position is not known.
      */
     public final static long PLAYBACK_POSITION_UNKNOWN = -1;
@@ -374,6 +382,18 @@
     }
 
     /**
+     * Get the id of the currently active item in the queue. If there is no
+     * queue or a queue is not supported by the session this will be
+     * {@link MediaSession.QueueItem#UNKNOWN_ID}.
+     *
+     * @return The id of the currently active item in the queue or
+     *         {@link MediaSession.QueueItem#UNKNOWN_ID}.
+     */
+    public long getActiveQueueItemId() {
+        return mActiveItemId;
+    }
+
+    /**
      * Get the {@link PlaybackState} state for the given
      * {@link RemoteControlClient} state.
      *
@@ -716,7 +736,7 @@
         private long mActions;
         private CharSequence mErrorMessage;
         private long mUpdateTime;
-        private long mActiveItemId = MediaSession.Item.UNKNOWN_ID;
+        private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
 
         /**
          * Creates an initially empty state builder.
@@ -904,12 +924,12 @@
 
         /**
          * Set the active item in the play queue by specifying its id. The
-         * default value is {@link MediaSession.Item#UNKNOWN_ID}
+         * default value is {@link MediaSession.QueueItem#UNKNOWN_ID}
          *
          * @param id The id of the active item.
          * @return this
          */
-        public Builder setActiveItem(long id) {
+        public Builder setActiveQueueItemId(long id) {
             mActiveItemId = id;
             return this;
         }
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 00c7ad4..7f1c304 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -680,7 +680,7 @@
          * <p>
          * A value of 1 indicates the channel is included in the channel list that applications use
          * to browse channels, a value of 0 indicates the channel is not included in the list. If
-         * not specified, this value is set to 1 (browsable) by default.
+         * not specified, this value is set to 0 (not browsable) by default.
          * </p><p>
          * Type: INTEGER (boolean)
          * </p>
diff --git a/media/java/android/media/browse/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
similarity index 76%
rename from media/java/android/media/browse/IMediaBrowserService.aidl
rename to media/java/android/service/media/IMediaBrowserService.aidl
index 8acd724..2631ddd 100644
--- a/media/java/android/media/browse/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -1,9 +1,9 @@
 // Copyright 2014 Google Inc. All Rights Reserved.
 
-package android.media.browse;
+package android.service.media;
 
 import android.content.res.Configuration;
-import android.media.browse.IMediaBrowserServiceCallbacks;
+import android.service.media.IMediaBrowserServiceCallbacks;
 import android.net.Uri;
 import android.os.Bundle;
 
@@ -18,6 +18,4 @@
 
     void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
-    void loadIcon(in int seqNum, in Uri uri, int width, int height,
-            IMediaBrowserServiceCallbacks callbacks);
 }
\ No newline at end of file
diff --git a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
similarity index 91%
rename from media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
rename to media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index 06fabcc..7702a50 100644
--- a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -1,6 +1,6 @@
 // Copyright 2014 Google Inc. All Rights Reserved.
 
-package android.media.browse;
+package android.service.media;
 
 import android.content.pm.ParceledListSlice;
 import android.graphics.Bitmap;
@@ -24,5 +24,4 @@
     void onConnect(in Uri root, in MediaSession.Token session, in Bundle extras);
     void onConnectFailed();
     void onLoadChildren(in Uri uri, in ParceledListSlice list);
-    void onLoadIcon(int seqNum, in Bitmap bitmap);
 }
diff --git a/media/java/android/media/browse/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
similarity index 81%
rename from media/java/android/media/browse/MediaBrowserService.java
rename to media/java/android/service/media/MediaBrowserService.java
index 99126c9..4d6fd7b 100644
--- a/media/java/android/media/browse/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.browse;
+package android.service.media;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -27,6 +27,8 @@
 import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Binder;
@@ -34,6 +36,9 @@
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.service.media.IMediaBrowserService;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.service.media.IMediaBrowserService.Stub;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -264,59 +269,6 @@
                 }
             });
         }
-
-        @Override
-        public void loadIcon(final int seq, final Uri uri, final int width, final int height,
-                final IMediaBrowserServiceCallbacks callbacks) {
-            if (uri == null) {
-                throw new IllegalStateException("loadIcon sent null list for uri " + uri);
-            }
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    // In theory we could return a result to a new connection, but in practice
-                    // the other side in MediaBrowser uses a new IMediaBrowserServiceCallbacks
-                    // object every time it calls connect(), so as long as it does that we won't
-                    // see results sent for the wrong connection.
-                    final ConnectionRecord connection = mConnections.get(callbacks.asBinder());
-                    if (connection == null) {
-                        if (DBG) {
-                            Log.d(TAG, "Not loading bitmap for invalid connection. uri=" + uri);
-                        }
-                        return;
-                    }
-
-                    final Result<Bitmap> result = new Result<Bitmap>(uri) {
-                        @Override
-                        void onResultSent(Bitmap bitmap) {
-                            if (mConnections.get(connection.callbacks.asBinder()) != connection) {
-                                if (DBG) {
-                                    Log.d(TAG, "Not sending onLoadIcon result for connection"
-                                            + " that has been disconnected. pkg=" + connection.pkg
-                                            + " uri=" + uri);
-                                }
-                                return;
-                            }
-
-                            try {
-                                callbacks.onLoadIcon(seq, bitmap);
-                            } catch (RemoteException e) {
-                                // The other side is in the process of crashing.
-                                Log.w(TAG, "RemoteException in calling onLoadIcon", e);
-                            }
-                        }
-                    };
-
-                    onLoadIcon(uri, width, height, result);
-
-                    if (!result.isDone()) {
-                        throw new IllegalStateException("onLoadIcon must call detach() or"
-                                + " sendResult() before returning for package=" + connection.pkg
-                                + " uri=" + uri);
-                    }
-                }
-            });
-        }
     }
 
     @Override
@@ -372,26 +324,7 @@
      * @return The list of children, or null if the uri is invalid.
      */
     public abstract void onLoadChildren(@NonNull Uri parentUri,
-            @NonNull Result<List<MediaBrowserItem>> result);
-
-    /**
-     * Called to get the icon of a particular media item.
-     * <p>
-     * Implementations must call result.{@link Result#sendResult result.sendResult} with the bitmap.
-     * If loading the bitmap will be an expensive operation that should be performed
-     * on another thread, result.{@link Result#detach result.detach} may be called before returning
-     * from this function, and then {@link Result#sendResult result.sendResult} called when
-     * the loading is complete.
-     *
-     * @param uri The uri of the media item.
-     * @param width The requested width of the icon in dp.
-     * @param height The requested height of the icon in dp.
-     *
-     * @return The file descriptor of the icon, which may then be loaded
-     *          using a bitmap factory, or null if the item does not have an icon.
-     */
-    public abstract void onLoadIcon(@NonNull Uri uri, int width, int height,
-            @NonNull Result<Bitmap> result);
+            @NonNull Result<List<MediaBrowser.MediaItem>> result);
 
     /**
      * Call to set the media session.
@@ -478,9 +411,11 @@
      * Callers must make sure that this connection is still connected.
      */
     private void performLoadChildren(final Uri uri, final ConnectionRecord connection) {
-        final Result<List<MediaBrowserItem>> result = new Result<List<MediaBrowserItem>>(uri) {
+        final Result<List<MediaBrowser.MediaItem>> result
+ = new Result<List<MediaBrowser.MediaItem>>(
+                uri) {
             @Override
-            void onResultSent(List<MediaBrowserItem> list) {
+            void onResultSent(List<MediaBrowser.MediaItem> list) {
                 if (list == null) {
                     throw new IllegalStateException("onLoadChildren sent null list for uri " + uri);
                 }
@@ -492,7 +427,7 @@
                     return;
                 }
 
-                final ParceledListSlice<MediaBrowserItem> pls = new ParceledListSlice(list);
+                final ParceledListSlice<MediaBrowser.MediaItem> pls = new ParceledListSlice(list);
                 try {
                     connection.callbacks.onLoadChildren(uri, pls);
                 } catch (RemoteException ex) {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index c678ac7..0fed27e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -263,7 +263,7 @@
 
     String8 vendorMessage;
     if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
-        vendorMessage.format("DRM vendor-defined error: %d", err);
+        vendorMessage = String8::format("DRM vendor-defined error: %d", err);
         drmMessage = vendorMessage.string();
     }
 
@@ -285,7 +285,7 @@
             if (msg == NULL) {
                 msg = drmMessage;
             } else {
-                errbuf.format("%s: %s", msg, drmMessage);
+                errbuf = String8::format("%s: %s", msg, drmMessage);
                 msg = errbuf.string();
             }
         }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 38d7e3e..86c23c7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -25,6 +25,9 @@
 public class MediaNames {
     // A directory to hold all kinds of media files
     public static final String MEDIA_SAMPLE_POOL = "/sdcard/media_api/samples/";
+    // A file to hold all streaming URLs
+    public static final String MEDIA_STREAMING_SRC = "/sdcard/media_api/streaming.txt";
+
     // Audio files
     public static final String MP3CBR = "/sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3";
     public static final String MP3VBR = "/sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_VBR.mp3";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index e84f762..66ed933 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -42,13 +42,13 @@
 import java.util.Random;
 /**
  * Junit / Instrumentation test case for the media player api
- 
- */  
-public class CodecTest {    
+
+ */
+public class CodecTest {
     private static String TAG = "CodecTest";
     private static MediaPlayer mMediaPlayer;
     private MediaPlayer.OnPreparedListener mOnPreparedListener;
-    
+
     private static int WAIT_FOR_COMMAND_TO_COMPLETE = 60000;  //1 min max.
     private static boolean mInitialized = false;
     private static boolean mPrepareReset = false;
@@ -66,18 +66,18 @@
     public static int mMediaInfoNotSeekableCount = 0;
     public static int mMediaInfoMetdataUpdateCount = 0;
 
-    public static String printCpuInfo(){      
+    public static String printCpuInfo(){
         String cm = "dumpsys cpuinfo";
         String cpuinfo =null;
         int ch;
         try{
             Process  p = Runtime.getRuntime().exec(cm);
-            InputStream in = p.getInputStream();        
+            InputStream in = p.getInputStream();
             StringBuffer sb = new StringBuffer(512);
-            while ( ( ch = in.read() ) != -1 ){  
-                sb.append((char) ch); 
+            while ( ( ch = in.read() ) != -1 ){
+                sb.append((char) ch);
             }
-            cpuinfo = sb.toString();      
+            cpuinfo = sb.toString();
         }catch (IOException e){
             Log.v(TAG, e.toString());
         }
@@ -90,14 +90,14 @@
         MediaPlayer mp = new MediaPlayer();
         try{
             mp.setDataSource(filePath);
-            mp.prepare(); 
+            mp.prepare();
         }catch (Exception e){
             Log.v(TAG, e.toString());
         }
         int duration = mp.getDuration();
         Log.v(TAG, "Duration " + duration);
         mp.release();
-        Log.v(TAG, "release");      
+        Log.v(TAG, "release");
         return duration;
     }
 
@@ -122,7 +122,7 @@
         }
         currentPosition = mp.getCurrentPosition();
         mp.stop();
-        mp.release();   
+        mp.release();
         Log.v(TAG, "mp currentPositon = " + currentPosition + " play duration = " + (t2-t1));
         //The currentposition should be within 10% of the sleep time
         //For the very short mp3, it should return the length instead of 10 seconds
@@ -130,11 +130,11 @@
             if (currentPosition < 1000 )
                 return true;
         }
-        if ((currentPosition < ((t2-t1) *1.2)) && (currentPosition > 0)) 
+        if ((currentPosition < ((t2-t1) *1.2)) && (currentPosition > 0))
             return true;
         else
             return false;
-    }  
+    }
 
     public static boolean seekTo(String filePath){
         Log.v(TAG, "seekTo " + filePath);
@@ -149,12 +149,12 @@
             currentPosition = mp.getCurrentPosition();
         }catch (Exception e){
             Log.v(TAG, e.getMessage());
-        }      
+        }
         mp.stop();
         mp.release();
         Log.v(TAG, "CurrentPosition = " + currentPosition);
         //The currentposition should be at least greater than the 80% of seek time
-        if ((currentPosition > MediaNames.SEEK_TIME *0.8)) 
+        if ((currentPosition > MediaNames.SEEK_TIME *0.8))
             return true;
         else
             return false;
@@ -170,7 +170,7 @@
         try{
             mp.setDataSource(filePath);
             mp.prepare();
-            duration = mp.getDuration(); 
+            duration = mp.getDuration();
             Log.v(TAG, "setLooping duration " + duration);
             mp.setLooping(true);
             mp.start();
@@ -180,14 +180,14 @@
             Thread.sleep(20000);
             t2=SystemClock.uptimeMillis();
             Log.v(TAG, "pause");
-            //Bug# 1106852 - IllegalStateException will be thrown if pause is called 
+            //Bug# 1106852 - IllegalStateException will be thrown if pause is called
             //in here
             //mp.pause();
             currentPosition = mp.getCurrentPosition();
             Log.v(TAG, "looping position " + currentPosition + "duration = " + (t2-t1));
         }catch (Exception e){
             Log.v(TAG, "Exception : " + e.toString());
-        }      
+        }
         mp.stop();
         mp.release();
         //The current position should be within 20% of the sleep time
@@ -196,7 +196,7 @@
             return true;
         else
             return false;
-    }  
+    }
 
     public static boolean pause(String filePath) throws Exception {
         Log.v(TAG, "pause - " + filePath);
@@ -206,7 +206,7 @@
         long t2=0;
         MediaPlayer mp = new MediaPlayer();
         mp.setDataSource(filePath);
-        mp.prepare();    
+        mp.prepare();
         int duration = mp.getDuration();
         mp.start();
         t1=SystemClock.uptimeMillis();
@@ -244,7 +244,7 @@
         mp.pause();
         mp.release();
     }
-    
+
     static MediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener =
         new MediaPlayer.OnVideoSizeChangedListener() {
             public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
@@ -258,7 +258,7 @@
     //Register the videoSizeChanged listener
     public static int videoHeight(String filePath) throws Exception {
         Log.v(TAG, "videoHeight - " + filePath);
-        int videoHeight = 0;    
+        int videoHeight = 0;
         synchronized (lock) {
             initializeMessageLooper();
             try {
@@ -286,7 +286,7 @@
         } catch (Exception e) {
             Log.e(TAG, e.getMessage());
         }
-        
+
         return videoHeight;
     }
 
@@ -321,12 +321,12 @@
             terminateMessageLooper();
         } catch (Exception e) {
             Log.e(TAG, e.getMessage());
-        }        
+        }
         return videoWidth;
     }
 
     //This also test the streaming video which may take a long
-    //time to start the playback. 
+    //time to start the playback.
     public static boolean videoSeekTo(String filePath) throws Exception {
         Log.v(TAG, "videoSeekTo - " + filePath);
         int currentPosition = 0;
@@ -392,12 +392,12 @@
         currentPosition = mp.getCurrentPosition();
         Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
         mp.stop();
-        mp.release();   
+        mp.release();
         Log.v(TAG, "duration = " + duration);
         if (currentPosition < 0.9 * duration || isPlaying)
             return false;
         else
-            return true;        
+            return true;
     }
 
     public static boolean shortMediaStop(String filePath){
@@ -419,12 +419,12 @@
         currentPosition = mp.getCurrentPosition();
         Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
         mp.stop();
-        mp.release();   
+        mp.release();
         Log.v(TAG, "duration = " + duration);
         if (currentPosition > duration || isPlaying)
             return false;
         else
-            return true;        
+            return true;
     }
 
     public static boolean playToEnd(String filePath){
@@ -449,13 +449,13 @@
         //updateDuration = mp.getDuration();
         Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
         mp.stop();
-        mp.release();   
+        mp.release();
         //Log.v(TAG, "duration = " + duration);
         //Log.v(TAG, "Update duration = " + updateDuration);
         if (currentPosition > duration || isPlaying)
             return false;
         else
-            return true;        
+            return true;
     }
 
     public static boolean seektoBeforeStart(String filePath){
@@ -478,7 +478,7 @@
         if (currentPosition < duration/2)
             return false;
         else
-            return true;        
+            return true;
     }
 
     public static boolean mediaRecorderRecord(String filePath){
@@ -499,7 +499,7 @@
             mRecorder.release();
         }catch (Exception e){
             Log.v(TAG, e.toString());
-        }  
+        }
 
         //Verify the recorded file
         MediaPlayer mp = new MediaPlayer();
@@ -540,7 +540,7 @@
             }
             Bitmap outThumbnail = mMediaMetadataRetriever.getFrameAtTime(-1);
 
-            //Verify the thumbnail 
+            //Verify the thumbnail
             Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);
             outputWidth = outThumbnail.getWidth();
             outputHeight = outThumbnail.getHeight();
@@ -586,8 +586,8 @@
             return false;
     }
 
-    public static boolean prepareAsyncReset(String filePath){    
-        //preparesAsync 
+    public static boolean prepareAsyncReset(String filePath){
+        //preparesAsync
         try{
             MediaPlayer mp = new MediaPlayer();
             mp.setDataSource(filePath);
@@ -602,9 +602,9 @@
     }
 
 
-    public static boolean isLooping(String filePath) {        
+    public static boolean isLooping(String filePath) {
         MediaPlayer mp = null;
-        
+
         try {
             mp = new MediaPlayer();
             if (mp.isLooping()) {
@@ -619,7 +619,7 @@
                 Log.v(TAG, "MediaPlayer.isLooping() returned false after setLooping(true)");
                 return false;
             }
-            
+
             mp.setLooping(false);
             if (mp.isLooping()) {
                 Log.v(TAG, "MediaPlayer.isLooping() returned true after setLooping(false)");
@@ -659,9 +659,9 @@
 
         return true;
     }
-    
+
     /*
-     * Initializes the message looper so that the mediaPlayer object can 
+     * Initializes the message looper so that the mediaPlayer object can
      * receive the callback messages.
      */
     private static void initializeMessageLooper() {
@@ -672,10 +672,10 @@
                 // Set up a looper to be used by camera.
                 Looper.prepare();
                 Log.v(TAG, "start loopRun");
-                // Save the looper so that we can terminate this thread 
+                // Save the looper so that we can terminate this thread
                 // after we are done with it.
-                mLooper = Looper.myLooper();                
-                mMediaPlayer = new MediaPlayer();                                
+                mLooper = Looper.myLooper();
+                mMediaPlayer = new MediaPlayer();
                 synchronized (lock) {
                     mInitialized = true;
                     lock.notify();
@@ -685,7 +685,7 @@
             }
         }.start();
     }
-    
+
     /*
      * Terminates the message looper thread.
      */
@@ -693,7 +693,7 @@
         mLooper.quit();
         mMediaPlayer.release();
     }
-    
+
     static MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
         public void onPrepared(MediaPlayer mp) {
             synchronized (prepareDone) {
@@ -707,14 +707,14 @@
             }
         }
     };
-   
+
     public static boolean prepareAsyncCallback(String filePath, boolean reset) throws Exception {
         //Added the PrepareReset flag which allow us to switch to different
         //test case.
         if (reset){
             mPrepareReset = true;
         }
-        
+
         synchronized (lock) {
             initializeMessageLooper();
             try {
@@ -728,18 +728,18 @@
             mMediaPlayer.setOnPreparedListener(mPreparedListener);
             mMediaPlayer.setDataSource(filePath);
             mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
-            mMediaPlayer.prepareAsync(); 
+            mMediaPlayer.prepareAsync();
             synchronized (prepareDone) {
                 try {
                     prepareDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
                 } catch (Exception e) {
                     Log.v(TAG, "wait was interrupted.");
                 }
-            }         
+            }
             terminateMessageLooper();
         }catch (Exception e){
             Log.v(TAG,e.getMessage());
-        }      
+        }
        return onPrepareSuccess;
     }
 
@@ -784,8 +784,12 @@
         }
     };
 
-    // For each media file, forward twice and backward once, then play to the end
     public static boolean playMediaSamples(String filePath) throws Exception {
+        return playMediaSamples(filePath, 2000);
+    }
+
+    // For each media file, forward twice and backward once, then play to the end
+    public static boolean playMediaSamples(String filePath, int buffertime) throws Exception {
         int duration = 0;
         int curPosition = 0;
         int nextPosition = 0;
@@ -822,14 +826,14 @@
             waittime = duration - mMediaPlayer.getCurrentPosition();
             synchronized(onCompletion){
                 try {
-                    onCompletion.wait(waittime + 2000);
+                    onCompletion.wait(waittime + buffertime);
                 }catch (Exception e) {
                     Log.v(TAG, "playMediaSamples are interrupted");
                     return false;
                 }
             }
             terminateMessageLooper();
-        }catch (Exception e) {
+        } catch (Exception e) {
             Log.v(TAG, "playMediaSamples:" + e.getMessage());
         }
         return onCompleteSuccess;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
new file mode 100644
index 0000000..d92c857
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStreamingStressTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaPlayerStressTestRunner;
+
+import android.os.Bundle;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.android.mediaframeworktest.MediaNames;
+import com.android.mediaframeworktest.functional.CodecTest;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.Writer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Junit / Instrumentation test case for the media player
+ */
+public class MediaPlayerStreamingStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+    private String TAG = "MediaPlayerStreamingStressTest";
+    private String mStreamingSrc;
+
+    public MediaPlayerStreamingStressTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    protected void setUp() throws Exception {
+        //Insert a 2 second before launching the test activity. This is
+        //the workaround for the race condition of requesting the updated surface.
+        Thread.sleep(2000);
+        getActivity();
+        MediaPlayerStressTestRunner mRunner = (MediaPlayerStressTestRunner)getInstrumentation();
+        Bundle arguments = mRunner.getArguments();
+        mStreamingSrc = arguments.getString("streaming-source");
+        if (mStreamingSrc == null) {
+            mStreamingSrc = MediaNames.MEDIA_STREAMING_SRC;
+        }
+        super.setUp();
+    }
+
+    private int mTotalPlaybackError = 0;
+    private int mTotalComplete = 0;
+    private int mTotalInfoUnknown = 0;
+    private int mTotalVideoTrackLagging = 0;
+    private int mTotalBadInterleaving = 0;
+    private int mTotalNotSeekable = 0;
+    private int mTotalMetaDataUpdate = 0;
+
+    //Test result output file
+    private static final String PLAYBACK_RESULT = "StreamingTestResult.txt";
+
+    private void writeTestOutput(String filename, Writer output) throws Exception{
+        output.write("URL: " + filename);
+        output.write(" Complete: " + CodecTest.onCompleteSuccess);
+        output.write(" Error: " + CodecTest.mPlaybackError);
+        output.write(" Unknown Info: " + CodecTest.mMediaInfoUnknownCount);
+        output.write(" Track Lagging: " +  CodecTest.mMediaInfoVideoTrackLaggingCount);
+        output.write(" Bad Interleaving: " + CodecTest.mMediaInfoBadInterleavingCount);
+        output.write(" Not Seekable: " + CodecTest.mMediaInfoNotSeekableCount);
+        output.write(" Info Meta data update: " + CodecTest.mMediaInfoMetdataUpdateCount);
+        output.write("\n");
+    }
+
+    private void writeTestSummary(Writer output) throws Exception{
+        output.write("Total Result:\n");
+        output.write("Total Complete: " + mTotalComplete + "\n");
+        output.write("Total Error: " + mTotalPlaybackError + "\n");
+        output.write("Total Unknown Info: " + mTotalInfoUnknown + "\n");
+        output.write("Total Track Lagging: " + mTotalVideoTrackLagging + "\n" );
+        output.write("Total Bad Interleaving: " + mTotalBadInterleaving + "\n");
+        output.write("Total Not Seekable: " + mTotalNotSeekable + "\n");
+        output.write("Total Info Meta data update: " + mTotalMetaDataUpdate + "\n");
+        output.write("\n");
+    }
+
+    private void updateTestResult(){
+        if (CodecTest.onCompleteSuccess){
+            mTotalComplete++;
+        }
+        else if (CodecTest.mPlaybackError){
+            mTotalPlaybackError++;
+        }
+        mTotalInfoUnknown += CodecTest.mMediaInfoUnknownCount;
+        mTotalVideoTrackLagging += CodecTest.mMediaInfoVideoTrackLaggingCount;
+        mTotalBadInterleaving += CodecTest.mMediaInfoBadInterleavingCount;
+        mTotalNotSeekable += CodecTest.mMediaInfoNotSeekableCount;
+        mTotalMetaDataUpdate += CodecTest.mMediaInfoMetdataUpdateCount;
+    }
+
+    //Test that will start the playback for all the videos
+    //under the samples folder
+    @LargeTest
+    public void testVideoPlayback() throws Exception {
+        String fileWithError = "Filename:\n";
+        File playbackOutput = new File(Environment.getExternalStorageDirectory(), PLAYBACK_RESULT);
+        Writer output = new BufferedWriter(new FileWriter(playbackOutput, true));
+
+        boolean testResult = true;
+        // load directory files
+        boolean onCompleteSuccess = false;
+
+
+        Log.i(TAG, "Streaming src file: " + mStreamingSrc);
+        //TODO: add try catch
+
+        File f = new File(mStreamingSrc);
+        BufferedReader br = new BufferedReader(new FileReader(f));
+        List<String> urls = new ArrayList<String>();
+        String line;
+        while ((line = br.readLine()) != null) {
+           urls.add(line.trim());
+        }
+        br.close();
+        if (urls == null) {
+            Log.v("MediaPlayerStreamingTest:testVideoPlayback", "no url found");
+            return;
+        } else {
+            for (int i = 0; i < urls.size(); i++) {
+                //Get url
+                String filename = urls.get(i);
+                onCompleteSuccess =
+                    CodecTest.playMediaSamples(filename, 60000);
+                if (!onCompleteSuccess){
+                    //Don't fail the test right away, print out the failure file.
+                    fileWithError += filename + '\n';
+                    Log.v(TAG, "Failure File : " + fileWithError);
+                    testResult = false;
+                }
+                Thread.sleep(3000);
+                //Write test result to an output file
+                writeTestOutput(filename,output);
+                //Get the summary
+                updateTestResult();
+            }
+            writeTestSummary(output);
+            output.close();
+            assertTrue("testMediaSamples", testResult);
+       }
+    }
+}
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index ee5d42a..761d58a 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -27,6 +27,7 @@
         android:id="@+id/static_content"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
+        android:paddingStart="8dip"
         android:elevation="@dimen/preview_controls_elevation"
         android:background="?android:attr/colorPrimary">
 
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 0629481..c2a0da9 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -55,8 +55,7 @@
                     android:text="@string/label_copies">
                 </TextView>
 
-                <view
-                    class="com.android.printspooler.widget.FirstFocusableEditText"
+                <EditText
                     android:id="@+id/copies_edittext"
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
@@ -64,7 +63,7 @@
                     android:singleLine="true"
                     android:ellipsize="end"
                     android:inputType="numberDecimal">
-                </view>
+                </EditText>
 
             </LinearLayout>
 
@@ -198,8 +197,7 @@
                     android:visibility="visible">
                 </TextView>
 
-                <view
-                    class="com.android.printspooler.widget.FirstFocusableEditText"
+                <EditText
                     android:id="@+id/page_range_edittext"
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
@@ -208,7 +206,7 @@
                     android:ellipsize="end"
                     android:visibility="visible"
                     android:inputType="textNoSuggestions">
-                </view>
+                </EditText>
 
             </LinearLayout>
 
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 43d8aaf..4381a7a 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -17,8 +17,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
-      android:paddingStart="16dip"
-      android:paddingEnd="16dip"
+      android:paddingStart="8dip"
+      android:paddingEnd="8dip"
       android:minHeight="56dip"
       android:orientation="horizontal"
       android:gravity="start|center_vertical">
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 173057b..77c500a 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -23,8 +23,6 @@
         android:id="@android:id/list"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:paddingStart="@dimen/printer_list_view_padding_start"
-        android:paddingEnd="@dimen/printer_list_view_padding_end"
         android:scrollbarStyle="outsideOverlay"
         android:cacheColorHint="@android:color/transparent"
         android:scrollbarAlwaysDrawVerticalTrack="true" >
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
deleted file mode 100644
index 14403a1..0000000
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
-    android:orientation="vertical"
-    android:gravity="start|center_vertical">
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="?android:attr/spinnerDropDownItemStyle"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:textIsSelectable="false"
-        android:gravity="top|left"
-        android:duplicateParentState="true">
-    </TextView>
-
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="?android:attr/spinnerDropDownItemStyle"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:textIsSelectable="false"
-        android:visibility="gone"
-        android:duplicateParentState="true">
-    </TextView>
-
-</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
index a4666a5..6cf9754b5 100644
--- a/packages/PrintSpooler/res/values-land/constants.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -16,10 +16,6 @@
 
 <resources>
 
-    <dimen name="printer_list_view_padding_start">48dip</dimen>
-
-    <dimen name="printer_list_view_padding_end">48dip</dimen>
-
     <integer name="preview_page_per_row_count">2</integer>
 
     <integer name="print_option_column_count">3</integer>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index b95703b..b4e4777 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -28,9 +28,6 @@
 
     <dimen name="print_dialog_frame_max_width_dip">400dip</dimen>
 
-    <dimen name="printer_list_view_padding_start">16dip</dimen>
-    <dimen name="printer_list_view_padding_end">16dip</dimen>
-
     <dimen name="selected_page_elevation">6dip</dimen>
     <dimen name="unselected_page_elevation">2dip</dimen>
 
@@ -46,6 +43,6 @@
     <fraction name="page_unselected_alpha">50%</fraction>
 
     <dimen name="preview_page_footer_height">32dip</dimen>
-    <dimen name="preview_page_min_width">130dip</dimen>
+    <dimen name="preview_page_min_width">128dip</dimen>
 
 </resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index db319e9..532b01f 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,7 +16,10 @@
 
 <resources>
 
-    <style name="PrintActivity" parent="@android:style/Theme.Material.Settings">
+    <style name="PrintActivity" parent="@android:style/Theme.Material">
+        <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+        <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+        <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 5bcdb9f..d949673 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -289,15 +289,11 @@
                     + " for position: " + position);
         }
 
-        final int pageCount = getItemCount();
         MyViewHolder myHolder = (MyViewHolder) holder;
 
         View page = holder.itemView;
-        if (pageCount > 1) {
-            page.setOnClickListener(mPageClickListener);
-        } else {
-            page.setOnClickListener(null);
-        }
+        page.setOnClickListener(mPageClickListener);
+
         page.setTag(holder);
 
         myHolder.mPageInAdapter = position;
@@ -339,16 +335,9 @@
         }
         content.init(provider, mMediaSize, mMinMargins);
 
-
         View pageSelector = page.findViewById(R.id.page_selector);
         pageSelector.setTag(myHolder);
-        if (pageCount > 1) {
-            pageSelector.setOnClickListener(mPageClickListener);
-            pageSelector.setVisibility(View.VISIBLE);
-        } else {
-            pageSelector.setOnClickListener(null);
-            pageSelector.setVisibility(View.GONE);
-        }
+        pageSelector.setOnClickListener(mPageClickListener);
 
         if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
             pageSelector.setSelected(true);
@@ -449,8 +438,9 @@
 
         final int verticalPadding;
         if (mPageContentHeight + mFooterHeight + mPreviewListPadding > availableHeight) {
-            verticalPadding = Math.max(mPreviewPageMargin,
-                    (availableHeight - totalContentHeight) / 2);
+            verticalPadding = Math.max(0,
+                    (availableHeight - mPageContentHeight - mFooterHeight) / 2
+                            - mPreviewPageMargin);
         } else {
             verticalPadding = Math.max(mPreviewListPadding,
                     (availableHeight - totalContentHeight) / 2);
@@ -791,6 +781,9 @@
                 page.animate().translationZ(mSelectedPageElevation)
                         .alpha(mSelectedPageAlpha);
             } else {
+                if (mConfirmedPagesInDocument.size() <= 1) {
+                    return;
+                }
                 mConfirmedPagesInDocument.remove(pageInDocument);
                 pageSelector.setSelected(false);
                 page.animate().translationZ(mUnselectedPageElevation)
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index fe17516..01c9746 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -982,21 +982,21 @@
 
         // Media size.
         mMediaSizeSpinnerAdapter = new ArrayAdapter<>(
-                this, R.layout.spinner_dropdown_item, R.id.title);
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
         mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
         mMediaSizeSpinner.setOnItemSelectedListener(itemSelectedListener);
 
         // Color mode.
         mColorModeSpinnerAdapter = new ArrayAdapter<>(
-                this, R.layout.spinner_dropdown_item, R.id.title);
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
         mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
         mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
 
         // Orientation
         mOrientationSpinnerAdapter = new ArrayAdapter<>(
-                this, R.layout.spinner_dropdown_item, R.id.title);
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         String[] orientationLabels = getResources().getStringArray(
                 R.array.orientation_labels);
         mOrientationSpinnerAdapter.add(new SpinnerItem<>(
@@ -1008,8 +1008,8 @@
         mOrientationSpinner.setOnItemSelectedListener(itemSelectedListener);
 
         // Range options
-        ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
-                new ArrayAdapter<>(this, R.layout.spinner_dropdown_item, R.id.title);
+        ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter = new ArrayAdapter<>(
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
         mRangeOptionsSpinner.setAdapter(rangeOptionsSpinnerAdapter);
         mRangeOptionsSpinner.setOnItemSelectedListener(itemSelectedListener);
@@ -1075,6 +1075,7 @@
                 mDestinationSpinner.setEnabled(false);
             }
             mCopiesEditText.setEnabled(false);
+            mCopiesEditText.setFocusable(false);
             mMediaSizeSpinner.setEnabled(false);
             mColorModeSpinner.setEnabled(false);
             mOrientationSpinner.setEnabled(false);
@@ -1089,6 +1090,7 @@
         // available, we disable all print options except the destination.
         if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) {
             mCopiesEditText.setEnabled(false);
+            mCopiesEditText.setFocusable(false);
             mMediaSizeSpinner.setEnabled(false);
             mColorModeSpinner.setEnabled(false);
             mOrientationSpinner.setEnabled(false);
@@ -1316,8 +1318,10 @@
         // Copies
         if (mDestinationSpinnerAdapter.getPdfPrinter() != mCurrentPrinter) {
             mCopiesEditText.setEnabled(true);
+            mCopiesEditText.setFocusableInTouchMode(true);
         } else {
             mCopiesEditText.setEnabled(false);
+            mCopiesEditText.setFocusable(false);
         }
         if (mCopiesEditText.getError() == null
                 && TextUtils.isEmpty(mCopiesEditText.getText())) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java b/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
deleted file mode 100644
index d6bb7c8..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printspooler.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.EditText;
-
-/**
- * An instance of this class class is intended to be the first focusable
- * in a layout to which the system automatically gives focus. It performs
- * some voodoo to avoid the first tap on it to start an edit mode, rather
- * to bring up the IME, i.e. to get the behavior as if the view was not
- * focused.
- */
-public final class FirstFocusableEditText extends EditText {
-    private boolean mClickedBeforeFocus;
-    private CharSequence mError;
-
-    public FirstFocusableEditText(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public boolean performClick() {
-        super.performClick();
-        if (isFocused() && !mClickedBeforeFocus) {
-            clearFocus();
-            requestFocus();
-        }
-        mClickedBeforeFocus = true;
-        return true;
-    }
-
-    @Override
-    public CharSequence getError() {
-        return mError;
-    }
-
-    @Override
-    public void setError(CharSequence error, Drawable icon) {
-        setCompoundDrawables(null, null, icon, null);
-        mError = error;
-    }
-
-    protected void onFocusChanged(boolean gainFocus, int direction,
-            Rect previouslyFocusedRect) {
-        if (!gainFocus) {
-            mClickedBeforeFocus = false;
-        }
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-    }
-}
\ No newline at end of file
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index e428948..c84b06a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -152,6 +152,17 @@
 
         // Make sure we start in a closed options state.
         onDragProgress(1.0f);
+
+        // The framework gives focus to the frist focusable and we
+        // do not want that, hence we will take focus instead.
+        setFocusableInTouchMode(true);
+    }
+
+    @Override
+    public void focusableViewAvailable(View v) {
+        // The framework gives focus to the frist focusable and we
+        // do not want that, hence do not announce new focusables.
+        return;
     }
 
     @Override
@@ -309,6 +320,7 @@
             mSummaryContent.setLayerType(View.LAYER_TYPE_NONE, null);
             mDraggableContent.setLayerType(View.LAYER_TYPE_NONE, null);
             mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
+            mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
         }
 
         mDragProgress = progress;
@@ -320,7 +332,6 @@
         mMoreOptionsButton.setAlpha(inverseAlpha);
 
         mEmbeddedContentScrim.setBackgroundColor(computeScrimColor());
-
         if (progress == 0) {
             if (mOptionsStateChangeListener != null) {
                 mOptionsStateChangeListener.onOptionsOpened();
@@ -354,14 +365,15 @@
     }
 
     private void ensureImeClosedAndInputFocusCleared() {
-        View focus = findFocus();
-        if (focus != null) {
+        View focused = findFocus();
+
+        if (focused != null && focused.isFocused()) {
             InputMethodManager imm = (InputMethodManager) mContext.getSystemService(
                     Context.INPUT_METHOD_SERVICE);
-            if (imm.isActive(focus)) {
+            if (imm.isActive(focused)) {
                 imm.hideSoftInputFromWindow(getWindowToken(), 0);
             }
-            focus.clearFocus();
+            focused.clearFocus();
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 87c015c..4ef2189 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -318,10 +318,10 @@
         }
     }
 
-    private void checkUserRestrictions(String setting) {
+    private void checkUserRestrictions(String setting, int userId) {
         String userRestriction = sRestrictedKeys.get(setting);
         if (!TextUtils.isEmpty(userRestriction)
-            && mUserManager.hasUserRestriction(userRestriction)) {
+            && mUserManager.hasUserRestriction(userRestriction, new UserHandle(userId))) {
             throw new SecurityException(
                     "Permission denial: user is restricted from changing this setting.");
         }
@@ -936,7 +936,7 @@
         try {
             int numValues = values.length;
             for (int i = 0; i < numValues; i++) {
-                checkUserRestrictions(values[i].getAsString(Settings.Secure.NAME));
+                checkUserRestrictions(values[i].getAsString(Settings.Secure.NAME), callingUser);
                 if (db.insert(args.table, null, values[i]) < 0) return 0;
                 SettingsCache.populate(cache, values[i]);
                 if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + values[i]);
@@ -1067,7 +1067,7 @@
         // Check write permissions only after determining which table the insert will touch
         checkWritePermissions(args);
 
-        checkUserRestrictions(name);
+        checkUserRestrictions(name, desiredUserHandle);
 
         // The global table is stored under the owner, always
         if (TABLE_GLOBAL.equals(args.table)) {
@@ -1143,7 +1143,7 @@
             callingUser = UserHandle.USER_OWNER;
         }
         checkWritePermissions(args);
-        checkUserRestrictions(initialValues.getAsString(Settings.Secure.NAME));
+        checkUserRestrictions(initialValues.getAsString(Settings.Secure.NAME), callingUser);
 
         final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
         mutationCount.incrementAndGet();
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 3cd5f67..dd158c2 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -58,6 +58,9 @@
     <!-- Size of fading edge for scrolling -->
     <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
 
+    <!-- The radius of the rounded corners on a task view. -->
+    <dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
+
     <!-- Where to place the app icon over the thumbnail -->
     <dimen name="status_bar_recents_app_icon_left_margin">0dp</dimen>
     <dimen name="status_bar_recents_app_icon_top_margin">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 7ac6644..46d8a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -54,12 +54,7 @@
     @Override
     protected void handleClick() {
         final boolean wasEnabled = (Boolean) mState.value;
-        final boolean changed = mController.setLocationEnabled(!wasEnabled);
-        if (!wasEnabled && changed) {
-            // If we've successfully switched from location off to on, close the
-            // notifications tray to show the network location provider consent dialog.
-            mHost.collapsePanels();
-        }
+        mController.setLocationEnabled(!wasEnabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index d328660..ec39d77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -72,6 +72,7 @@
     static RecentsComponent.Callbacks sRecentsComponentCallbacks;
 
     Context mContext;
+    LayoutInflater mInflater;
     SystemServicesProxy mSystemServicesProxy;
     Handler mHandler;
     boolean mBootCompleted;
@@ -98,32 +99,20 @@
 
     public AlternateRecentsComponent(Context context) {
         RecentsTaskLoader.initialize(context);
-        Resources res = context.getResources();
+        mInflater = LayoutInflater.from(context);
         mContext = context;
         mSystemServicesProxy = new SystemServicesProxy(context);
         mHandler = new Handler();
-        mConfig = RecentsConfiguration.reinitialize(context, mSystemServicesProxy);
-        mWindowRect = mSystemServicesProxy.getWindowRect();
         mTaskStackBounds = new Rect();
-        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-        mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
-                mNavBarWidth, mTaskStackBounds);
-        if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
-            mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
-        } else {
-            mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
-        }
     }
 
-    public void onStart() {
-        // Initialize some static datastructures
-        TaskStackViewLayoutAlgorithm.initializeCurve();
-        reloadHeaderBarLayout();
-    }
+    public void onStart() {}
 
     public void onBootCompleted() {
+        // Initialize some static datastructures
+        TaskStackViewLayoutAlgorithm.initializeCurve();
+        // Load the header bar layout
+        reloadHeaderBarLayout();
         mBootCompleted = true;
     }
 
@@ -235,9 +224,19 @@
     }
 
     public void onConfigurationChanged(Configuration newConfig) {
+        reloadHeaderBarLayout();
+        sLastScreenshot = null;
+    }
+
+    /** Prepares the header bar layout. */
+    void reloadHeaderBarLayout() {
+        Resources res = mContext.getResources();
+        mWindowRect = mSystemServicesProxy.getWindowRect();
+        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
         mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
         mConfig.updateOnConfigurationChange();
-        mWindowRect = mSystemServicesProxy.getWindowRect();
         mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
                 mNavBarWidth, mTaskStackBounds);
         if (mConfig.isLandscape && mConfig.transposeRecentsLayoutWithOrientation) {
@@ -245,14 +244,8 @@
         } else {
             mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
         }
-        sLastScreenshot = null;
-        reloadHeaderBarLayout();
-    }
 
-    /** Prepares the header bar layout. */
-    void reloadHeaderBarLayout() {
         // Inflate the header bar layout so that we can rebind and draw it for the transition
-        Resources res = mContext.getResources();
         TaskStack stack = new TaskStack();
         mDummyStackView = new TaskStackView(mContext, stack);
         TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
@@ -261,8 +254,7 @@
         algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
         Rect taskViewSize = algo.getUntransformedTaskViewSize();
         int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header, null,
+        mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
                 false);
         mHeaderBar.measure(
                 View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
@@ -419,10 +411,6 @@
             return null;
         }
 
-        // Get the stack
-        mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
-        mDummyStackView.getScroller().setStackScrollToInitialState();
-
         // Find the running task in the TaskStack
         Task task = null;
         ArrayList<Task> tasks = stack.getTasks();
@@ -444,6 +432,8 @@
         }
 
         // Get the transform for the running task
+        mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
+        mDummyStackView.getScroller().setStackScrollToInitialState();
         mTmpTransform = mDummyStackView.getStackAlgorithm().getStackTransform(task,
                 mDummyStackView.getScroller().getStackScroll(), mTmpTransform, null);
         return mTmpTransform;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index a93e244..f7ad35b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
@@ -28,6 +27,8 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.UserHandle;
+import android.util.Log;
+
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -232,6 +233,8 @@
 /* Recents task loader
  * NOTE: We should not hold any references to a Context from a static instance */
 public class RecentsTaskLoader {
+    private static final String TAG = "RecentsTaskLoader";
+
     static RecentsTaskLoader sInstance;
 
     SystemServicesProxy mSystemServicesProxy;
@@ -335,11 +338,15 @@
                 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
                         taskKey.userId);
             }
-            icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
-            mApplicationIconCache.put(taskKey, icon);
-            return icon;
+            if (infoHandle.info != null) {
+                icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
+                if (icon != null) {
+                    mApplicationIconCache.put(taskKey, icon);
+                    return icon;
+                }
+            }
         }
-        // If we are not preloading, return the default icon to show
+        // If we couldn't load any icon, return null
         return null;
     }
 
@@ -361,8 +368,13 @@
             infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
                     taskKey.userId);
         }
-        label = ssp.getActivityLabel(infoHandle.info);
-        mActivityLabelCache.put(taskKey, label);
+        if (infoHandle.info != null) {
+            label = ssp.getActivityLabel(infoHandle.info);
+            mActivityLabelCache.put(taskKey, label);
+        } else {
+            Log.w(TAG, "Missing ActivityInfo for " + taskKey.baseIntent.getComponent()
+                    + " u=" + taskKey.userId);
+        }
         return label;
     }
 
@@ -401,8 +413,8 @@
             List<Task> tasksToLoadOut) {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
         List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
-        HashMap<ComponentName, ActivityInfoHandle> activityInfoCache =
-                new HashMap<ComponentName, ActivityInfoHandle>();
+        HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
+                new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
         ArrayList<Task> tasksToAdd = new ArrayList<Task>();
         TaskStack stack = new TaskStack();
 
@@ -410,19 +422,21 @@
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = tasks.get(i);
 
-            // Get an existing activity info handle if possible
-            ComponentName cn = t.baseIntent.getComponent();
-            ActivityInfoHandle infoHandle = new ActivityInfoHandle();
-            boolean hasCachedActivityInfo = false;
-            if (activityInfoCache.containsKey(cn)) {
-                infoHandle = activityInfoCache.get(cn);
-                hasCachedActivityInfo = true;
-            }
-
             // Compose the task key
             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
                     t.firstActiveTime, t.lastActiveTime);
 
+            // Get an existing activity info handle if possible
+            Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+            ActivityInfoHandle infoHandle;
+            boolean hasCachedActivityInfo = false;
+            if (activityInfoCache.containsKey(cnKey)) {
+                infoHandle = activityInfoCache.get(cnKey);
+                hasCachedActivityInfo = true;
+            } else {
+                infoHandle = new ActivityInfoHandle();
+            }
+
             // Determine whether to preload this task
             boolean preloadTask = false;
             if (preloadTaskId > 0) {
@@ -440,7 +454,7 @@
 
             // Update the activity info cache
             if (!hasCachedActivityInfo && infoHandle.info != null) {
-                activityInfoCache.put(cn, infoHandle);
+                activityInfoCache.put(cnKey, infoHandle);
             }
 
             // Add the task to the stack
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 977db60..406e03f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.recents.model;
 
+import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import com.android.systemui.recents.misc.Utilities;
 
+import java.util.Objects;
+
 
 /**
  * A task represents the top most task in the system's task stack.
@@ -35,8 +38,35 @@
         public void onTaskDataUnloaded();
     }
 
+    /** The ComponentNameKey represents the unique primary key for a component
+     * belonging to a specified user. */
+    public static class ComponentNameKey {
+        final ComponentName component;
+        final int userId;
+
+        public ComponentNameKey(ComponentName cn, int user) {
+            component = cn;
+            userId = user;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(component, userId);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof ComponentNameKey)) {
+                return false;
+            }
+            return component.equals(((ComponentNameKey) o).component) &&
+                    userId == ((ComponentNameKey) o).userId;
+        }
+    }
+
     /* The Task Key represents the unique primary key for the task */
     public static class TaskKey {
+        final ComponentNameKey mComponentNameKey;
         public final int id;
         public final Intent baseIntent;
         public final int userId;
@@ -44,6 +74,7 @@
         public long lastActiveTime;
 
         public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
+            mComponentNameKey = new ComponentNameKey(intent.getComponent(), userId);
             this.id = id;
             this.baseIntent = intent;
             this.userId = userId;
@@ -51,6 +82,11 @@
             this.lastActiveTime = lastActiveTime;
         }
 
+        /** Returns the component name key for this task. */
+        public ComponentNameKey getComponentNameKey() {
+            return mComponentNameKey;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof TaskKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index fb13126..9da209a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -45,6 +45,7 @@
     private boolean mIsAirplaneMode = false;
     private int mAirplaneIconId = 0;
     private String mWifiDescription, mMobileDescription, mMobileTypeDescription;
+    private boolean mRoaming;
 
     ViewGroup mWifiGroup, mMobileGroup;
     ImageView mWifi, mMobile, mMobileType, mAirplane;
@@ -106,12 +107,13 @@
 
     @Override
     public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
-            String contentDescription, String typeContentDescription) {
+            String contentDescription, String typeContentDescription, boolean roaming) {
         mMobileVisible = visible;
         mMobileStrengthId = strengthIcon;
         mMobileTypeId = typeIcon;
         mMobileDescription = contentDescription;
         mMobileTypeDescription = typeContentDescription;
+        mRoaming = roaming;
 
         apply();
     }
@@ -207,8 +209,7 @@
                     (mMobileVisible ? "VISIBLE" : "GONE"),
                     mMobileStrengthId, mMobileTypeId));
 
-        mMobileType.setVisibility(
-                !mWifiVisible ? View.VISIBLE : View.GONE);
+        mMobileType.setVisibility(!mWifiVisible || mRoaming ? View.VISIBLE : View.GONE);
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 15a7047..9b59814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -64,6 +64,7 @@
     static final boolean CHATTY = false; // additional diagnostics, but not logspew
 
     private static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_signal_flightmode;
+    private static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
 
     // telephony
     boolean mHspaDataDistinguishable;
@@ -164,7 +165,7 @@
     public interface SignalCluster {
         void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
         void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
-                String contentDescription, String typeContentDescription);
+                String contentDescription, String typeContentDescription, boolean roaming);
         void setIsAirplaneMode(boolean is, int airplaneIcon);
     }
 
@@ -372,7 +373,8 @@
                     mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
                     mDataTypeIconId,
                     mContentDescriptionWimax,
-                    mContentDescriptionDataType);
+                    mContentDescriptionDataType,
+                    mDataTypeIconId == ROAMING_ICON);
         } else {
             // normal mobile data
             cluster.setMobileDataIndicators(
@@ -380,7 +382,8 @@
                     mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
                     mDataTypeIconId,
                     mContentDescriptionPhoneSignal,
-                    mContentDescriptionDataType);
+                    mContentDescriptionDataType,
+                    mDataTypeIconId == ROAMING_ICON);
         }
         cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId);
     }
@@ -777,11 +780,11 @@
 
         if (isCdma()) {
             if (isCdmaEri()) {
-                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                mDataTypeIconId = ROAMING_ICON;
                 mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
             }
         } else if (mPhone.isNetworkRoaming()) {
-                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                mDataTypeIconId = ROAMING_ICON;
                 mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
         }
     }
@@ -1195,11 +1198,11 @@
             mQSDataTypeIconId = 0;
             if (isCdma()) {
                 if (isCdmaEri()) {
-                    mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                    mDataTypeIconId = ROAMING_ICON;
                     mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
                 }
             } else if (mPhone.isNetworkRoaming()) {
-                mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_roam;
+                mDataTypeIconId = ROAMING_ICON;
                 mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
             }
         }
@@ -1544,8 +1547,7 @@
                             datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g :
                             datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h :
                             datatype.equals("lte") ? R.drawable.stat_sys_data_fully_connected_lte :
-                            datatype.equals("roam")
-                                    ? R.drawable.stat_sys_data_fully_connected_roam :
+                            datatype.equals("roam") ? ROAMING_ICON :
                             0;
                     mDemoQSDataTypeIconId =
                             datatype.equals("1x") ? R.drawable.ic_qs_signal_1x :
@@ -1572,7 +1574,8 @@
                             iconId,
                             mDemoDataTypeIconId,
                             "Demo",
-                            "Demo");
+                            "Demo",
+                            mDemoDataTypeIconId == ROAMING_ICON);
                 }
                 refreshViews();
             }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 107bb06..0919f77 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3559,7 +3559,7 @@
             // Check for  apps that can handle provisioning first
             Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
             List<String> carrierPackages =
-                    mTelephonyManager.getCarrierPackageNamesForBroadcastIntent(provisioningIntent);
+                    mTelephonyManager.getCarrierPackageNamesForIntent(provisioningIntent);
             if (carrierPackages != null && !carrierPackages.isEmpty()) {
                 if (carrierPackages.size() != 1) {
                     if (DBG) log("Multiple matching carrier apps found, launching the first.");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6ca536c..6310764 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1198,11 +1198,12 @@
      */
     private boolean mUserIsMonkey;
 
-    /** Flag whether the device has a recents UI */
-    final boolean mHasRecents;
+    /** Flag whether the device has a Recents UI */
+    boolean mHasRecents;
 
-    final int mThumbnailWidth;
-    final int mThumbnailHeight;
+    /** The dimensions of the thumbnails in the Recents UI. */
+    int mThumbnailWidth;
+    int mThumbnailHeight;
 
     final ServiceThread mHandlerThread;
     final MainHandler mHandler;
@@ -2257,11 +2258,6 @@
         mConfigurationSeq = mConfiguration.seq = 1;
         mProcessCpuTracker.init();
 
-        final Resources res = mContext.getResources();
-        mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
-        mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
-        mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
-
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
         mStackSupervisor = new ActivityStackSupervisor(this);
@@ -8952,6 +8948,14 @@
         return false;
     }
 
+    private void checkTime(long startTime, String where) {
+        long now = SystemClock.elapsedRealtime();
+        if ((now-startTime) > 1000) {
+            // If we are taking more than a second, log about it.
+            Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
+        }
+    }
+
     private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
             String name, IBinder token, boolean stable, int userId) {
         ContentProviderRecord cpr;
@@ -8959,6 +8963,8 @@
         ProviderInfo cpi = null;
 
         synchronized(this) {
+            long startTime = SystemClock.elapsedRealtime();
+
             ProcessRecord r = null;
             if (caller != null) {
                 r = getRecordForAppLocked(caller);
@@ -8972,6 +8978,8 @@
 
             boolean checkCrossUser = true;
 
+            checkTime(startTime, "getContentProviderImpl: getProviderByName");
+
             // First check if this content provider has been published...
             cpr = mProviderMap.getProviderByName(name, userId);
             // If that didn't work, check if it exists for user 0 and then
@@ -8996,10 +9004,12 @@
             if (providerRunning) {
                 cpi = cpr.info;
                 String msg;
+                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                 if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                         != null) {
                     throw new SecurityException(msg);
                 }
+                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
 
                 if (r != null && cpr.canRunHere(r)) {
                     // This provider has been published or is in the process
@@ -9015,6 +9025,8 @@
 
                 final long origId = Binder.clearCallingIdentity();
 
+                checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
+
                 // In this case the provider instance already exists, so we can
                 // return it right away.
                 conn = incProviderCountLocked(r, cpr, token, stable);
@@ -9024,7 +9036,9 @@
                         // make sure to count it as being accessed and thus
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
+                        checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
                         updateLruProcessLocked(cpr.proc, false, null);
+                        checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
                     }
                 }
 
@@ -9037,7 +9051,9 @@
                             Process.killProcess(cpr.proc.pid);
                         }
                     }
+                    checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                     boolean success = updateOomAdjLocked(cpr.proc);
+                    checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
                     if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);
                     // NOTE: there is still a race here where a signal could be
                     // pending on the process even though we managed to update its
@@ -9052,7 +9068,9 @@
                                 "Existing provider " + cpr.name.flattenToShortString()
                                 + " is crashing; detaching " + r);
                         boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+                        checkTime(startTime, "getContentProviderImpl: before appDied");
                         appDiedLocked(cpr.proc);
+                        checkTime(startTime, "getContentProviderImpl: after appDied");
                         if (!lastRef) {
                             // This wasn't the last ref our process had on
                             // the provider...  we have now been killed, bail.
@@ -10680,6 +10698,14 @@
         }
     }
 
+    /** Loads resources after the current configuration has been set. */
+    private void loadResourcesOnSystemReady() {
+        final Resources res = mContext.getResources();
+        mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
+        mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+        mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+    }
+
     public boolean testIsSystemReady() {
         // no need to synchronize(this) just to read & return the value
         return mSystemReady;
@@ -10961,6 +10987,7 @@
         }
 
         retrieveSettings();
+        loadResourcesOnSystemReady();
 
         synchronized (this) {
             readGrantedUriPermissionsLocked();
@@ -15208,6 +15235,10 @@
             final int is24Hour = intent.getBooleanExtra(
                     Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1 : 0;
             mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
+            BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+            synchronized (stats) {
+                stats.noteCurrentTimeChangedLocked();
+            }
         }
 
         if (Intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d066940..3efd049 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -229,9 +229,6 @@
     private ActivityRecord mLastScreenshotActivity = null;
     private Bitmap mLastScreenshotBitmap = null;
 
-    int mThumbnailWidth = -1;
-    int mThumbnailHeight = -1;
-
     int mCurrentUser;
 
     final int mStackId;
@@ -355,10 +352,6 @@
         mWindowManager = mService.mWindowManager;
         mStackId = activityContainer.mStackId;
         mCurrentUser = mService.mCurrentUserId;
-        // Get the activity screenshot thumbnail dimensions
-        Resources res = mService.mContext.getResources();
-        mThumbnailWidth = mService.mThumbnailWidth;
-        mThumbnailHeight = mService.mThumbnailHeight;
     }
 
     /**
@@ -773,8 +766,8 @@
             return null;
         }
 
-        int w = mThumbnailWidth;
-        int h = mThumbnailHeight;
+        int w = mService.mThumbnailWidth;
+        int h = mService.mThumbnailHeight;
         if (w > 0) {
             if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
                     || mLastScreenshotActivity.state == ActivityState.RESUMED
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 9b32b65..1d2f7a9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1543,6 +1543,15 @@
         final Intent intent = r.intent;
         final int callingUid = r.launchedFromUid;
 
+        // In some flows in to this function, we retrieve the task record and hold on to it
+        // without a lock before calling back in to here...  so the task at this point may
+        // not actually be in recents.  Check for that, and if it isn't in recents just
+        // consider it invalid.
+        if (inTask != null && !inTask.inRecents) {
+            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
+            inTask = null;
+        }
+
         final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
         final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
         final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -1686,32 +1695,50 @@
         // If the caller is not coming from another activity, but has given us an
         // explicit task into which they would like us to launch the new activity,
         // then let's see about doing that.
-        if (sourceRecord == null && inTask != null && inTask.stack != null && inTask.inRecents) {
+        if (sourceRecord == null && inTask != null && inTask.stack != null) {
+            final Intent baseIntent = inTask.getBaseIntent();
+            final ActivityRecord root = inTask.getRootActivity();
+            if (baseIntent == null) {
+                ActivityOptions.abort(options);
+                throw new IllegalArgumentException("Launching into task without base intent: "
+                        + inTask);
+            }
+
             // If this task is empty, then we are adding the first activity -- it
             // determines the root, and must be launching as a NEW_TASK.
-            if (inTask.getRootActivity() == null) {
-                if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
-                        && !launchSingleInstance && !launchSingleTask) {
-                    throw new IllegalStateException("Caller has inTask " + inTask
-                            + " but target is not a new task");
-                } else if (inTask.getBaseIntent() == null || !intent.getComponent().equals(
-                        inTask.getBaseIntent().getComponent())) {
-                    throw new IllegalStateException("Caller requested " + inTask + " is component "
-                            + inTask.getBaseIntent() + " but starting " + intent);
+            if (launchSingleInstance || launchSingleTask) {
+                if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
+                    ActivityOptions.abort(options);
+                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+                            + r + " into different task " + inTask);
                 }
+                if (root != null) {
+                    ActivityOptions.abort(options);
+                    throw new IllegalArgumentException("Caller with inTask " + inTask
+                            + " has root " + root + " but target is singleInstance/Task");
+                }
+            }
+
+            // If task is empty, then adopt the interesting intent launch flags in to the
+            // activity being started.
+            if (root == null) {
+                final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+                        | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+                launchFlags = (launchFlags&~flagsOfInterest)
+                        | (baseIntent.getFlags()&flagsOfInterest);
+                intent.setFlags(launchFlags);
                 inTask.setIntent(r);
 
             // If the task is not empty, then we are going to add the new activity on top
             // of the task, so it can not be launching as a new task.
-            } else {
-                if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
-                        || launchSingleInstance || launchSingleTask) {
-                    throw new IllegalStateException("Caller has inTask " + inTask
-                            + " but target is a new task");
-                }
+            } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+                ActivityOptions.abort(options);
+                throw new IllegalStateException("Caller has inTask " + inTask
+                        + " but target is a new task");
             }
-            sourceStack = inTask.stack;
             reuseTask = inTask;
+            addingToTask = true;
         } else {
             inTask = null;
         }
@@ -1724,10 +1751,11 @@
         if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                 (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                 || launchSingleInstance || launchSingleTask) {
-            // If bring to front is requested, and no result is requested, and
+            // If bring to front is requested, and no result is requested and we have not
+            // been given an explicit task to launch in to, and
             // we can find a task that was started with this same
             // component, then instead of launching bring that one to the front.
-            if (r.resultTo == null) {
+            if (inTask == null && r.resultTo == null) {
                 // See if there is a task to bring to the front.  If this is
                 // a SINGLE_INSTANCE activity, there can be one and only one
                 // instance of it in the history, and it is always in its own
@@ -1957,13 +1985,8 @@
                 Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
-            if (inTask == null) {
-                // If we have an incoming task, we are just going to use that.
-                newTask = true;
-                targetStack = adjustStackFocus(r, newTask);
-            } else {
-                targetStack = inTask.stack;
-            }
+            newTask = true;
+            targetStack = adjustStackFocus(r, newTask);
             if (!launchTaskBehind) {
                 targetStack.moveToFront();
             }
@@ -2048,8 +2071,27 @@
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
             targetStack = inTask.stack;
-            targetStack.moveToFront();
+            targetStack.moveTaskToFrontLocked(inTask, r, options);
             mWindowManager.moveTaskToTop(targetStack.topTask().taskId);
+
+            // Check whether we should actually launch the new activity in to the task,
+            // or just reuse the current activity on top.
+            ActivityRecord top = inTask.getTopActivity();
+            if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
+                if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+                        || launchSingleTop || launchSingleTask) {
+                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+                    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+                        // We don't need to start a new activity, and
+                        // the client said not to do anything if that
+                        // is the case, so this is it!
+                        return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+                    }
+                    top.deliverNewIntentLocked(callingUid, r.intent);
+                    return ActivityManager.START_DELIVERED_TO_TOP;
+                }
+            }
+
             r.setTask(inTask, null);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in explicit task " + r.task);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 77c324f..1287dce 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -21,6 +21,7 @@
 import java.nio.ByteBuffer;
 
 import android.app.ActivityManager;
+import android.os.SystemClock;
 import com.android.internal.util.MemInfoReader;
 import com.android.server.wm.WindowManagerService;
 
@@ -528,12 +529,18 @@
         if (amt == UNKNOWN_ADJ)
             return;
 
+        long start = SystemClock.elapsedRealtime();
         ByteBuffer buf = ByteBuffer.allocate(4 * 4);
         buf.putInt(LMK_PROCPRIO);
         buf.putInt(pid);
         buf.putInt(uid);
         buf.putInt(amt);
         writeLmkd(buf);
+        long now = SystemClock.elapsedRealtime();
+        if ((now-start) > 250) {
+            Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+                    + " = " + amt);
+        }
     }
 
     /*
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 978a9f4..0da2cfa 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -23,6 +23,7 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
+import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -441,7 +442,7 @@
 
     private String getShortMetadataString() {
         int fields = mMetadata == null ? 0 : mMetadata.size();
-        MediaMetadata.Description description = mMetadata == null ? null : mMetadata
+        MediaDescription description = mMetadata == null ? null : mMetadata
                 .getDescription();
         return "size=" + fields + ", description=" + description;
     }
@@ -820,9 +821,9 @@
             }
         }
 
-        public void playUri(Uri uri, Bundle extras) {
+        public void playFromMediaId(String mediaId, Bundle extras) {
             try {
-                mCb.onPlayUri(uri, extras);
+                mCb.onPlayFromMediaId(mediaId, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in playUri.", e);
             }
@@ -1042,8 +1043,8 @@
         }
 
         @Override
-        public void playUri(Uri uri, Bundle extras) throws RemoteException {
-            mSessionCb.playUri(uri, extras);
+        public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
+            mSessionCb.playFromMediaId(mediaId, extras);
         }
 
         @Override
@@ -1052,7 +1053,7 @@
         }
 
         @Override
-        public void skipToTrack(long id) {
+        public void skipToQueueItem(long id) {
             mSessionCb.skipToTrack(id);
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8c0d2c9..fc1b746 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -57,7 +57,6 @@
 import android.os.IInterface;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -127,6 +126,7 @@
     static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
     static final int MESSAGE_SEND_RANKING_UPDATE = 6;
     static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
+    static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
 
     static final int LONG_DELAY = 3500; // 3.5 seconds
     static final int SHORT_DELAY = 2000; // 2 seconds
@@ -178,6 +178,7 @@
     private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
     private ComponentName mEffectsSuppressor;
     private int mListenerHints;  // right now, all hints are global
+    private int mInterruptionFilter;  // current ZEN mode as communicated to listeners
 
     // for enabling and disabling notification pulse behavior
     private boolean mScreenOn = true;
@@ -806,7 +807,7 @@
             @Override
             void onZenModeChanged() {
                 synchronized(mNotificationList) {
-                    updateListenerHintsLocked();
+                    updateInterruptionFilterLocked();
                 }
             }
         });
@@ -938,8 +939,7 @@
     }
 
     private void updateListenerHintsLocked() {
-        final int hints = (mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS) |
-                mZenModeHelper.getZenModeListenerHint();
+        final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
         if (hints == mListenerHints) return;
         mListenerHints = hints;
         scheduleListenerHintsChanged(hints);
@@ -954,6 +954,13 @@
                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
     }
 
+    private void updateInterruptionFilterLocked() {
+        int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
+        if (interruptionFilter == mInterruptionFilter) return;
+        mInterruptionFilter = interruptionFilter;
+        scheduleInterruptionFilterChanged(interruptionFilter);
+    }
+
     private final IBinder mService = new INotificationManager.Stub() {
         // Toasts
         // ============================================================================
@@ -1318,7 +1325,6 @@
                     } else {
                         mListenersDisablingEffects.remove(info);
                     }
-                    mZenModeHelper.requestFromListener(hints);
                     updateListenerHintsLocked();
                     updateEffectsSuppressorLocked();
                 }
@@ -1335,6 +1341,29 @@
         }
 
         @Override
+        public void requestInterruptionFilterFromListener(INotificationListener token,
+                int interruptionFilter) throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationList) {
+                    mListeners.checkServiceTokenLocked(token);
+                    mZenModeHelper.requestFromListener(interruptionFilter);
+                    updateInterruptionFilterLocked();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public int getInterruptionFilterFromListener(INotificationListener token)
+                throws RemoteException {
+            synchronized (mNotificationLight) {
+                return mInterruptionFilter;
+            }
+        }
+
+        @Override
         public ZenModeConfig getZenModeConfig() {
             enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
             return mZenModeHelper.getConfig();
@@ -2058,12 +2087,26 @@
         mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
     }
 
+    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
+        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+        mHandler.obtainMessage(
+                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
+                listenerInterruptionFilter,
+                0).sendToTarget();
+    }
+
     private void handleListenerHintsChanged(int hints) {
         synchronized (mNotificationList) {
             mListeners.notifyListenerHintsChangedLocked(hints);
         }
     }
 
+    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
+        synchronized (mNotificationList) {
+            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
+        }
+    }
+
     private final class WorkerHandler extends Handler
     {
         @Override
@@ -2083,6 +2126,9 @@
                 case MESSAGE_LISTENER_HINTS_CHANGED:
                     handleListenerHintsChanged(msg.arg1);
                     break;
+                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
+                    handleListenerInterruptionFilterChanged(msg.arg1);
+                    break;
             }
         }
 
@@ -2701,6 +2747,20 @@
             }
         }
 
+        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
+            for (final ManagedServiceInfo serviceInfo : mServices) {
+                if (!serviceInfo.isEnabledForCurrentProfiles()) {
+                    continue;
+                }
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
+                    }
+                });
+            }
+        }
+
         private void notifyPosted(final ManagedServiceInfo info,
                 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
             final INotificationListener listener = (INotificationListener)info.service;
@@ -2743,6 +2803,16 @@
             }
         }
 
+        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
+                int interruptionFilter) {
+            final INotificationListener listener = (INotificationListener) info.service;
+            try {
+                listener.onInterruptionFilterChanged(interruptionFilter);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
+            }
+        }
+
         private boolean isListenerPackage(String packageName) {
             if (packageName == null) {
                 return false;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 0b93690..7a5336b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -115,35 +115,35 @@
         mAudioManager = audioManager;
     }
 
-    public int getZenModeListenerHint() {
-        switch(mZenMode) {
+    public int getZenModeListenerInterruptionFilter() {
+        switch (mZenMode) {
             case Global.ZEN_MODE_OFF:
-                return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_ALL;
+                return NotificationListenerService.INTERRUPTION_FILTER_ALL;
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_PRIORITY;
+                return NotificationListenerService.INTERRUPTION_FILTER_PRIORITY;
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
-                return NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_NONE;
+                return NotificationListenerService.INTERRUPTION_FILTER_NONE;
             default:
                 return 0;
         }
     }
 
-    private static int zenFromListenerHint(int hints, int defValue) {
-        final int level = hints & NotificationListenerService.HOST_INTERRUPTION_LEVEL_MASK;
-        switch(level) {
-            case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_ALL:
+    private static int zenModeFromListenerInterruptionFilter(int listenerInterruptionFilter,
+            int defValue) {
+        switch (listenerInterruptionFilter) {
+            case NotificationListenerService.INTERRUPTION_FILTER_ALL:
                 return Global.ZEN_MODE_OFF;
-            case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_PRIORITY:
+            case NotificationListenerService.INTERRUPTION_FILTER_PRIORITY:
                 return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-            case NotificationListenerService.HINT_HOST_INTERRUPTION_LEVEL_NONE:
+            case NotificationListenerService.INTERRUPTION_FILTER_NONE:
                 return Global.ZEN_MODE_NO_INTERRUPTIONS;
             default:
                 return defValue;
         }
     }
 
-    public void requestFromListener(int hints) {
-        final int newZen = zenFromListenerHint(hints, -1);
+    public void requestFromListener(int interruptionFilter) {
+        final int newZen = zenModeFromListenerInterruptionFilter(interruptionFilter, -1);
         if (newZen != -1) {
             setZenMode(newZen, "listener");
         }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 694669c..ca11862 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -224,10 +224,6 @@
         }
     }
 
-    public int pruneDexCache(String cacheSubDir) {
-        return mInstaller.execute("prunedexcache " + cacheSubDir);
-    }
-
     public int freeCache(long freeStorageSize) {
         StringBuilder builder = new StringBuilder("freecache");
         builder.append(' ');
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89878ef..2cb9077 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1497,29 +1497,6 @@
                 }
             }
 
-            if (didDexOptLibraryOrTool) {
-                // If we dexopted a library or tool, then something on the system has
-                // changed. Consider this significant, and wipe away all other
-                // existing dexopt files to ensure we don't leave any dangling around.
-                //
-                // TODO: This should be revisited because it isn't as good an indicator
-                // as it used to be. It used to include the boot classpath but at some point
-                // DexFile.isDexOptNeeded started returning false for the boot
-                // class path files in all cases. It is very possible in a
-                // small maintenance release update that the library and tool
-                // jars may be unchanged but APK could be removed resulting in
-                // unused dalvik-cache files.
-                for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-                    mInstaller.pruneDexCache(dexCodeInstructionSet);
-                }
-
-                // Additionally, delete all dex files from the root directory
-                // since there shouldn't be any there anyway, unless we're upgrading
-                // from an older OS version or a build that contained the "old" style
-                // flat scheme.
-                mInstaller.pruneDexCache(".");
-            }
-
             // Collect vendor overlay packages.
             // (Do this before scanning any apps.)
             // For security and version matching reason, only consider
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a49d8ab..d22912c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -119,7 +119,7 @@
 
         mContext = context;
         mContentResolver = context.getContentResolver();
-        mWatchLogHandler = new WatchLogHandler(IoThread.get().getLooper());
+        mWatchLogHandler = new WatchLogHandler(mContentResolver, IoThread.get().getLooper());
 
         mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
 
@@ -248,7 +248,7 @@
                     serviceState = new ServiceState(component, userId);
                     userState.serviceStateMap.put(component, serviceState);
                 } else {
-                    inputList.addAll(serviceState.mInputList);
+                    inputList.addAll(serviceState.inputList);
                 }
             } else {
                 try {
@@ -258,9 +258,6 @@
                     continue;
                 }
             }
-
-            // Reconnect the service if existing input is updated.
-            updateServiceConnectionLocked(component, userId);
             userState.packageSet.add(si.packageName);
         }
 
@@ -273,7 +270,7 @@
             if (state == null) {
                 state = new TvInputState();
             }
-            state.mInfo = info;
+            state.info = info;
             inputMap.put(info.getId(), state);
         }
 
@@ -285,7 +282,7 @@
 
         for (String inputId : userState.inputMap.keySet()) {
             if (!inputMap.containsKey(inputId)) {
-                TvInputInfo info = userState.inputMap.get(inputId).mInfo;
+                TvInputInfo info = userState.inputMap.get(inputId).info;
                 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                 if (serviceState != null) {
                     abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
@@ -352,9 +349,9 @@
             }
             // Release created sessions.
             for (SessionState state : userState.sessionStateMap.values()) {
-                if (state.mSession != null) {
+                if (state.session != null) {
                     try {
-                        state.mSession.release();
+                        state.session.release();
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in release", e);
                     }
@@ -364,14 +361,14 @@
 
             // Unregister all callbacks and unbind all services.
             for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                if (serviceState.mCallback != null) {
+                if (serviceState.callback != null) {
                     try {
-                        serviceState.mService.unregisterCallback(serviceState.mCallback);
+                        serviceState.service.unregisterCallback(serviceState.callback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in unregisterCallback", e);
                     }
                 }
-                mContext.unbindService(serviceState.mConnection);
+                mContext.unbindService(serviceState.connection);
             }
             userState.serviceStateMap.clear();
 
@@ -412,7 +409,7 @@
             throw new IllegalArgumentException("Session state not found for token " + sessionToken);
         }
         // Only the application that requested this session or the system can access it.
-        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
+        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
             throw new SecurityException("Illegal access to the session with token " + sessionToken
                     + " from uid " + callingUid);
         }
@@ -424,10 +421,10 @@
     }
 
     private ITvInputSession getSessionLocked(SessionState sessionState) {
-        ITvInputSession session = sessionState.mSession;
+        ITvInputSession session = sessionState.session;
         if (session == null) {
             throw new IllegalStateException("Session not yet created for token "
-                    + sessionState.mSessionToken);
+                    + sessionState.sessionToken);
         }
         return session;
     }
@@ -439,8 +436,8 @@
     }
 
     private static boolean shouldMaintainConnection(ServiceState serviceState) {
-        return !serviceState.mSessionTokens.isEmpty() || serviceState.mIsHardware;
-        // TODO: Find a way to maintain connection only when necessary.
+        return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
+        // TODO: Find a way to maintain connection to hardware TV input service only when necessary.
     }
 
     private void updateServiceConnectionLocked(ComponentName component, int userId) {
@@ -449,18 +446,18 @@
         if (serviceState == null) {
             return;
         }
-        if (serviceState.mReconnecting) {
-            if (!serviceState.mSessionTokens.isEmpty()) {
+        if (serviceState.reconnecting) {
+            if (!serviceState.sessionTokens.isEmpty()) {
                 // wait until all the sessions are removed.
                 return;
             }
-            serviceState.mReconnecting = false;
+            serviceState.reconnecting = false;
         }
         boolean maintainConnection = shouldMaintainConnection(serviceState);
-        if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) {
+        if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) {
             // This means that the service is not yet connected but its state indicates that we
             // have pending requests. Then, connect the service.
-            if (serviceState.mBound) {
+            if (serviceState.bound) {
                 // We have already bound to the service so we don't try to bind again until after we
                 // unbind later on.
                 return;
@@ -470,18 +467,15 @@
             }
 
             Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
-            // Binding service may fail if the service is updating.
-            // In that case, the connection will be revived in buildTvInputListLocked called by
-            // onSomePackagesChanged.
-            serviceState.mBound = mContext.bindServiceAsUser(
-                    i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
-        } else if (serviceState.mService != null && !maintainConnection) {
+            serviceState.bound = mContext.bindServiceAsUser(
+                    i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
+        } else if (serviceState.service != null && !maintainConnection) {
             // This means that the service is already connected but its state indicates that we have
             // nothing to do with it. Then, disconnect the service.
             if (DEBUG) {
                 Slog.d(TAG, "unbindService(service=" + component + ")");
             }
-            mContext.unbindService(serviceState.mConnection);
+            mContext.unbindService(serviceState.connection);
             userState.serviceStateMap.remove(component);
         }
     }
@@ -491,19 +485,19 @@
         // Let clients know the create session requests are failed.
         UserState userState = getUserStateLocked(userId);
         List<SessionState> sessionsToAbort = new ArrayList<>();
-        for (IBinder sessionToken : serviceState.mSessionTokens) {
+        for (IBinder sessionToken : serviceState.sessionTokens) {
             SessionState sessionState = userState.sessionStateMap.get(sessionToken);
-            if (sessionState.mSession == null && (inputId == null
-                    || sessionState.mInfo.getId().equals(inputId))) {
+            if (sessionState.session == null && (inputId == null
+                    || sessionState.info.getId().equals(inputId))) {
                 sessionsToAbort.add(sessionState);
             }
         }
         for (SessionState sessionState : sessionsToAbort) {
-            removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
-            sendSessionTokenToClientLocked(sessionState.mClient,
-                    sessionState.mInfo.getId(), null, null, sessionState.mSeq);
+            removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+            sendSessionTokenToClientLocked(sessionState.client,
+                    sessionState.info.getId(), null, null, sessionState.seq);
         }
-        updateServiceConnectionLocked(serviceState.mComponent, userId);
+        updateServiceConnectionLocked(serviceState.component, userId);
     }
 
     private ClientState createClientStateLocked(IBinder clientToken, int userId) {
@@ -518,221 +512,26 @@
         return clientState;
     }
 
-    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
-            final int userId) {
-        final UserState userState = getUserStateLocked(userId);
-        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+    private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+            int userId) {
+        UserState userState = getUserStateLocked(userId);
+        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")");
+            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
         }
-
-        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
+        InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
 
         // Set up a callback to send the session token.
-        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
-            @Override
-            public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
-                if (DEBUG) {
-                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
-                }
-                synchronized (mLock) {
-                    sessionState.mSession = session;
-                    sessionState.mHardwareSessionToken = harewareSessionToken;
-                    if (session == null) {
-                        removeSessionStateLocked(sessionToken, userId);
-                        sendSessionTokenToClientLocked(sessionState.mClient,
-                                sessionState.mInfo.getId(), null, null, sessionState.mSeq);
-                    } else {
-                        try {
-                            session.asBinder().linkToDeath(sessionState, 0);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "session process has already died", e);
-                        }
-
-                        IBinder clientToken = sessionState.mClient.asBinder();
-                        ClientState clientState = userState.clientStateMap.get(clientToken);
-                        if (clientState == null) {
-                            clientState = createClientStateLocked(clientToken, userId);
-                        }
-                        clientState.mSessionTokens.add(sessionState.mSessionToken);
-
-                        sendSessionTokenToClientLocked(sessionState.mClient,
-                                sessionState.mInfo.getId(), sessionToken, channels[0],
-                                sessionState.mSeq);
-                    }
-                    channels[0].dispose();
-                }
-            }
-
-            @Override
-            public void onChannelRetuned(Uri channelUri) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        // TODO: Consider adding this channel change in the watch log. When we do
-                        // that, how we can protect the watch log from malicious tv inputs should
-                        // be addressed. e.g. add a field which represents where the channel change
-                        // originated from.
-                        sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onChannelRetuned", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onTracksChanged(List<TvTrackInfo> tracks) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onTracksChanged(" + tracks + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onTracksChanged(tracks, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onTracksChanged", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onTrackSelected(int type, String trackId) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onTrackSelected(type, trackId, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onTrackSelected", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onVideoAvailable() {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onVideoAvailable()");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onVideoAvailable(sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onVideoAvailable", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onVideoUnavailable(int reason) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onVideoUnavailable", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onContentAllowed() {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onContentAllowed()");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onContentAllowed(sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onContentAllowed", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onContentBlocked(String rating) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onContentBlocked()");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onContentBlocked(rating, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onContentBlocked", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onLayoutSurface(int left, int top, int right, int bottom) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
-                                + ", right=" + right + ", bottom=" + bottom + ",)");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onLayoutSurface(left, top, right, bottom,
-                                sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onLayoutSurface", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onSessionEvent(String eventType, Bundle eventArgs) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
-                                sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onSessionEvent", e);
-                    }
-                }
-            }
-        };
+        ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
 
         // Create a session. When failed, send a null token immediately.
         try {
-            service.createSession(channels[1], callback, sessionState.mInfo.getId());
+            service.createSession(channels[1], callback, sessionState.info.getId());
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
-            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null,
-                    null, sessionState.mSeq);
+            sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null,
+                    null, sessionState.seq);
         }
         channels[1].dispose();
     }
@@ -748,17 +547,17 @@
 
     private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-        if (sessionState.mSession != null) {
+        if (sessionState.session != null) {
             UserState userState = getUserStateLocked(userId);
             if (sessionToken == userState.mainSessionToken) {
                 setMainLocked(sessionToken, false, callingUid, userId);
             }
             try {
-                sessionState.mSession.release();
+                sessionState.session.release();
             } catch (RemoteException e) {
                 Slog.e(TAG, "session process has already died", e);
             }
-            sessionState.mSession = null;
+            sessionState.session = null;
         }
         removeSessionStateLocked(sessionToken, userId);
     }
@@ -781,22 +580,22 @@
 
         // Also remove the session token from the session token list of the current client and
         // service.
-        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
+        ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
         if (clientState != null) {
-            clientState.mSessionTokens.remove(sessionToken);
+            clientState.sessionTokens.remove(sessionToken);
             if (clientState.isEmpty()) {
-                userState.clientStateMap.remove(sessionState.mClient.asBinder());
+                userState.clientStateMap.remove(sessionState.client.asBinder());
             }
         }
 
-        TvInputInfo info = sessionState.mInfo;
+        TvInputInfo info = sessionState.info;
         if (info != null) {
             ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
             if (serviceState != null) {
-                serviceState.mSessionTokens.remove(sessionToken);
+                serviceState.sessionTokens.remove(sessionToken);
             }
         }
-        updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
+        updateServiceConnectionLocked(sessionState.info.getComponent(), userId);
 
         // Log the end of watch.
         SomeArgs args = SomeArgs.obtain();
@@ -807,13 +606,12 @@
 
     private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-        if (sessionState.mHardwareSessionToken != null) {
-            sessionState = getSessionStateLocked(sessionState.mHardwareSessionToken,
+        if (sessionState.hardwareSessionToken != null) {
+            sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
                     Process.SYSTEM_UID, userId);
         }
-        ServiceState serviceState = getServiceStateLocked(sessionState.mInfo.getComponent(),
-                userId);
-        if (!serviceState.mIsHardware) {
+        ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
+        if (!serviceState.isHardware) {
             return;
         }
         ITvInputSession session = getSessionLocked(sessionState);
@@ -876,10 +674,10 @@
     private void setStateLocked(String inputId, int state, int userId) {
         UserState userState = getUserStateLocked(userId);
         TvInputState inputState = userState.inputMap.get(inputId);
-        ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent());
-        int oldState = inputState.mState;
-        inputState.mState = state;
-        if (serviceState != null && serviceState.mService == null
+        ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
+        int oldState = inputState.state;
+        inputState.state = state;
+        if (serviceState != null && serviceState.service == null
                 && shouldMaintainConnection(serviceState)) {
             // We don't notify state change while reconnecting. It should remain disconnected.
             return;
@@ -900,7 +698,7 @@
                     UserState userState = getUserStateLocked(resolvedUserId);
                     List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
                     for (TvInputState state : userState.inputMap.values()) {
-                        inputList.add(state.mInfo);
+                        inputList.add(state.info);
                     }
                     return inputList;
                 }
@@ -918,7 +716,7 @@
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
                     TvInputState state = userState.inputMap.get(inputId);
-                    return state == null ? null : state.mInfo;
+                    return state == null ? null : state.info;
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -964,8 +762,8 @@
                         Slog.e(TAG, "client process has already died", e);
                     }
                     for (TvInputState state : userState.inputMap.values()) {
-                        notifyInputStateChangedLocked(userState, state.mInfo.getId(),
-                                state.mState, callback);
+                        notifyInputStateChangedLocked(userState, state.info.getId(), state.state,
+                                callback);
                     }
                 }
             } finally {
@@ -1114,14 +912,14 @@
                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
                         return;
                     }
-                    TvInputInfo info = inputState.mInfo;
+                    TvInputInfo info = inputState.info;
                     ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                     if (serviceState == null) {
                         serviceState = new ServiceState(info.getComponent(), resolvedUserId);
                         userState.serviceStateMap.put(info.getComponent(), serviceState);
                     }
                     // Send a null token immediately while reconnecting.
-                    if (serviceState.mReconnecting == true) {
+                    if (serviceState.reconnecting == true) {
                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
                         return;
                     }
@@ -1135,10 +933,10 @@
                     userState.sessionStateMap.put(sessionToken, sessionState);
 
                     // Also, add them to the session state map of the current service.
-                    serviceState.mSessionTokens.add(sessionToken);
+                    serviceState.sessionTokens.add(sessionToken);
 
-                    if (serviceState.mService != null) {
-                        createSessionInternalLocked(serviceState.mService, sessionToken,
+                    if (serviceState.service != null) {
+                        createSessionInternalLocked(serviceState.service, sessionToken,
                                 resolvedUserId);
                     } else {
                         updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
@@ -1213,10 +1011,10 @@
                     try {
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        if (sessionState.mHardwareSessionToken == null) {
+                        if (sessionState.hardwareSessionToken == null) {
                             getSessionLocked(sessionState).setSurface(surface);
                         } else {
-                            getSessionLocked(sessionState.mHardwareSessionToken,
+                            getSessionLocked(sessionState.hardwareSessionToken,
                                     Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
                         }
                     } catch (RemoteException e) {
@@ -1244,9 +1042,10 @@
                     try {
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
-                        if (sessionState.mHardwareSessionToken != null) {
-                            getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
+                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+                                height);
+                        if (sessionState.hardwareSessionToken != null) {
+                            getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
                                     resolvedUserId).dispatchSurfaceChanged(format, width, height);
                         }
                     } catch (RemoteException e) {
@@ -1272,10 +1071,10 @@
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
                         getSessionLocked(sessionState).setVolume(volume);
-                        if (sessionState.mHardwareSessionToken != null) {
+                        if (sessionState.hardwareSessionToken != null) {
                             // Here, we let the hardware session know only whether volume is on or
                             // off to prevent that the volume is controlled in the both side.
-                            getSessionLocked(sessionState.mHardwareSessionToken,
+                            getSessionLocked(sessionState.hardwareSessionToken,
                                     Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
                                             ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
                         }
@@ -1309,7 +1108,7 @@
 
                         // Log the start of watch.
                         SomeArgs args = SomeArgs.obtain();
-                        args.arg1 = sessionState.mInfo.getComponent().getPackageName();
+                        args.arg1 = sessionState.info.getComponent().getPackageName();
                         args.arg2 = System.currentTimeMillis();
                         args.arg3 = ContentUris.parseId(channelUri);
                         args.arg4 = params;
@@ -1569,10 +1368,10 @@
                         return false;
                     }
                     for (SessionState sessionState : userState.sessionStateMap.values()) {
-                        if (sessionState.mInfo.getId().equals(inputId)
-                                && sessionState.mHardwareSessionToken != null) {
+                        if (sessionState.info.getId().equals(inputId)
+                                && sessionState.hardwareSessionToken != null) {
                             hardwareInputId = userState.sessionStateMap.get(
-                                    sessionState.mHardwareSessionToken).mInfo.getId();
+                                    sessionState.hardwareSessionToken).info.getId();
                             break;
                         }
                     }
@@ -1601,8 +1400,8 @@
                         SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
                                 new SessionState[0]);
                         // Check if there is a wrapper input.
-                        if (sessionStates[0].mHardwareSessionToken != null
-                                || sessionStates[1].mHardwareSessionToken != null) {
+                        if (sessionStates[0].hardwareSessionToken != null
+                                || sessionStates[1].hardwareSessionToken != null) {
                             return true;
                         }
                     }
@@ -1662,15 +1461,15 @@
 
                         pw.increaseIndent();
 
-                        pw.println("mSessionTokens:");
+                        pw.println("sessionTokens:");
                         pw.increaseIndent();
-                        for (IBinder token : client.mSessionTokens) {
+                        for (IBinder token : client.sessionTokens) {
                             pw.println("" + token);
                         }
                         pw.decreaseIndent();
 
-                        pw.println("mClientTokens: " + client.mClientToken);
-                        pw.println("mUserId: " + client.mUserId);
+                        pw.println("clientTokens: " + client.clientToken);
+                        pw.println("userId: " + client.userId);
 
                         pw.decreaseIndent();
                     }
@@ -1685,17 +1484,17 @@
 
                         pw.increaseIndent();
 
-                        pw.println("mSessionTokens:");
+                        pw.println("sessionTokens:");
                         pw.increaseIndent();
-                        for (IBinder token : service.mSessionTokens) {
+                        for (IBinder token : service.sessionTokens) {
                             pw.println("" + token);
                         }
                         pw.decreaseIndent();
 
-                        pw.println("mService: " + service.mService);
-                        pw.println("mCallback: " + service.mCallback);
-                        pw.println("mBound: " + service.mBound);
-                        pw.println("mReconnecting: " + service.mReconnecting);
+                        pw.println("service: " + service.service);
+                        pw.println("callback: " + service.callback);
+                        pw.println("bound: " + service.bound);
+                        pw.println("reconnecting: " + service.reconnecting);
 
                         pw.decreaseIndent();
                     }
@@ -1709,15 +1508,15 @@
                         pw.println(entry.getKey() + ": " + session);
 
                         pw.increaseIndent();
-                        pw.println("mInfo: " + session.mInfo);
-                        pw.println("mClient: " + session.mClient);
-                        pw.println("mSeq: " + session.mSeq);
-                        pw.println("mCallingUid: " + session.mCallingUid);
-                        pw.println("mUserId: " + session.mUserId);
-                        pw.println("mSessionToken: " + session.mSessionToken);
-                        pw.println("mSession: " + session.mSession);
-                        pw.println("mLogUri: " + session.mLogUri);
-                        pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
+                        pw.println("info: " + session.info);
+                        pw.println("client: " + session.client);
+                        pw.println("seq: " + session.seq);
+                        pw.println("callingUid: " + session.callingUid);
+                        pw.println("userId: " + session.userId);
+                        pw.println("sessionToken: " + session.sessionToken);
+                        pw.println("session: " + session.session);
+                        pw.println("logUri: " + session.logUri);
+                        pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
                         pw.decreaseIndent();
                     }
                     pw.decreaseIndent();
@@ -1736,19 +1535,6 @@
         }
     }
 
-    private static final class TvInputState {
-        // A TvInputInfo object which represents the TV input.
-        private TvInputInfo mInfo;
-
-        // The state of TV input. Connected by default.
-        private int mState = INPUT_STATE_CONNECTED;
-
-        @Override
-        public String toString() {
-            return "mInfo: " + mInfo + "; mState: " + mState;
-        }
-    }
-
     private static final class UserState {
         // A mapping from the TV input id to its TvInputState.
         private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
@@ -1789,104 +1575,117 @@
     }
 
     private final class ClientState implements IBinder.DeathRecipient {
-        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+        private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
 
-        private IBinder mClientToken;
-        private final int mUserId;
+        private IBinder clientToken;
+        private final int userId;
 
         ClientState(IBinder clientToken, int userId) {
-            mClientToken = clientToken;
-            mUserId = userId;
+            this.clientToken = clientToken;
+            this.userId = userId;
         }
 
         public boolean isEmpty() {
-            return mSessionTokens.isEmpty();
+            return sessionTokens.isEmpty();
         }
 
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mUserId);
+                UserState userState = getUserStateLocked(userId);
                 // DO NOT remove the client state of clientStateMap in this method. It will be
                 // removed in releaseSessionLocked().
-                ClientState clientState = userState.clientStateMap.get(mClientToken);
+                ClientState clientState = userState.clientStateMap.get(clientToken);
                 if (clientState != null) {
-                    while (clientState.mSessionTokens.size() > 0) {
+                    while (clientState.sessionTokens.size() > 0) {
                         releaseSessionLocked(
-                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
+                                clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
                     }
                 }
-                mClientToken = null;
+                clientToken = null;
             }
         }
     }
 
     private final class ServiceState {
-        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
-        private final ServiceConnection mConnection;
-        private final ComponentName mComponent;
-        private final boolean mIsHardware;
-        private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>();
+        private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
+        private final ServiceConnection connection;
+        private final ComponentName component;
+        private final boolean isHardware;
+        private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
 
-        private ITvInputService mService;
-        private ServiceCallback mCallback;
-        private boolean mBound;
-        private boolean mReconnecting;
+        private ITvInputService service;
+        private ServiceCallback callback;
+        private boolean bound;
+        private boolean reconnecting;
 
         private ServiceState(ComponentName component, int userId) {
-            mComponent = component;
-            mConnection = new InputServiceConnection(component, userId);
-            mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent);
+            this.component = component;
+            this.connection = new InputServiceConnection(component, userId);
+            this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
+        }
+    }
+
+    private static final class TvInputState {
+        // A TvInputInfo object which represents the TV input.
+        private TvInputInfo info;
+
+        // The state of TV input. Connected by default.
+        private int state = INPUT_STATE_CONNECTED;
+
+        @Override
+        public String toString() {
+            return "info: " + info + "; state: " + state;
         }
     }
 
     private final class SessionState implements IBinder.DeathRecipient {
-        private final TvInputInfo mInfo;
-        private final ITvInputClient mClient;
-        private final int mSeq;
-        private final int mCallingUid;
-        private final int mUserId;
-        private final IBinder mSessionToken;
-        private ITvInputSession mSession;
-        private Uri mLogUri;
+        private final TvInputInfo info;
+        private final ITvInputClient client;
+        private final int seq;
+        private final int callingUid;
+        private final int userId;
+        private final IBinder sessionToken;
+        private ITvInputSession session;
+        private Uri logUri;
         // Not null if this session represents an external device connected to a hardware TV input.
-        private IBinder mHardwareSessionToken;
+        private IBinder hardwareSessionToken;
 
         private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
                 int seq, int callingUid, int userId) {
-            mSessionToken = sessionToken;
-            mInfo = info;
-            mClient = client;
-            mSeq = seq;
-            mCallingUid = callingUid;
-            mUserId = userId;
+            this.sessionToken = sessionToken;
+            this.info = info;
+            this.client = client;
+            this.seq = seq;
+            this.callingUid = callingUid;
+            this.userId = userId;
         }
 
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                mSession = null;
-                if (mClient != null) {
+                session = null;
+                if (client != null) {
                     try {
-                        mClient.onSessionReleased(mSeq);
+                        client.onSessionReleased(seq);
                     } catch(RemoteException e) {
                         Slog.e(TAG, "error in onSessionReleased", e);
                     }
                 }
                 // If there are any other sessions based on this session, they should be released.
-                UserState userState = getUserStateLocked(mUserId);
+                UserState userState = getUserStateLocked(userId);
                 for (SessionState sessionState : userState.sessionStateMap.values()) {
-                    if (mSessionToken == sessionState.mHardwareSessionToken) {
-                        releaseSessionLocked(sessionState.mSessionToken, Process.SYSTEM_UID,
-                                mUserId);
+                    if (sessionToken == sessionState.hardwareSessionToken) {
+                        releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
+                                userId);
                         try {
-                            sessionState.mClient.onSessionReleased(sessionState.mSeq);
+                            sessionState.client.onSessionReleased(sessionState.seq);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in onSessionReleased", e);
                         }
                     }
                 }
-                removeSessionStateLocked(mSessionToken, mUserId);
+                removeSessionStateLocked(sessionToken, userId);
             }
         }
     }
@@ -1908,37 +1707,37 @@
             synchronized (mLock) {
                 UserState userState = getUserStateLocked(mUserId);
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
-                serviceState.mService = ITvInputService.Stub.asInterface(service);
+                serviceState.service = ITvInputService.Stub.asInterface(service);
 
                 // Register a callback, if we need to.
-                if (serviceState.mIsHardware && serviceState.mCallback == null) {
-                    serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+                if (serviceState.isHardware && serviceState.callback == null) {
+                    serviceState.callback = new ServiceCallback(mComponent, mUserId);
                     try {
-                        serviceState.mService.registerCallback(serviceState.mCallback);
+                        serviceState.service.registerCallback(serviceState.callback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in registerCallback", e);
                     }
                 }
 
                 // And create sessions, if any.
-                for (IBinder sessionToken : serviceState.mSessionTokens) {
-                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
+                for (IBinder sessionToken : serviceState.sessionTokens) {
+                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
                 }
 
                 for (TvInputState inputState : userState.inputMap.values()) {
-                    if (inputState.mInfo.getComponent().equals(component)
-                            && inputState.mState != INPUT_STATE_DISCONNECTED) {
-                        notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
-                                inputState.mState, null);
+                    if (inputState.info.getComponent().equals(component)
+                            && inputState.state != INPUT_STATE_DISCONNECTED) {
+                        notifyInputStateChangedLocked(userState, inputState.info.getId(),
+                                inputState.state, null);
                     }
                 }
 
-                if (serviceState.mIsHardware) {
+                if (serviceState.isHardware) {
                     List<TvInputHardwareInfo> hardwareInfoList =
                             mTvInputHardwareManager.getHardwareList();
                     for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
                         try {
-                            serviceState.mService.notifyHardwareAdded(hardwareInfo);
+                            serviceState.service.notifyHardwareAdded(hardwareInfo);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in notifyHardwareAdded", e);
                         }
@@ -1948,7 +1747,7 @@
                             mTvInputHardwareManager.getHdmiDeviceList();
                     for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
                         try {
-                            serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+                            serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                         }
@@ -1970,16 +1769,16 @@
                 UserState userState = getUserStateLocked(mUserId);
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                 if (serviceState != null) {
-                    serviceState.mReconnecting = true;
-                    serviceState.mBound = false;
-                    serviceState.mService = null;
-                    serviceState.mCallback = null;
+                    serviceState.reconnecting = true;
+                    serviceState.bound = false;
+                    serviceState.service = null;
+                    serviceState.callback = null;
 
                     abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
 
                     for (TvInputState inputState : userState.inputMap.values()) {
-                        if (inputState.mInfo.getComponent().equals(component)) {
-                            notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+                        if (inputState.info.getComponent().equals(component)) {
+                            notifyInputStateChangedLocked(userState, inputState.info.getId(),
                                     INPUT_STATE_DISCONNECTED, null);
                         }
                     }
@@ -2012,7 +1811,7 @@
 
         private void addTvInputLocked(TvInputInfo inputInfo) {
             ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
-            serviceState.mInputList.add(inputInfo);
+            serviceState.inputList.add(inputInfo);
             buildTvInputListLocked(mUserId);
         }
 
@@ -2042,7 +1841,7 @@
             synchronized (mLock) {
                 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
                 boolean removed = false;
-                for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
+                for (Iterator<TvInputInfo> it = serviceState.inputList.iterator();
                         it.hasNext(); ) {
                     if (it.next().getId().equals(inputId)) {
                         it.remove();
@@ -2060,7 +1859,211 @@
         }
     }
 
-    private final class WatchLogHandler extends Handler {
+    private final class SessionCallback extends ITvInputSessionCallback.Stub {
+        private final SessionState sessionState;
+        private final InputChannel[] mChannels;
+
+        SessionCallback(SessionState sessionState, InputChannel[] channels) {
+            this.sessionState = sessionState;
+            mChannels = channels;
+        }
+
+        @Override
+        public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
+            if (DEBUG) {
+                Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.info.getId() + ")");
+            }
+            synchronized (mLock) {
+                sessionState.session = session;
+                sessionState.hardwareSessionToken = harewareSessionToken;
+                if (session == null) {
+                    removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+                    sendSessionTokenToClientLocked(sessionState.client,
+                            sessionState.info.getId(), null, null, sessionState.seq);
+                } else {
+                    try {
+                        session.asBinder().linkToDeath(sessionState, 0);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "session process has already died", e);
+                    }
+
+                    IBinder clientToken = sessionState.client.asBinder();
+                    UserState userState = getUserStateLocked(sessionState.userId);
+                    ClientState clientState = userState.clientStateMap.get(clientToken);
+                    if (clientState == null) {
+                        clientState = createClientStateLocked(clientToken, sessionState.userId);
+                    }
+                    clientState.sessionTokens.add(sessionState.sessionToken);
+
+                    sendSessionTokenToClientLocked(sessionState.client,
+                            sessionState.info.getId(), sessionState.sessionToken, mChannels[0],
+                            sessionState.seq);
+                }
+                mChannels[0].dispose();
+            }
+        }
+
+        @Override
+        public void onChannelRetuned(Uri channelUri) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    // TODO: Consider adding this channel change in the watch log. When we do
+                    // that, how we can protect the watch log from malicious tv inputs should
+                    // be addressed. e.g. add a field which represents where the channel change
+                    // originated from.
+                    sessionState.client.onChannelRetuned(channelUri, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onChannelRetuned", e);
+                }
+            }
+        }
+
+        @Override
+        public void onTracksChanged(List<TvTrackInfo> tracks) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onTracksChanged(" + tracks + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onTracksChanged(tracks, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onTracksChanged", e);
+                }
+            }
+        }
+
+        @Override
+        public void onTrackSelected(int type, String trackId) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onTrackSelected(type, trackId, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onTrackSelected", e);
+                }
+            }
+        }
+
+        @Override
+        public void onVideoAvailable() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onVideoAvailable()");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onVideoAvailable(sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onVideoAvailable", e);
+                }
+            }
+        }
+
+        @Override
+        public void onVideoUnavailable(int reason) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onVideoUnavailable(reason, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onVideoUnavailable", e);
+                }
+            }
+        }
+
+        @Override
+        public void onContentAllowed() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onContentAllowed()");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onContentAllowed(sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onContentAllowed", e);
+                }
+            }
+        }
+
+        @Override
+        public void onContentBlocked(String rating) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onContentBlocked()");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onContentBlocked(rating, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onContentBlocked", e);
+                }
+            }
+        }
+
+        @Override
+        public void onLayoutSurface(int left, int top, int right, int bottom) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+                            + ", right=" + right + ", bottom=" + bottom + ",)");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onLayoutSurface(left, top, right, bottom, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onLayoutSurface", e);
+                }
+            }
+        }
+
+        @Override
+        public void onSessionEvent(String eventType, Bundle eventArgs) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onSessionEvent(eventType, eventArgs,
+                            sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onSessionEvent", e);
+                }
+            }
+        }
+    }
+
+    private static final class WatchLogHandler extends Handler {
         // There are only two kinds of watch events that can happen on the system:
         // 1. The current TV input session is tuned to a new channel.
         // 2. The session is released for some reason.
@@ -2072,8 +2075,11 @@
         private static final int MSG_LOG_WATCH_START = 1;
         private static final int MSG_LOG_WATCH_END = 2;
 
-        public WatchLogHandler(Looper looper) {
+        private final ContentResolver mContentResolver;
+
+        public WatchLogHandler(ContentResolver contentResolver, Looper looper) {
             super(looper);
+            mContentResolver = contentResolver;
         }
 
         @Override
@@ -2159,7 +2165,7 @@
         }
     }
 
-    final class HardwareListener implements TvInputHardwareManager.Listener {
+    private final class HardwareListener implements TvInputHardwareManager.Listener {
         @Override
         public void onStateChanged(String inputId, int state) {
             synchronized (mLock) {
@@ -2173,9 +2179,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHardwareAdded(info);
+                        serviceState.service.notifyHardwareAdded(info);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHardwareAdded", e);
                     }
@@ -2189,9 +2195,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHardwareRemoved(info);
+                        serviceState.service.notifyHardwareRemoved(info);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHardwareRemoved", e);
                     }
@@ -2205,9 +2211,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+                        serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                     }
@@ -2221,9 +2227,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
+                        serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
                     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 38433ae..4b7dd08 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -754,7 +754,11 @@
                 final boolean isHwAccelerated = (attrs.flags &
                         WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
                 final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
-                if (!PixelFormat.formatHasAlpha(attrs.format)) {
+                if (!PixelFormat.formatHasAlpha(attrs.format)
+                        && attrs.surfaceInsets.left == 0
+                        && attrs.surfaceInsets.top == 0
+                        && attrs.surfaceInsets.right == 0
+                        && attrs.surfaceInsets.bottom  == 0) {
                     flags |= SurfaceControl.OPAQUE;
                 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 28cc99f..564a3df 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4600,7 +4600,6 @@
     @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabled) {
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
-
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -4611,13 +4610,28 @@
             if (!isDeviceOwner && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
                 throw new SecurityException("Profile owners cannot set user restriction " + key);
             }
+            boolean alreadyRestricted = mUserManager.hasUserRestriction(key, userHandle);
 
+            IAudioService iAudioService = null;
+            if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)
+                    || UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                iAudioService = IAudioService.Stub.asInterface(
+                        ServiceManager.getService(Context.AUDIO_SERVICE));
+            }
+
+            if (enabled && !alreadyRestricted) {
+                try {
+                    if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+                        iAudioService.setMicrophoneMute(true, who.getPackageName());
+                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                        iAudioService.setMasterMute(true, 0, who.getPackageName(), null);
+                    }
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
+                }
+            }
             long id = Binder.clearCallingIdentity();
             try {
-                AudioManager audioManager =
-                        (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-                boolean alreadyRestricted = mUserManager.hasUserRestriction(key);
-
                 if (enabled && !alreadyRestricted) {
                     if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
                         Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -4648,26 +4662,23 @@
                         Settings.Secure.putIntForUser(mContext.getContentResolver(),
                                 Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
                                 userHandle.getIdentifier());
-                    } else if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
-                        audioManager.setMicrophoneMute(true);
-                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                        audioManager.setMasterMute(true);
                     }
                 }
-
                 mUserManager.setUserRestriction(key, enabled, userHandle);
-
-                if (!enabled && alreadyRestricted) {
-                    if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
-                        audioManager.setMicrophoneMute(false);
-                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
-                        audioManager.setMasterMute(false);
-                    }
-                }
-
             } finally {
                 restoreCallingIdentity(id);
             }
+            if (!enabled && alreadyRestricted) {
+                try {
+                    if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+                        iAudioService.setMicrophoneMute(false, who.getPackageName());
+                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                        iAudioService.setMasterMute(false, 0, who.getPackageName(), null);
+                    }
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
+                }
+            }
         }
     }
 
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index ecb0d4b..7c596c1 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -89,6 +89,7 @@
         private final int mCallerDisplayNamePresentation;
         private final PhoneAccountHandle mAccountHandle;
         private final int mCallCapabilities;
+        private final int mCallProperties;
         private final int mDisconnectCauseCode;
         private final String mDisconnectCauseMessage;
         private final long mConnectTimeMillis;
@@ -145,6 +146,14 @@
         }
 
         /**
+         * @return A bitmask of the properties of the {@code Call}, as defined in
+         *         {@link CallProperties}.
+         */
+        public int getCallProperties() {
+            return mCallProperties;
+        }
+
+        /**
          * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
          * as a code chosen from among those declared in {@link DisconnectCause}.
          */
@@ -210,6 +219,7 @@
                                 d.mCallerDisplayNamePresentation) &&
                         Objects.equals(mAccountHandle, d.mAccountHandle) &&
                         Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
+                        Objects.equals(mCallProperties, d.mCallProperties) &&
                         Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
                         Objects.equals(mDisconnectCauseMessage, d.mDisconnectCauseMessage) &&
                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
@@ -230,6 +240,7 @@
                     Objects.hashCode(mCallerDisplayNamePresentation) +
                     Objects.hashCode(mAccountHandle) +
                     Objects.hashCode(mCallCapabilities) +
+                    Objects.hashCode(mCallProperties) +
                     Objects.hashCode(mDisconnectCauseCode) +
                     Objects.hashCode(mDisconnectCauseMessage) +
                     Objects.hashCode(mConnectTimeMillis) +
@@ -247,6 +258,7 @@
                 int callerDisplayNamePresentation,
                 PhoneAccountHandle accountHandle,
                 int capabilities,
+                int properties,
                 int disconnectCauseCode,
                 String disconnectCauseMessage,
                 long connectTimeMillis,
@@ -260,6 +272,7 @@
             mCallerDisplayNamePresentation = callerDisplayNamePresentation;
             mAccountHandle = accountHandle;
             mCallCapabilities = capabilities;
+            mCallProperties = properties;
             mDisconnectCauseCode = disconnectCauseCode;
             mDisconnectCauseMessage = disconnectCauseMessage;
             mConnectTimeMillis = connectTimeMillis;
@@ -642,6 +655,7 @@
                 parcelableCall.getCallerDisplayNamePresentation(),
                 parcelableCall.getAccountHandle(),
                 parcelableCall.getCapabilities(),
+                parcelableCall.getProperties(),
                 parcelableCall.getDisconnectCauseCode(),
                 parcelableCall.getDisconnectCauseMsg(),
                 parcelableCall.getConnectTimeMillis(),
diff --git a/telecomm/java/android/telecomm/CallProperties.java b/telecomm/java/android/telecomm/CallProperties.java
new file mode 100644
index 0000000..90eb0cb
--- /dev/null
+++ b/telecomm/java/android/telecomm/CallProperties.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecomm;
+
+/**
+ * Defines properties of a phone call which may be affected by changes to the call.
+ * @hide
+ */
+public class CallProperties {
+    /** Call is currently in a conference call. */
+    public static final int CONFERENCE                      = 0x00000001;
+}
diff --git a/telecomm/java/android/telecomm/ParcelableCall.java b/telecomm/java/android/telecomm/ParcelableCall.java
index 8098b94..a2aa192 100644
--- a/telecomm/java/android/telecomm/ParcelableCall.java
+++ b/telecomm/java/android/telecomm/ParcelableCall.java
@@ -40,6 +40,7 @@
     private final String mDisconnectCauseMsg;
     private final List<String> mCannedSmsResponses;
     private final int mCapabilities;
+    private final int mProperties;
     private final long mConnectTimeMillis;
     private final Uri mHandle;
     private final int mHandlePresentation;
@@ -63,6 +64,7 @@
             String disconnectCauseMsg,
             List<String> cannedSmsResponses,
             int capabilities,
+            int properties,
             long connectTimeMillis,
             Uri handle,
             int handlePresentation,
@@ -83,6 +85,7 @@
         mDisconnectCauseMsg = disconnectCauseMsg;
         mCannedSmsResponses = cannedSmsResponses;
         mCapabilities = capabilities;
+        mProperties = properties;
         mConnectTimeMillis = connectTimeMillis;
         mHandle = handle;
         mHandlePresentation = handlePresentation;
@@ -137,6 +140,9 @@
         return mCapabilities;
     }
 
+    /** Bitmask of properties of the call. */
+    public int getProperties() { return mProperties; }
+
     /** The time that the call switched to the active state. */
     public long getConnectTimeMillis() {
         return mConnectTimeMillis;
@@ -246,6 +252,7 @@
             List<String> cannedSmsResponses = new ArrayList<>();
             source.readList(cannedSmsResponses, classLoader);
             int capabilities = source.readInt();
+            int properties = source.readInt();
             long connectTimeMillis = source.readLong();
             Uri handle = source.readParcelable(classLoader);
             int handlePresentation = source.readInt();
@@ -264,10 +271,10 @@
             source.readList(conferenceableCallIds, classLoader);
             Bundle extras = source.readParcelable(classLoader);
             return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg,
-                    cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
-                    callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
-                    accountHandle, videoCallProvider, parentCallId, childCallIds, statusHints,
-                    videoState, conferenceableCallIds, extras);
+                    cannedSmsResponses, capabilities, properties, connectTimeMillis, handle,
+                    handlePresentation, callerDisplayName, callerDisplayNamePresentation,
+                    gatewayInfo, accountHandle, videoCallProvider, parentCallId, childCallIds,
+                    statusHints, videoState, conferenceableCallIds, extras);
         }
 
         @Override
@@ -291,6 +298,7 @@
         destination.writeString(mDisconnectCauseMsg);
         destination.writeList(mCannedSmsResponses);
         destination.writeInt(mCapabilities);
+        destination.writeInt(mProperties);
         destination.writeLong(mConnectTimeMillis);
         destination.writeParcelable(mHandle, 0);
         destination.writeInt(mHandlePresentation);
diff --git a/telecomm/java/android/telecomm/PhoneCapabilities.java b/telecomm/java/android/telecomm/PhoneCapabilities.java
index 45168d5..0c6a1ef 100644
--- a/telecomm/java/android/telecomm/PhoneCapabilities.java
+++ b/telecomm/java/android/telecomm/PhoneCapabilities.java
@@ -19,7 +19,6 @@
 /**
  * Defines capabilities a phone call can support, such as conference calling and video telephony.
  * Also defines properties of a phone call, such as whether it is using VoLTE technology.
-
  */
 public final class PhoneCapabilities {
     /** Call can currently be put on hold or unheld. */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 71b796a..cdee3de 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3105,13 +3105,13 @@
 
     /** @hide */
     @SystemApi
-    public List<String> getCarrierPackageNamesForBroadcastIntent(Intent intent) {
+    public List<String> getCarrierPackageNamesForIntent(Intent intent) {
         try {
-            return getITelephony().getCarrierPackageNamesForBroadcastIntent(intent);
+            return getITelephony().getCarrierPackageNamesForIntent(intent);
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "getCarrierPackageNamesForBroadcastIntent RemoteException", ex);
+            Rlog.e(TAG, "getCarrierPackageNamesForIntent RemoteException", ex);
         } catch (NullPointerException ex) {
-            Rlog.e(TAG, "getCarrierPackageNamesForBroadcastIntent NPE", ex);
+            Rlog.e(TAG, "getCarrierPackageNamesForIntent NPE", ex);
         }
         return null;
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b1c3c4a..5c3dcdb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -710,12 +710,12 @@
      * Returns the package name of the carrier apps that should handle the input intent.
      *
      * @param packageManager PackageManager for getting receivers.
-     * @param intent Intent that will be broadcast.
+     * @param intent Intent that will be sent.
      * @return list of carrier app package names that can handle the intent.
      *         Returns null if there is an error and an empty list if there
      *         are no matching packages.
      */
-    List<String> getCarrierPackageNamesForBroadcastIntent(in Intent intent);
+    List<String> getCarrierPackageNamesForIntent(in Intent intent);
 
     /**
      * Set whether Android should display a simplified Mobile Network Settings UI.
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index ea0db56..e03b9c8 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -366,15 +366,11 @@
                     if (recent.id >= 0) {
                         // Stack on top.
                         intent.putExtra(DocActivity.LABEL, "Stacked");
-                        task.startActivity(ActivityTestMain.this, intent, null);
                     } else {
                         // Start root activity.
-                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
-                                | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                                | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
                         intent.putExtra(DocActivity.LABEL, "New Root");
-                        task.startActivity(ActivityTestMain.this, intent, null);
                     }
+                    task.startActivity(ActivityTestMain.this, intent, null);
                 }
                 return true;
             }
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
index 526ea5d..50633db 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
@@ -21,7 +21,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.media.browse.MediaBrowserService;
+import android.service.media.MediaBrowserService;
 import android.os.Bundle;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentTransaction;
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
index 2fc77dc..64602d52 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
@@ -22,8 +22,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.media.browse.MediaBrowser;
-import android.media.browse.MediaBrowserItem;
-import android.media.browse.MediaBrowserService;
+import android.service.media.MediaBrowserService;
 import android.os.Bundle;
 import android.net.Uri;
 import android.support.v4.app.FragmentActivity;
@@ -57,9 +56,9 @@
     private MediaBrowser mBrowser;
 
     private static class Item {
-        final MediaBrowserItem media;
+        final MediaBrowser.MediaItem media;
 
-        Item(MediaBrowserItem m) {
+        Item(MediaBrowser.MediaItem m) {
             this.media = m;
         }
     }
@@ -103,13 +102,13 @@
         final Item item = mItems.get(position);
 
         Log.i("BrowserListFragment", "Item clicked: " + position + " -- "
-                + mAdapter.getItem(position).media.getUri());
+                + mAdapter.getItem(position).media.getDescription().getIconUri());
 
         final BrowserListFragment fragment = new BrowserListFragment();
 
         final Bundle args = new Bundle();
         args.putParcelable(BrowserListFragment.ARG_COMPONENT, mComponent);
-        args.putParcelable(BrowserListFragment.ARG_URI, item.media.getUri());
+        args.putParcelable(BrowserListFragment.ARG_URI, item.media.getDescription().getIconUri());
         fragment.setArguments(args);
 
         getFragmentManager().beginTransaction()
@@ -130,7 +129,8 @@
             }
             mBrowser.subscribe(mUri, new MediaBrowser.SubscriptionCallback() {
                     @Override
-                    public void onChildrenLoaded(Uri parentUri, List<MediaBrowserItem> children) {
+                    public void onChildrenLoaded(Uri parentUri,
+                            List<MediaBrowser.MediaItem> children) {
                         Log.d(TAG, "onChildrenLoaded parentUri=" + parentUri
                                 + " children= " + children);
                         mItems.clear();
@@ -197,7 +197,7 @@
 
             final TextView tv = (TextView)convertView;
             final Item item = mItems.get(position);
-            tv.setText(item.media.getTitle());
+            tv.setText(item.media.getDescription().getTitle());
 
             return convertView;
         }
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
index 937f1e6..845db6c 100644
--- a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
@@ -25,13 +25,14 @@
 import android.database.MatrixCursor;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.media.MediaDescription;
 import android.media.MediaPlayer;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
 import android.media.MediaPlayer.OnPreparedListener;
-import android.media.browse.MediaBrowserItem;
-import android.media.browse.MediaBrowserService;
-import android.media.browse.MediaBrowserService.BrowserRoot;
+import android.media.browse.MediaBrowser;
+import android.service.media.MediaBrowserService;
+import android.service.media.MediaBrowserService.BrowserRoot;
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
@@ -122,17 +123,19 @@
 
     @Override
     public void onLoadChildren(final Uri parentUri,
-            final Result<List<MediaBrowserItem>> result) {
+            final Result<List<MediaBrowser.MediaItem>> result) {
         new Handler().postDelayed(new Runnable() {
                 public void run() {
-                    final ArrayList<MediaBrowserItem> list = new ArrayList();
+                    final ArrayList<MediaBrowser.MediaItem> list = new ArrayList();
 
                     for (int i=0; i<10; i++) {
-                        list.add(new MediaBrowserItem.Builder(
-                                    Uri.withAppendedPath(BASE_URI, Integer.toString(i)),
-                                    MediaBrowserItem.FLAG_BROWSABLE, "Title " + i)
-                                .setSummary("Summary " + i)
-                                .build());
+                        MediaDescription.Builder bob = new MediaDescription.Builder();
+                        bob.setTitle("Title " + i);
+                        bob.setSubtitle("Summary " + i);
+                        bob.setMediaId(Uri.withAppendedPath(BASE_URI,
+                                Integer.toString(i)).toString());
+                        list.add(new MediaBrowser.MediaItem(MediaBrowser.MediaItem.FLAG_BROWSABLE,
+                                bob.build()));
                     }
 
                     result.sendResult(list);
@@ -141,11 +144,6 @@
         result.detach();
     }
 
-    @Override
-    public void onLoadIcon(Uri uri, int width, int height, Result<Bitmap> result) {
-        result.sendResult(null);
-    }
-
     /*
     @Override
     public void query(final Query query, final IMetadataResultHandler metadataResultHandler,
diff --git a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
index a5bcda5..d1172ac 100644
--- a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
+++ b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
@@ -12,6 +12,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Bitmap;
+import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
@@ -185,10 +186,10 @@
             text = "Empty metadata!";
             art = null;
         } else {
-            MediaMetadata.Description description = mMetadata.getDescription();
+            MediaDescription description = mMetadata.getDescription();
             title = description.getTitle();
             text = description.getSubtitle();
-            art = description.getIcon();
+            art = description.getIconBitmap();
         }
 
         String playPauseLabel = "";
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index b44e2d1..117fc24 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1594,6 +1594,11 @@
     return mIncludedAssets.getResources(false);
 }
 
+AssetManager& AaptAssets::getAssetManager()
+{
+    return mIncludedAssets;
+}
+
 void AaptAssets::print(const String8& prefix) const
 {
     String8 innerPrefix(prefix);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 0c2576a..3fc9f81 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -561,6 +561,7 @@
     status_t buildIncludedResources(Bundle* bundle);
     status_t addIncludedResources(const sp<AaptFile>& file);
     const ResTable& getIncludedResources() const;
+    AssetManager& getAssetManager();
 
     void print(const String8& prefix) const;
 
diff --git a/tools/aapt/AaptXml.cpp b/tools/aapt/AaptXml.cpp
new file mode 100644
index 0000000..708e405
--- /dev/null
+++ b/tools/aapt/AaptXml.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+#include "AaptXml.h"
+
+using namespace android;
+
+namespace AaptXml {
+
+static String8 getStringAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+        String8* outError) {
+    Res_value value;
+    if (tree.getAttributeValue(attrIndex, &value) < 0) {
+        if (outError != NULL) {
+            *outError = "could not find attribute at index";
+        }
+        return String8();
+    }
+
+    if (value.dataType != Res_value::TYPE_STRING) {
+        if (outError != NULL) {
+            *outError = "attribute is not a string value";
+        }
+        return String8();
+    }
+
+    size_t len;
+    const uint16_t* str = tree.getAttributeStringValue(attrIndex, &len);
+    return str ? String8(str, len) : String8();
+}
+
+static int32_t getIntegerAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+    int32_t defValue, String8* outError) {
+    Res_value value;
+    if (tree.getAttributeValue(attrIndex, &value) < 0) {
+        if (outError != NULL) {
+            *outError = "could not find attribute at index";
+        }
+        return defValue;
+    }
+
+    if (value.dataType < Res_value::TYPE_FIRST_INT
+            || value.dataType > Res_value::TYPE_LAST_INT) {
+        if (outError != NULL) {
+            *outError = "attribute is not an integer value";
+        }
+        return defValue;
+    }
+    return value.data;
+}
+
+
+ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) {
+    size_t attrCount = tree.getAttributeCount();
+    for (size_t i = 0; i < attrCount; i++) {
+        if (tree.getAttributeNameResID(i) == attrRes) {
+            return (ssize_t)i;
+        }
+    }
+    return -1;
+}
+
+String8 getAttribute(const ResXMLTree& tree, const char* ns,
+        const char* attr, String8* outError) {
+    ssize_t idx = tree.indexOfAttribute(ns, attr);
+    if (idx < 0) {
+        return String8();
+    }
+    return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return String8();
+    }
+    return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getResolvedAttribute(const ResTable& resTable, const ResXMLTree& tree,
+        uint32_t attrRes, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return String8();
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType == Res_value::TYPE_STRING) {
+            size_t len;
+            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+            return str ? String8(str, len) : String8();
+        }
+        resTable.resolveReference(&value, 0);
+        if (value.dataType != Res_value::TYPE_STRING) {
+            if (outError != NULL) {
+                *outError = "attribute is not a string value";
+            }
+            return String8();
+        }
+    }
+    size_t len;
+    const Res_value* value2 = &value;
+    const char16_t* str = resTable.valueToString(value2, 0, NULL, &len);
+    return str ? String8(str, len) : String8();
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, const char* ns,
+        const char* attr, int32_t defValue, String8* outError) {
+    ssize_t idx = tree.indexOfAttribute(ns, attr);
+    if (idx < 0) {
+        return defValue;
+    }
+    return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, int32_t defValue,
+        String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return defValue;
+    }
+    return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getResolvedIntegerAttribute(const ResTable& resTable, const ResXMLTree& tree,
+        uint32_t attrRes, int32_t defValue, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return defValue;
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType == Res_value::TYPE_REFERENCE) {
+            resTable.resolveReference(&value, 0);
+        }
+        if (value.dataType < Res_value::TYPE_FIRST_INT
+                || value.dataType > Res_value::TYPE_LAST_INT) {
+            if (outError != NULL) {
+                *outError = "attribute is not an integer value";
+            }
+            return defValue;
+        }
+    }
+    return value.data;
+}
+
+void getResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
+        uint32_t attrRes, Res_value* outValue, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        if (outError != NULL) {
+            *outError = "attribute could not be found";
+        }
+        return;
+    }
+    if (tree.getAttributeValue(idx, outValue) != NO_ERROR) {
+        if (outValue->dataType == Res_value::TYPE_REFERENCE) {
+            resTable.resolveReference(outValue, 0);
+        }
+        // The attribute was found and was resolved if need be.
+        return;
+    }
+    if (outError != NULL) {
+        *outError = "error getting resolved resource attribute";
+    }
+}
+
+} // namespace AaptXml
diff --git a/tools/aapt/AaptXml.h b/tools/aapt/AaptXml.h
new file mode 100644
index 0000000..16977f3
--- /dev/null
+++ b/tools/aapt/AaptXml.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAPT_XML_H
+#define __AAPT_XML_H
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+/**
+ * Utility methods for dealing with ResXMLTree.
+ */
+namespace AaptXml {
+
+/**
+ * Returns the index of the attribute, or < 0 if it was not found.
+ */
+ssize_t indexOfAttribute(const android::ResXMLTree& tree, uint32_t attrRes);
+
+/**
+ * Returns the string value for the specified attribute.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, const char* ns,
+        const char* attr, android::String8* outError = NULL);
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+        const char* attr, int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+        const char* attr, android::String8* outError) {
+    return getIntegerAttribute(tree, ns, attr, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+        int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError) {
+    return getIntegerAttribute(tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes, int32_t defValue = -1,
+        android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+inline int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError) {
+    return getResolvedIntegerAttribute(resTable, tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string may be a resource in the supplied ResTable.
+ */
+android::String8 getResolvedAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError = NULL);
+
+/**
+ * Returns the resource for the specified attribute in the outValue parameter.
+ * The resource may be a resource in the supplied ResTable.
+ */
+void getResolvedResourceAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes, android::Res_value* outValue,
+        android::String8* outError = NULL);
+
+} // namespace AaptXml
+
+#endif // __AAPT_XML_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 4ce5045..2cbabe1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -28,6 +28,7 @@
     AaptAssets.cpp \
     AaptConfig.cpp \
     AaptUtil.cpp \
+    AaptXml.cpp \
     ApkBuilder.cpp \
     Command.cpp \
     CrunchCache.cpp \
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index af49461..9bed899 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -130,6 +130,10 @@
     void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
     bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; }
     void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; }
+    const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; }
+    void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; }
+    const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; }
+    void setPlatformBuildVersionName(const android::String8& name) { mPlatformVersionName = name; }
 
     bool getUTF16StringsOption() {
         return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -323,6 +327,8 @@
     const char* mSingleCrunchInputFile;
     const char* mSingleCrunchOutputFile;
     bool        mBuildSharedLibrary;
+    android::String8 mPlatformVersionCode;
+    android::String8 mPlatformVersionName;
 
     /* file specification */
     int         mArgc;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index a0f0a08..fd660bb 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
 //
 // Android Asset Packaging Tool main entry point.
 //
+#include "AaptXml.h"
 #include "ApkBuilder.h"
 #include "Bundle.h"
 #include "Images.h"
@@ -241,162 +242,17 @@
     return result;
 }
 
-static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
-{
-    size_t N = tree.getAttributeCount();
-    for (size_t i=0; i<N; i++) {
-        if (tree.getAttributeNameResID(i) == attrRes) {
-            return (ssize_t)i;
-        }
-    }
-    return -1;
-}
-
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
-                            const char* attr, String8* outError)
-{
-    ssize_t idx = tree.indexOfAttribute(ns, attr);
-    if (idx < 0) {
-        return String8();
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType != Res_value::TYPE_STRING) {
-            if (outError != NULL) {
-                *outError = "attribute is not a string value";
-            }
-            return String8();
-        }
-    }
-    size_t len;
-    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
-    return str ? String8(str, len) : String8();
-}
-
-static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return String8();
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType != Res_value::TYPE_STRING) {
-            if (outError != NULL) {
-                *outError = "attribute is not a string value";
-            }
-            return String8();
-        }
-    }
-    size_t len;
-    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
-    return str ? String8(str, len) : String8();
-}
-
-static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
-        String8* outError, int32_t defValue = -1)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return defValue;
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType < Res_value::TYPE_FIRST_INT
-                || value.dataType > Res_value::TYPE_LAST_INT) {
-            if (outError != NULL) {
-                *outError = "attribute is not an integer value";
-            }
-            return defValue;
-        }
-    }
-    return value.data;
-}
-
-static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
-        uint32_t attrRes, String8* outError, int32_t defValue = -1)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return defValue;
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType == Res_value::TYPE_REFERENCE) {
-            resTable->resolveReference(&value, 0);
-        }
-        if (value.dataType < Res_value::TYPE_FIRST_INT
-                || value.dataType > Res_value::TYPE_LAST_INT) {
-            if (outError != NULL) {
-                *outError = "attribute is not an integer value";
-            }
-            return defValue;
-        }
-    }
-    return value.data;
-}
-
-static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
-        uint32_t attrRes, String8* outError)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return String8();
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType == Res_value::TYPE_STRING) {
-            size_t len;
-            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
-            return str ? String8(str, len) : String8();
-        }
-        resTable->resolveReference(&value, 0);
-        if (value.dataType != Res_value::TYPE_STRING) {
-            if (outError != NULL) {
-                *outError = "attribute is not a string value";
-            }
-            return String8();
-        }
-    }
-    size_t len;
-    const Res_value* value2 = &value;
-    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
-    return str ? String8(str, len) : String8();
-}
-
-static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
-        const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        if (outError != NULL) {
-            *outError = "attribute could not be found";
-        }
-        return;
-    }
-    if (tree.getAttributeValue(idx, value) != NO_ERROR) {
-        if (value->dataType == Res_value::TYPE_REFERENCE) {
-            resTable->resolveReference(value, 0);
-        }
-        // The attribute was found and was resolved if need be.
-        return;
-    }
-    if (outError != NULL) {
-        *outError = "error getting resolved resource attribute";
-    }
-}
-
-static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
+static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
         uint32_t attrRes, String8 attrLabel, String8* outError)
 {
     Res_value value;
-    getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
+    AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
     if (*outError != "") {
         *outError = "error print resolved resource attribute";
         return;
     }
     if (value.dataType == Res_value::TYPE_STRING) {
-        String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
+        String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
         printf("%s='%s'", attrLabel.string(),
                 ResTable::normalizeForOutput(result.string()).string());
     } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
@@ -488,10 +344,10 @@
         }
         String8 tag(ctag16);
         if (tag == "screen") {
-            int32_t screenSize = getIntegerAttribute(tree,
-                    SCREEN_SIZE_ATTR, NULL, -1);
-            int32_t screenDensity = getIntegerAttribute(tree,
-                    SCREEN_DENSITY_ATTR, NULL, -1);
+            int32_t screenSize = AaptXml::getIntegerAttribute(tree,
+                    SCREEN_SIZE_ATTR);
+            int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
+                    SCREEN_DENSITY_ATTR);
             if (screenSize > 0 && screenDensity > 0) {
                 if (!first) {
                     printf(",");
@@ -577,7 +433,7 @@
                 }
             } else if (depth == 2 && withinApduService) {
                 if (tag == "aid-group") {
-                    String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
+                    String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
                     if (error != "") {
                         if (outError != NULL) *outError = error;
                         return Vector<String8>();
@@ -876,11 +732,11 @@
                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                         goto bail;
                     }
-                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
+                    String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                     printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
                 } else if (depth == 2 && tag == "permission") {
                     String8 error;
-                    String8 name = getAttribute(tree, NAME_ATTR, &error);
+                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                     if (error != "") {
                         fprintf(stderr, "ERROR: %s\n", error.string());
                         goto bail;
@@ -889,14 +745,14 @@
                             ResTable::normalizeForOutput(name.string()).string());
                 } else if (depth == 2 && tag == "uses-permission") {
                     String8 error;
-                    String8 name = getAttribute(tree, NAME_ATTR, &error);
+                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                     if (error != "") {
                         fprintf(stderr, "ERROR: %s\n", error.string());
                         goto bail;
                     }
                     printUsesPermission(name,
-                            getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
-                            getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+                            AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+                            AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                 }
             }
         } else if (strcmp("badging", option) == 0) {
@@ -1151,12 +1007,14 @@
                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                         goto bail;
                     }
-                    pkg = getAttribute(tree, NULL, "package", NULL);
+                    pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                     printf("package: name='%s' ",
                             ResTable::normalizeForOutput(pkg.string()).string());
-                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+                    int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
+                            &error);
                     if (error != "") {
-                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
+                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
+                                error.string());
                         goto bail;
                     }
                     if (versionCode > 0) {
@@ -1164,23 +1022,29 @@
                     } else {
                         printf("versionCode='' ");
                     }
-                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
+                    String8 versionName = AaptXml::getResolvedAttribute(res, tree,
+                            VERSION_NAME_ATTR, &error);
                     if (error != "") {
-                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
+                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
+                                error.string());
                         goto bail;
                     }
                     printf("versionName='%s'",
                             ResTable::normalizeForOutput(versionName.string()).string());
 
-                    String8 splitName = getAttribute(tree, NULL, "split", NULL);
+                    String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
                     if (!splitName.isEmpty()) {
                         printf(" split='%s'", ResTable::normalizeForOutput(
                                     splitName.string()).string());
                     }
+
+                    int32_t platformVersionCode = AaptXml::getIntegerAttribute(tree, NULL,
+                            "platformBuildVersionCode");
+                    printf(" platformBuildVersionCode='%d'", platformVersionCode);
                     printf("\n");
 
-                    int32_t installLocation = getResolvedIntegerAttribute(&res, tree,
-                            INSTALL_LOCATION_ATTR, &error, -1);
+                    int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
+                            INSTALL_LOCATION_ATTR, &error);
                     if (error != "") {
                         fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
                                 error.string());
@@ -1215,7 +1079,8 @@
                         for (size_t i=0; i<NL; i++) {
                             const char* localeStr =  locales[i].string();
                             assets.setLocale(localeStr != NULL ? localeStr : "");
-                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                            String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+                                    &error);
                             if (llabel != "") {
                                 if (localeStr == NULL || strlen(localeStr) == 0) {
                                     label = llabel;
@@ -1236,7 +1101,8 @@
                         for (size_t i=0; i<ND; i++) {
                             tmpConfig.density = densities[i];
                             assets.setConfiguration(tmpConfig);
-                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                            String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+                                    &error);
                             if (icon != "") {
                                 printf("application-icon-%d:'%s'\n", densities[i],
                                         ResTable::normalizeForOutput(icon.string()).string());
@@ -1244,14 +1110,17 @@
                         }
                         assets.setConfiguration(config);
 
-                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                        String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
+                                    error.string());
                             goto bail;
                         }
-                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
+                        int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
+                                &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
+                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
+                                    error.string());
                             goto bail;
                         }
                         printf("application: label='%s' ",
@@ -1261,9 +1130,11 @@
                             printf("testOnly='%d'\n", testOnly);
                         }
 
-                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
+                        int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
+                                DEBUGGABLE_ATTR, 0, &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
+                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
+                                    error.string());
                             goto bail;
                         }
                         if (debuggable != 0) {
@@ -1284,10 +1155,11 @@
                             }
                         }
                     } else if (tag == "uses-sdk") {
-                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+                        int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
                         if (error != "") {
                             error = "";
-                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
+                            String8 name = AaptXml::getResolvedAttribute(res, tree,
+                                    MIN_SDK_VERSION_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
                                         error.string());
@@ -1300,14 +1172,15 @@
                             targetSdk = code;
                             printf("sdkVersion:'%d'\n", code);
                         }
-                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
+                        code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
                         if (code != -1) {
                             printf("maxSdkVersion:'%d'\n", code);
                         }
-                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
+                        code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
                         if (error != "") {
                             error = "";
-                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
+                            String8 name = AaptXml::getResolvedAttribute(res, tree,
+                                    TARGET_SDK_VERSION_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
                                         error.string());
@@ -1323,16 +1196,16 @@
                             printf("targetSdkVersion:'%d'\n", code);
                         }
                     } else if (tag == "uses-configuration") {
-                        int32_t reqTouchScreen = getIntegerAttribute(tree,
-                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
-                        int32_t reqKeyboardType = getIntegerAttribute(tree,
-                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
-                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
-                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
-                        int32_t reqNavigation = getIntegerAttribute(tree,
-                                REQ_NAVIGATION_ATTR, NULL, 0);
-                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
-                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
+                        int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
+                                REQ_TOUCH_SCREEN_ATTR, 0);
+                        int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
+                                REQ_KEYBOARD_TYPE_ATTR, 0);
+                        int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
+                                REQ_HARD_KEYBOARD_ATTR, 0);
+                        int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
+                                REQ_NAVIGATION_ATTR, 0);
+                        int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
+                                REQ_FIVE_WAY_NAV_ATTR, 0);
                         printf("uses-configuration:");
                         if (reqTouchScreen != 0) {
                             printf(" reqTouchScreen='%d'", reqTouchScreen);
@@ -1353,26 +1226,26 @@
                     } else if (tag == "supports-input") {
                         withinSupportsInput = true;
                     } else if (tag == "supports-screens") {
-                        smallScreen = getIntegerAttribute(tree,
-                                SMALL_SCREEN_ATTR, NULL, 1);
-                        normalScreen = getIntegerAttribute(tree,
-                                NORMAL_SCREEN_ATTR, NULL, 1);
-                        largeScreen = getIntegerAttribute(tree,
-                                LARGE_SCREEN_ATTR, NULL, 1);
-                        xlargeScreen = getIntegerAttribute(tree,
-                                XLARGE_SCREEN_ATTR, NULL, 1);
-                        anyDensity = getIntegerAttribute(tree,
-                                ANY_DENSITY_ATTR, NULL, 1);
-                        requiresSmallestWidthDp = getIntegerAttribute(tree,
-                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
-                        compatibleWidthLimitDp = getIntegerAttribute(tree,
-                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
-                        largestWidthLimitDp = getIntegerAttribute(tree,
-                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+                        smallScreen = AaptXml::getIntegerAttribute(tree,
+                                SMALL_SCREEN_ATTR, 1);
+                        normalScreen = AaptXml::getIntegerAttribute(tree,
+                                NORMAL_SCREEN_ATTR, 1);
+                        largeScreen = AaptXml::getIntegerAttribute(tree,
+                                LARGE_SCREEN_ATTR, 1);
+                        xlargeScreen = AaptXml::getIntegerAttribute(tree,
+                                XLARGE_SCREEN_ATTR, 1);
+                        anyDensity = AaptXml::getIntegerAttribute(tree,
+                                ANY_DENSITY_ATTR, 1);
+                        requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
+                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
+                        compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
+                        largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+                                LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
                     } else if (tag == "feature-group") {
                         withinFeatureGroup = true;
                         FeatureGroup group;
-                        group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                        group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:label' attribute:"
                                     " %s\n", error.string());
@@ -1381,17 +1254,17 @@
                         featureGroups.add(group);
 
                     } else if (tag == "uses-feature") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            int req = getIntegerAttribute(tree,
-                                    REQUIRED_ATTR, NULL, 1);
+                            int req = AaptXml::getIntegerAttribute(tree,
+                                    REQUIRED_ATTR, 1);
 
                             commonFeatures.features.add(name, req);
                             if (req) {
                                 addParentFeatures(&commonFeatures, name);
                             }
                         } else {
-                            int vers = getIntegerAttribute(tree,
+                            int vers = AaptXml::getIntegerAttribute(tree,
                                     GL_ES_VERSION_ATTR, &error);
                             if (error == "") {
                                 if (vers > commonFeatures.openGLESVersion) {
@@ -1400,7 +1273,7 @@
                             }
                         }
                     } else if (tag == "uses-permission") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             if (name == "android.permission.CAMERA") {
                                 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
@@ -1478,15 +1351,15 @@
                             }
 
                             printUsesPermission(name,
-                                    getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
-                                    getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+                                    AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+                                    AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                        } else {
                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                     error.string());
                             goto bail;
                         }
                     } else if (tag == "uses-package") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             printf("uses-package:'%s'\n",
                                     ResTable::normalizeForOutput(name.string()).string());
@@ -1496,7 +1369,7 @@
                                 goto bail;
                         }
                     } else if (tag == "original-package") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             printf("original-package:'%s'\n",
                                     ResTable::normalizeForOutput(name.string()).string());
@@ -1506,7 +1379,7 @@
                                 goto bail;
                         }
                     } else if (tag == "supports-gl-texture") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             printf("supports-gl-texture:'%s'\n",
                                     ResTable::normalizeForOutput(name.string()).string());
@@ -1524,9 +1397,9 @@
                         }
                         depth--;
                     } else if (tag == "package-verifier") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+                            String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
                             if (publicKey != "" && error == "") {
                                 printf("package-verifier: name='%s' publicKey='%s'\n",
                                         ResTable::normalizeForOutput(name.string()).string(),
@@ -1553,35 +1426,38 @@
                     if (withinApplication) {
                         if(tag == "activity") {
                             withinActivity = true;
-                            activityName = getAttribute(tree, NAME_ATTR, &error);
+                            activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                            activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+                                    &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                            activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+                                    &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
+                            activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+                                    &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            int32_t orien = getResolvedIntegerAttribute(&res, tree,
+                            int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
                                     SCREEN_ORIENTATION_ATTR, &error);
                             if (error == "") {
                                 if (orien == 0 || orien == 6 || orien == 8) {
@@ -1595,21 +1471,21 @@
                                 }
                             }
                         } else if (tag == "uses-library") {
-                            String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
+                            String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr,
                                         "ERROR getting 'android:name' attribute for uses-library"
                                         " %s\n", error.string());
                                 goto bail;
                             }
-                            int req = getIntegerAttribute(tree,
-                                    REQUIRED_ATTR, NULL, 1);
+                            int req = AaptXml::getIntegerAttribute(tree,
+                                    REQUIRED_ATTR, 1);
                             printf("uses-library%s:'%s'\n",
                                     req ? "" : "-not-required", ResTable::normalizeForOutput(
                                             libraryName.string()).string());
                         } else if (tag == "receiver") {
                             withinReceiver = true;
-                            receiverName = getAttribute(tree, NAME_ATTR, &error);
+                            receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
 
                             if (error != "") {
                                 fprintf(stderr,
@@ -1618,7 +1494,8 @@
                                 goto bail;
                             }
 
-                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+                            String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+                                    &error);
                             if (error == "") {
                                 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
                                     hasBindDeviceAdminPermission = true;
@@ -1629,7 +1506,7 @@
                             }
                         } else if (tag == "service") {
                             withinService = true;
-                            serviceName = getAttribute(tree, NAME_ATTR, &error);
+                            serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
 
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:name' attribute for "
@@ -1637,7 +1514,8 @@
                                 goto bail;
                             }
 
-                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+                            String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+                                    &error);
                             if (error == "") {
                                 if (permission == "android.permission.BIND_INPUT_METHOD") {
                                     hasBindInputMethodPermission = true;
@@ -1659,22 +1537,24 @@
                         } else if (tag == "provider") {
                             withinProvider = true;
 
-                            bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
+                            bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
+                                    EXPORTED_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
                                         " %s\n", error.string());
                                 goto bail;
                             }
 
-                            bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
-                                    GRANT_URI_PERMISSIONS_ATTR, &error);
+                            bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
+                                    res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
                                         " %s\n", error.string());
                                 goto bail;
                             }
 
-                            String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
+                            String8 permission = AaptXml::getResolvedAttribute(res, tree,
+                                    PERMISSION_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
                                         " %s\n", error.string());
@@ -1685,7 +1565,8 @@
                                 permission == "android.permission.MANAGE_DOCUMENTS";
 
                         } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
-                            String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+                            String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
+                                    NAME_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:name' attribute for "
                                         "meta-data:%s\n", error.string());
@@ -1693,12 +1574,12 @@
                             }
                             printf("meta-data: name='%s' ",
                                     ResTable::normalizeForOutput(metaDataName.string()).string());
-                            printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
+                            printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
                                     &error);
                             if (error != "") {
                                 // Try looking for a RESOURCE_ATTR
                                 error = "";
-                                printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
+                                printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
                                         String8("resource"), &error);
                                 if (error != "") {
                                     fprintf(stderr, "ERROR getting 'android:value' or "
@@ -1709,7 +1590,7 @@
                             }
                             printf("\n");
                         } else if (withinSupportsInput && tag == "input-type") {
-                            String8 name = getAttribute(tree, NAME_ATTR, &error);
+                            String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                             if (name != "" && error == "") {
                                 supportedInput.add(name);
                             } else {
@@ -1721,12 +1602,13 @@
                     } else if (withinFeatureGroup && tag == "uses-feature") {
                         FeatureGroup& top = featureGroups.editTop();
 
-                        String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             top.features.add(name, true);
                             addParentFeatures(&top, name);
                         } else {
-                            int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
+                            int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
+                                    &error);
                             if (error == "") {
                                 if (vers > top.openGLESVersion) {
                                     top.openGLESVersion = vers;
@@ -1754,7 +1636,7 @@
                         actCameraSecure = false;
                         catLauncher = false;
                     } else if (withinService && tag == "meta-data") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:name' attribute for"
                                     " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1768,7 +1650,8 @@
                                 offHost = false;
                             }
 
-                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
+                            String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
+                                    RESOURCE_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
                                         " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1797,7 +1680,7 @@
                 } else if ((depth == 5) && withinIntentFilter) {
                     String8 action;
                     if (tag == "action") {
-                        action = getAttribute(tree, NAME_ATTR, &error);
+                        action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                     error.string());
@@ -1849,7 +1732,7 @@
                     }
 
                     if (tag == "category") {
-                        String8 category = getAttribute(tree, NAME_ATTR, &error);
+                        String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
                                     error.string());
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index dd40b20..f24a023b 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -60,9 +60,6 @@
 
 int dumpResources(Bundle* bundle);
 
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
-                            const char* attr, String8* outError);
-
 status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
                                 FILE* fp, bool includeRaw);
 #endif // __MAIN_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7979a1d..5deeca2 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -4,6 +4,7 @@
 // Build resource files from raw assets.
 //
 #include "AaptAssets.h"
+#include "AaptXml.h"
 #include "CacheUpdater.h"
 #include "CrunchCache.h"
 #include "FileFinder.h"
@@ -805,6 +806,20 @@
         }
     }
 
+    if (bundle->getPlatformBuildVersionCode() != "") {
+        if (!addTagAttribute(root, "", "platformBuildVersionCode",
+                    bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    if (bundle->getPlatformBuildVersionName() != "") {
+        if (!addTagAttribute(root, "", "platformBuildVersionName",
+                    bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
     if (bundle->getDebugMode()) {
         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
         if (application != NULL) {
@@ -881,6 +896,106 @@
     return NO_ERROR;
 }
 
+static int32_t getPlatformAssetCookie(const AssetManager& assets) {
+    // Find the system package (0x01). AAPT always generates attributes
+    // with the type 0x01, so we're looking for the first attribute
+    // resource in the system package.
+    const ResTable& table = assets.getResources(true);
+    Res_value val;
+    ssize_t idx = table.getResource(0x01010000, &val, true);
+    if (idx != NO_ERROR) {
+        // Try as a bag.
+        const ResTable::bag_entry* entry;
+        ssize_t cnt = table.lockBag(0x01010000, &entry);
+        if (cnt >= 0) {
+            idx = entry->stringBlock;
+        }
+        table.unlockBag(entry);
+    }
+
+    if (idx < 0) {
+        return 0;
+    }
+    return table.getTableCookie(idx);
+}
+
+enum {
+    VERSION_CODE_ATTR = 0x0101021b,
+    VERSION_NAME_ATTR = 0x0101021c,
+};
+
+static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
+    size_t len;
+    ResXMLTree::event_code_t code;
+    while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code != ResXMLTree::START_TAG) {
+            continue;
+        }
+
+        const char16_t* ctag16 = tree.getElementName(&len);
+        if (ctag16 == NULL) {
+            fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
+            return UNKNOWN_ERROR;
+        }
+
+        String8 tag(ctag16, len);
+        if (tag != "manifest") {
+            continue;
+        }
+
+        String8 error;
+        int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+        if (error != "") {
+            fprintf(stderr, "ERROR: failed to get platform version code\n");
+            return UNKNOWN_ERROR;
+        }
+
+        if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") {
+            bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
+        }
+
+        String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
+        if (error != "") {
+            fprintf(stderr, "ERROR: failed to get platform version name\n");
+            return UNKNOWN_ERROR;
+        }
+
+        if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
+            bundle->setPlatformBuildVersionName(versionName);
+        }
+        return NO_ERROR;
+    }
+
+    fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n");
+    return UNKNOWN_ERROR;
+}
+
+static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) {
+    int32_t cookie = getPlatformAssetCookie(assets);
+    if (cookie == 0) {
+        fprintf(stderr, "ERROR: Platform package not found\n");
+        return UNKNOWN_ERROR;
+    }
+
+    ResXMLTree tree;
+    Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING);
+    if (asset == NULL) {
+        fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n");
+        return UNKNOWN_ERROR;
+    }
+
+    ssize_t result = NO_ERROR;
+    if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
+        fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
+        result = UNKNOWN_ERROR;
+    } else {
+        result = extractPlatformBuildVersion(tree, bundle);
+    }
+
+    delete asset;
+    return result;
+}
+
 #define ASSIGN_IT(n) \
         do { \
             ssize_t index = resources->indexOfKey(String8(#n)); \
@@ -1356,6 +1471,17 @@
         return UNKNOWN_ERROR;
     }
 
+    // If we're not overriding the platform build versions,
+    // extract them from the platform APK.
+    if (packageType != ResourceTable::System &&
+            (bundle->getPlatformBuildVersionCode() == "" ||
+            bundle->getPlatformBuildVersionName() == "")) {
+        err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
+        if (err != NO_ERROR) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
     String8 manifestPath(manifestFile->getPrintableSource());
 
@@ -2636,13 +2762,14 @@
                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                 return -1;
             }
-            pkg = getAttribute(tree, NULL, "package", NULL);
+            pkg = AaptXml::getAttribute(tree, NULL, "package");
         } else if (depth == 2) {
             if (tag == "application") {
                 inApplication = true;
                 keepTag = true;
 
-                String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+                String8 agent = AaptXml::getAttribute(tree,
+                        "http://schemas.android.com/apk/res/android",
                         "backupAgent", &error);
                 if (agent.length() > 0) {
                     addProguardKeepRule(keep, agent, pkg.string(),
@@ -2658,8 +2785,8 @@
             }
         }
         if (keepTag) {
-            String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
-                    "name", &error);
+            String8 name = AaptXml::getAttribute(tree,
+                    "http://schemas.android.com/apk/res/android", "name", &error);
             if (error != "") {
                 fprintf(stderr, "ERROR: %s\n", error.string());
                 return -1;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
new file mode 100644
index 0000000..fce4323
--- /dev/null
+++ b/tools/apilint/apilint.py
@@ -0,0 +1,540 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Enforces common Android public API design patterns.  It ignores lint messages from
+a previous API level, if provided.
+
+Usage: apilint.py current.txt
+Usage: apilint.py current.txt previous.txt
+"""
+
+import re, sys
+
+
+BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
+
+def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
+    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
+    codes = []
+    if reset: codes.append("0")
+    else:
+        if not fg is None: codes.append("3%d" % (fg))
+        if not bg is None:
+            if not bright: codes.append("4%d" % (bg))
+            else: codes.append("10%d" % (bg))
+        if bold: codes.append("1")
+        elif dim: codes.append("2")
+        else: codes.append("22")
+    return "\033[%sm" % (";".join(codes))
+
+
+class Field():
+    def __init__(self, clazz, raw):
+        self.clazz = clazz
+        self.raw = raw.strip(" {;")
+
+        raw = raw.split()
+        self.split = list(raw)
+
+        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
+            while r in raw: raw.remove(r)
+
+        self.typ = raw[0]
+        self.name = raw[1].strip(";")
+        if len(raw) >= 4 and raw[2] == "=":
+            self.value = raw[3].strip(';"')
+        else:
+            self.value = None
+
+    def __repr__(self):
+        return self.raw
+
+
+class Method():
+    def __init__(self, clazz, raw):
+        self.clazz = clazz
+        self.raw = raw.strip(" {;")
+
+        raw = re.split("[\s(),;]+", raw)
+        for r in ["", ";"]:
+            while r in raw: raw.remove(r)
+        self.split = list(raw)
+
+        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]:
+            while r in raw: raw.remove(r)
+
+        self.typ = raw[0]
+        self.name = raw[1]
+        self.args = []
+        for r in raw[2:]:
+            if r == "throws": break
+            self.args.append(r)
+
+    def __repr__(self):
+        return self.raw
+
+
+class Class():
+    def __init__(self, pkg, raw):
+        self.pkg = pkg
+        self.raw = raw.strip(" {;")
+        self.ctors = []
+        self.fields = []
+        self.methods = []
+
+        raw = raw.split()
+        self.split = list(raw)
+        if "class" in raw:
+            self.fullname = raw[raw.index("class")+1]
+        elif "interface" in raw:
+            self.fullname = raw[raw.index("interface")+1]
+
+        if "." in self.fullname:
+            self.name = self.fullname[self.fullname.rindex(".")+1:]
+        else:
+            self.name = self.fullname
+
+    def __repr__(self):
+        return self.raw
+
+
+class Package():
+    def __init__(self, raw):
+        self.raw = raw.strip(" {;")
+
+        raw = raw.split()
+        self.name = raw[raw.index("package")+1]
+
+    def __repr__(self):
+        return self.raw
+
+
+def parse_api(fn):
+    api = []
+    pkg = None
+    clazz = None
+
+    with open(fn) as f:
+        for raw in f.readlines():
+            raw = raw.rstrip()
+
+            if raw.startswith("package"):
+                pkg = Package(raw)
+            elif raw.startswith("  ") and raw.endswith("{"):
+                clazz = Class(pkg, raw)
+                api.append(clazz)
+            elif raw.startswith("    ctor"):
+                clazz.ctors.append(Method(clazz, raw))
+            elif raw.startswith("    method"):
+                clazz.methods.append(Method(clazz, raw))
+            elif raw.startswith("    field"):
+                clazz.fields.append(Field(clazz, raw))
+
+    return api
+
+
+failures = []
+
+def _fail(clazz, detail, msg):
+    """Records an API failure to be processed later."""
+    global failures
+
+    res = msg
+    if detail is not None:
+        res += "\n    in " + repr(detail)
+    res += "\n    in " + repr(clazz)
+    res += "\n    in " + repr(clazz.pkg)
+    failures.append(res)
+
+def warn(clazz, detail, msg):
+    _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK), format(reset=True), msg))
+
+def error(clazz, detail, msg):
+    _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK), format(reset=True), msg))
+
+
+def verify_constants(clazz):
+    """All static final constants must be FOO_NAME style."""
+    if re.match("R\.[a-z]+", clazz.fullname): return
+
+    for f in clazz.fields:
+        if "static" in f.split and "final" in f.split:
+            if re.match("[A-Z0-9_]+", f.name) is None:
+                error(clazz, f, "Constant field names should be FOO_NAME")
+
+
+def verify_enums(clazz):
+    """Enums are bad, mmkay?"""
+    if "extends java.lang.Enum" in clazz.raw:
+        error(clazz, None, "Enums are not allowed")
+
+
+def verify_class_names(clazz):
+    """Try catching malformed class names like myMtp or MTPUser."""
+    if re.search("[A-Z]{2,}", clazz.name) is not None:
+        warn(clazz, None, "Class name style should be Mtp not MTP")
+    if re.match("[^A-Z]", clazz.name):
+        error(clazz, None, "Class must start with uppercase char")
+
+
+def verify_method_names(clazz):
+    """Try catching malformed method names, like Foo() or getMTU()."""
+    if clazz.pkg.name == "android.opengl": return
+
+    for m in clazz.methods:
+        if re.search("[A-Z]{2,}", m.name) is not None:
+            warn(clazz, m, "Method name style should be getMtu() instead of getMTU()")
+        if re.match("[^a-z]", m.name):
+            error(clazz, None, "Method name must start with lowercase char")
+
+
+def verify_callbacks(clazz):
+    """Verify Callback classes.
+    All callback classes must be abstract.
+    All methods must follow onFoo() naming style."""
+
+    if clazz.name.endswith("Callbacks"):
+        error(clazz, None, "Class must be named exactly Callback")
+    if clazz.name.endswith("Observer"):
+        warn(clazz, None, "Class should be named Callback")
+
+    if clazz.name.endswith("Callback"):
+        if "interface" in clazz.split:
+            error(clazz, None, "Callback must be abstract class")
+
+        for m in clazz.methods:
+            if not re.match("on[A-Z][a-z]*", m.name):
+                error(clazz, m, "Callback method names must be onFoo style")
+
+
+def verify_listeners(clazz):
+    """Verify Listener classes.
+    All Listener classes must be interface.
+    All methods must follow onFoo() naming style.
+    If only a single method, it must match class name:
+        interface OnFooListener { void onFoo() }"""
+
+    if clazz.name.endswith("Listener"):
+        if " abstract class " in clazz.raw:
+            error(clazz, None, "Listener should be interface")
+
+        for m in clazz.methods:
+            if not re.match("on[A-Z][a-z]*", m.name):
+                error(clazz, m, "Listener method names must be onFoo style")
+
+        if len(clazz.methods) == 1 and clazz.name.startswith("On"):
+            m = clazz.methods[0]
+            if (m.name + "Listener").lower() != clazz.name.lower():
+                error(clazz, m, "Single method name should match class name")
+
+
+def verify_actions(clazz):
+    """Verify intent actions.
+    All action names must be named ACTION_FOO.
+    All action values must be scoped by package and match name:
+        package android.foo {
+            String ACTION_BAR = "android.foo.action.BAR";
+        }"""
+    for f in clazz.fields:
+        if f.value is None: continue
+        if f.name.startswith("EXTRA_"): continue
+
+        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
+            if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
+                if not f.name.startswith("ACTION_"):
+                    error(clazz, f, "Intent action must be ACTION_FOO")
+                else:
+                    if clazz.name == "Intent":
+                        prefix = "android.intent.action"
+                    elif clazz.name == "Settings":
+                        prefix = "android.settings"
+                    else:
+                        prefix = clazz.pkg.name + ".action"
+                    expected = prefix + "." + f.name[7:]
+                    if f.value != expected:
+                        error(clazz, f, "Inconsistent action value")
+
+
+def verify_extras(clazz):
+    """Verify intent extras.
+    All extra names must be named EXTRA_FOO.
+    All extra values must be scoped by package and match name:
+        package android.foo {
+            String EXTRA_BAR = "android.foo.extra.BAR";
+        }"""
+    for f in clazz.fields:
+        if f.value is None: continue
+        if f.name.startswith("ACTION_"): continue
+
+        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
+            if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
+                if not f.name.startswith("EXTRA_"):
+                    error(clazz, f, "Intent extra must be EXTRA_FOO")
+                else:
+                    if clazz.name == "Intent":
+                        prefix = "android.intent.extra"
+                    else:
+                        prefix = clazz.pkg.name + ".extra"
+                    expected = prefix + "." + f.name[6:]
+                    if f.value != expected:
+                        error(clazz, f, "Inconsistent extra value")
+
+
+def verify_equals(clazz):
+    """Verify that equals() and hashCode() must be overridden together."""
+    methods = [ m.name for m in clazz.methods ]
+    eq = "equals" in methods
+    hc = "hashCode" in methods
+    if eq != hc:
+        error(clazz, None, "Must override both equals and hashCode")
+
+
+def verify_parcelable(clazz):
+    """Verify that Parcelable objects aren't hiding required bits."""
+    if "implements android.os.Parcelable" in clazz.raw:
+        creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
+        write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
+        describe = [ i for i in clazz.methods if i.name == "describeContents" ]
+
+        if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
+            error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents")
+
+
+def verify_protected(clazz):
+    """Verify that no protected methods are allowed."""
+    for m in clazz.methods:
+        if "protected" in m.split:
+            error(clazz, m, "Protected method")
+    for f in clazz.fields:
+        if "protected" in f.split:
+            error(clazz, f, "Protected field")
+
+
+def verify_fields(clazz):
+    """Verify that all exposed fields are final.
+    Exposed fields must follow myName style.
+    Catch internal mFoo objects being exposed."""
+    for f in clazz.fields:
+        if not "final" in f.split:
+            error(clazz, f, "Bare fields must be final; consider adding accessors")
+
+        if not "static" in f.split:
+            if not re.match("[a-z]([a-zA-Z]+)?", f.name):
+                error(clazz, f, "Non-static fields must be myName")
+
+        if re.match("[m][A-Z]", f.name):
+            error(clazz, f, "Don't expose your internal objects")
+
+
+def verify_register(clazz):
+    """Verify parity of registration methods.
+    Callback objects use register/unregister methods.
+    Listener objects use add/remove methods."""
+    methods = [ m.name for m in clazz.methods ]
+    for m in clazz.methods:
+        if "Callback" in m.raw:
+            if m.name.startswith("register"):
+                other = "unregister" + m.name[8:]
+                if other not in methods:
+                    error(clazz, m, "Missing unregister")
+            if m.name.startswith("unregister"):
+                other = "register" + m.name[10:]
+                if other not in methods:
+                    error(clazz, m, "Missing register")
+
+            if m.name.startswith("add") or m.name.startswith("remove"):
+                error(clazz, m, "Callback should be register/unregister")
+
+        if "Listener" in m.raw:
+            if m.name.startswith("add"):
+                other = "remove" + m.name[3:]
+                if other not in methods:
+                    error(clazz, m, "Missing remove")
+            if m.name.startswith("remove") and not m.name.startswith("removeAll"):
+                other = "add" + m.name[6:]
+                if other not in methods:
+                    error(clazz, m, "Missing add")
+
+            if m.name.startswith("register") or m.name.startswith("unregister"):
+                error(clazz, m, "Listener should be add/remove")
+
+
+def verify_sync(clazz):
+    """Verify synchronized methods aren't exposed."""
+    for m in clazz.methods:
+        if "synchronized" in m.split:
+            error(clazz, m, "Lock exposed")
+
+
+def verify_intent_builder(clazz):
+    """Verify that Intent builders are createFooIntent() style."""
+    if clazz.name == "Intent": return
+
+    for m in clazz.methods:
+        if m.typ == "android.content.Intent":
+            if m.name.startswith("create") and m.name.endswith("Intent"):
+                pass
+            else:
+                warn(clazz, m, "Should be createFooIntent()")
+
+
+def verify_helper_classes(clazz):
+    """Verify that helper classes are named consistently with what they extend."""
+    if "extends android.app.Service" in clazz.raw:
+        if not clazz.name.endswith("Service"):
+            error(clazz, None, "Inconsistent class name")
+    if "extends android.content.ContentProvider" in clazz.raw:
+        if not clazz.name.endswith("Provider"):
+            error(clazz, None, "Inconsistent class name")
+    if "extends android.content.BroadcastReceiver" in clazz.raw:
+        if not clazz.name.endswith("Receiver"):
+            error(clazz, None, "Inconsistent class name")
+
+
+def verify_builder(clazz):
+    """Verify builder classes.
+    Methods should return the builder to enable chaining."""
+    if " extends " in clazz.raw: return
+    if not clazz.name.endswith("Builder"): return
+
+    if clazz.name != "Builder":
+        warn(clazz, None, "Should be standalone Builder class")
+
+    has_build = False
+    for m in clazz.methods:
+        if m.name == "build":
+            has_build = True
+            continue
+
+        if m.name.startswith("get"): continue
+        if m.name.startswith("clear"): continue
+
+        if not m.typ.endswith(clazz.fullname):
+            warn(clazz, m, "Should return the builder")
+
+    if not has_build:
+        warn(clazz, None, "Missing build() method")
+
+
+def verify_aidl(clazz):
+    """Catch people exposing raw AIDL."""
+    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
+        error(clazz, None, "Exposing raw AIDL interface")
+
+
+def verify_internal(clazz):
+    """Catch people exposing internal classes."""
+    if clazz.pkg.name.startswith("com.android"):
+        error(clazz, None, "Exposing internal class")
+
+
+def verify_layering(clazz):
+    """Catch package layering violations.
+    For example, something in android.os depending on android.app."""
+    ranking = [
+        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
+        "android.app",
+        "android.widget",
+        "android.view",
+        "android.animation",
+        "android.provider",
+        "android.content",
+        "android.database",
+        "android.graphics",
+        "android.text",
+        "android.os",
+        "android.util"
+    ]
+
+    def rank(p):
+        for i in range(len(ranking)):
+            if isinstance(ranking[i], list):
+                for j in ranking[i]:
+                    if p.startswith(j): return i
+            else:
+                if p.startswith(ranking[i]): return i
+
+    cr = rank(clazz.pkg.name)
+    if cr is None: return
+
+    for f in clazz.fields:
+        ir = rank(f.typ)
+        if ir and ir < cr:
+            warn(clazz, f, "Field type violates package layering")
+
+    for m in clazz.methods:
+        ir = rank(m.typ)
+        if ir and ir < cr:
+            warn(clazz, m, "Method return type violates package layering")
+        for arg in m.args:
+            ir = rank(arg)
+            if ir and ir < cr:
+                warn(clazz, m, "Method argument type violates package layering")
+
+
+def verify_all(api):
+    global failures
+
+    failures = []
+    for clazz in api:
+        if clazz.pkg.name.startswith("java"): continue
+        if clazz.pkg.name.startswith("junit"): continue
+        if clazz.pkg.name.startswith("org.apache"): continue
+        if clazz.pkg.name.startswith("org.xml"): continue
+        if clazz.pkg.name.startswith("org.json"): continue
+        if clazz.pkg.name.startswith("org.w3c"): continue
+
+        verify_constants(clazz)
+        verify_enums(clazz)
+        verify_class_names(clazz)
+        verify_method_names(clazz)
+        verify_callbacks(clazz)
+        verify_listeners(clazz)
+        verify_actions(clazz)
+        verify_extras(clazz)
+        verify_equals(clazz)
+        verify_parcelable(clazz)
+        verify_protected(clazz)
+        verify_fields(clazz)
+        verify_register(clazz)
+        verify_sync(clazz)
+        verify_intent_builder(clazz)
+        verify_helper_classes(clazz)
+        verify_builder(clazz)
+        verify_aidl(clazz)
+        verify_internal(clazz)
+        verify_layering(clazz)
+
+    return failures
+
+
+cur = parse_api(sys.argv[1])
+cur_fail = verify_all(cur)
+
+if len(sys.argv) > 2:
+    prev = parse_api(sys.argv[2])
+    prev_fail = verify_all(prev)
+
+    # ignore errors from previous API level
+    for p in prev_fail:
+        if p in cur_fail:
+            cur_fail.remove(p)
+
+
+for f in cur_fail:
+    print f
+    print