am 596ce060: Merge "SysUI: Implement NotificationListenerService in StatusBar" into lmp-preview-dev

* commit '596ce060988d869ed5d6ac0f25cad79d9ab4fba6':
  SysUI: Implement NotificationListenerService in StatusBar
diff --git a/Android.mk b/Android.mk
index de93ca2..70785b1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -325,6 +325,7 @@
 	telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
+	telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/api/current.txt b/api/current.txt
index 30ca14e..3f5a090 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -749,6 +749,7 @@
     field public static final int layout_centerVertical = 16843153; // 0x1010191
     field public static final int layout_column = 16843084; // 0x101014c
     field public static final int layout_columnSpan = 16843645; // 0x101037d
+    field public static final int layout_columnWeight = 16843868; // 0x101045c
     field public static final int layout_gravity = 16842931; // 0x10100b3
     field public static final int layout_height = 16842997; // 0x10100f5
     field public static final int layout_margin = 16842998; // 0x10100f6
@@ -760,6 +761,7 @@
     field public static final int layout_marginTop = 16843000; // 0x10100f8
     field public static final int layout_row = 16843643; // 0x101037b
     field public static final int layout_rowSpan = 16843644; // 0x101037c
+    field public static final int layout_rowWeight = 16843867; // 0x101045b
     field public static final int layout_scale = 16843155; // 0x1010193
     field public static final int layout_span = 16843085; // 0x101014d
     field public static final int layout_toEndOf = 16843704; // 0x10103b8
@@ -1399,8 +1401,6 @@
     field public static final int l_resource_pad9 = 17104904; // 0x1050008
     field public static final int notification_large_icon_height = 17104902; // 0x1050006
     field public static final int notification_large_icon_width = 17104901; // 0x1050005
-    field public static final int recents_thumbnail_height = 17104913; // 0x1050011
-    field public static final int recents_thumbnail_width = 17104914; // 0x1050012
     field public static final int thumbnail_height = 17104897; // 0x1050001
     field public static final int thumbnail_width = 17104898; // 0x1050002
   }
@@ -4538,13 +4538,22 @@
     ctor public Notification.Action.Builder(android.app.Notification.Action);
     method public android.app.Notification.Action.Builder addExtras(android.os.Bundle);
     method public android.app.Notification.Action.Builder addRemoteInput(android.app.RemoteInput);
-    method public android.app.Notification.Action.Builder apply(android.app.Notification.Action.Builder.Extender);
     method public android.app.Notification.Action build();
+    method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
     method public android.os.Bundle getExtras();
   }
 
-  public static abstract interface Notification.Action.Builder.Extender {
-    method public abstract android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
+  public static abstract interface Notification.Action.Extender {
+    method public abstract android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
+  }
+
+  public static final class Notification.Action.WearableExtender implements android.app.Notification.Action.Extender {
+    ctor public Notification.Action.WearableExtender();
+    ctor public Notification.Action.WearableExtender(android.app.Notification.Action);
+    method public android.app.Notification.Action.WearableExtender clone();
+    method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
+    method public boolean isAvailableOffline();
+    method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
   }
 
   public static class Notification.BigPictureStyle extends android.app.Notification.Style {
@@ -4570,8 +4579,8 @@
     method public android.app.Notification.Builder addAction(android.app.Notification.Action);
     method public android.app.Notification.Builder addExtras(android.os.Bundle);
     method public android.app.Notification.Builder addPerson(java.lang.String);
-    method public android.app.Notification.Builder apply(android.app.Notification.Builder.Extender);
     method public android.app.Notification build();
+    method public android.app.Notification.Builder extend(android.app.Notification.Extender);
     method public android.os.Bundle getExtras();
     method public deprecated android.app.Notification getNotification();
     method public android.app.Notification.Builder setAutoCancel(boolean);
@@ -4613,8 +4622,8 @@
     method public android.app.Notification.Builder setWhen(long);
   }
 
-  public static abstract interface Notification.Builder.Extender {
-    method public abstract android.app.Notification.Builder applyTo(android.app.Notification.Builder);
+  public static abstract interface Notification.Extender {
+    method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
   }
 
   public static class Notification.InboxStyle extends android.app.Notification.Style {
@@ -4644,6 +4653,51 @@
     field protected android.app.Notification.Builder mBuilder;
   }
 
+  public static final class Notification.WearableExtender implements android.app.Notification.Extender {
+    ctor public Notification.WearableExtender();
+    ctor public Notification.WearableExtender(android.app.Notification);
+    method public android.app.Notification.WearableExtender addAction(android.app.Notification.Action);
+    method public android.app.Notification.WearableExtender addActions(java.util.List<android.app.Notification.Action>);
+    method public android.app.Notification.WearableExtender addPage(android.app.Notification);
+    method public android.app.Notification.WearableExtender addPages(java.util.List<android.app.Notification>);
+    method public android.app.Notification.WearableExtender clearActions();
+    method public android.app.Notification.WearableExtender clearPages();
+    method public android.app.Notification.WearableExtender clone();
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.util.List<android.app.Notification.Action> getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintHideIcon();
+    method public boolean getHintShowBackgroundOnly();
+    method public java.util.List<android.app.Notification> getPages();
+    method public boolean getStartScrollBottom();
+    method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+    method public android.app.Notification.WearableExtender setContentAction(int);
+    method public android.app.Notification.WearableExtender setContentIcon(int);
+    method public android.app.Notification.WearableExtender setContentIconGravity(int);
+    method public android.app.Notification.WearableExtender setContentIntentAvailableOffline(boolean);
+    method public android.app.Notification.WearableExtender setCustomContentHeight(int);
+    method public android.app.Notification.WearableExtender setCustomSizePreset(int);
+    method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
+    method public android.app.Notification.WearableExtender setGravity(int);
+    method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
+    method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_LARGE = 4; // 0x4
+    field public static final int SIZE_MEDIUM = 3; // 0x3
+    field public static final int SIZE_SMALL = 2; // 0x2
+    field public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
   public class NotificationManager {
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
@@ -5106,6 +5160,7 @@
     method public void clearForwardingIntentFilters(android.content.ComponentName);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+    method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
@@ -5139,6 +5194,7 @@
     method public boolean isProfileOwnerApp(java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
+    method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public boolean setApplicationBlocked(android.content.ComponentName, java.lang.String, boolean);
@@ -5315,80 +5371,6 @@
 
 }
 
-package android.app.wearable {
-
-  public final class WearableActionExtensions implements android.app.Notification.Action.Builder.Extender android.os.Parcelable {
-    method public android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
-    method public int describeContents();
-    method public static android.app.wearable.WearableActionExtensions from(android.app.Notification.Action);
-    method public boolean isAvailableOffline();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static final class WearableActionExtensions.Builder {
-    ctor public WearableActionExtensions.Builder();
-    ctor public WearableActionExtensions.Builder(android.app.wearable.WearableActionExtensions);
-    method public android.app.wearable.WearableActionExtensions build();
-    method public android.app.wearable.WearableActionExtensions.Builder setAvailableOffline(boolean);
-  }
-
-  public final class WearableNotificationExtensions implements android.app.Notification.Builder.Extender android.os.Parcelable {
-    method public android.app.Notification.Builder applyTo(android.app.Notification.Builder);
-    method public int describeContents();
-    method public static android.app.wearable.WearableNotificationExtensions from(android.app.Notification);
-    method public android.app.Notification.Action getAction(int);
-    method public int getActionCount();
-    method public android.app.Notification.Action[] getActions();
-    method public android.graphics.Bitmap getBackground();
-    method public int getContentAction();
-    method public int getContentIcon();
-    method public int getContentIconGravity();
-    method public boolean getContentIntentAvailableOffline();
-    method public int getCustomContentHeight();
-    method public int getCustomSizePreset();
-    method public android.app.PendingIntent getDisplayIntent();
-    method public int getGravity();
-    method public boolean getHintHideIcon();
-    method public boolean getHintShowBackgroundOnly();
-    method public android.app.Notification[] getPages();
-    method public boolean getStartScrollBottom();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int SIZE_DEFAULT = 0; // 0x0
-    field public static final int SIZE_LARGE = 4; // 0x4
-    field public static final int SIZE_MEDIUM = 3; // 0x3
-    field public static final int SIZE_SMALL = 2; // 0x2
-    field public static final int SIZE_XSMALL = 1; // 0x1
-    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
-  }
-
-  public static final class WearableNotificationExtensions.Builder {
-    ctor public WearableNotificationExtensions.Builder();
-    ctor public WearableNotificationExtensions.Builder(android.app.wearable.WearableNotificationExtensions);
-    method public android.app.wearable.WearableNotificationExtensions.Builder addAction(android.app.Notification.Action);
-    method public android.app.wearable.WearableNotificationExtensions.Builder addActions(java.util.List<android.app.Notification.Action>);
-    method public android.app.wearable.WearableNotificationExtensions.Builder addPage(android.app.Notification);
-    method public android.app.wearable.WearableNotificationExtensions.Builder addPages(java.util.List<android.app.Notification>);
-    method public android.app.wearable.WearableNotificationExtensions build();
-    method public android.app.wearable.WearableNotificationExtensions.Builder clearActions();
-    method public android.app.wearable.WearableNotificationExtensions.Builder clearPages();
-    method public android.app.wearable.WearableNotificationExtensions.Builder setBackground(android.graphics.Bitmap);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setContentAction(int);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setContentIcon(int);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setContentIconGravity(int);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setContentIntentAvailableOffline(boolean);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setCustomContentHeight(int);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setCustomSizePreset(int);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setDisplayIntent(android.app.PendingIntent);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setGravity(int);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setHintHideIcon(boolean);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setHintShowBackgroundOnly(boolean);
-    method public android.app.wearable.WearableNotificationExtensions.Builder setStartScrollBottom(boolean);
-  }
-
-}
-
 package android.appwidget {
 
   public class AppWidgetHost {
@@ -11952,7 +11934,6 @@
     method public int getMinDelay();
     method public java.lang.String getName();
     method public float getPower();
-    method public java.lang.String getRequiredPermission();
     method public float getResolution();
     method public java.lang.String getStringType();
     method public int getType();
@@ -17121,11 +17102,9 @@
   }
 
   public static final class WifiEnterpriseConfig.Eap {
-    field public static final int AKA = 5; // 0x5
     field public static final int NONE = -1; // 0xffffffff
     field public static final int PEAP = 0; // 0x0
     field public static final int PWD = 3; // 0x3
-    field public static final int SIM = 4; // 0x4
     field public static final int TLS = 1; // 0x1
     field public static final int TTLS = 2; // 0x2
   }
@@ -17545,14 +17524,17 @@
 package android.net.wifi.passpoint {
 
   public class WifiPasspointCredential implements android.os.Parcelable {
-    ctor public WifiPasspointCredential(java.lang.String, android.net.wifi.WifiEnterpriseConfig);
+    ctor public WifiPasspointCredential(java.lang.String, java.lang.String, android.net.wifi.WifiEnterpriseConfig);
     method public int describeContents();
+    method public java.lang.String getCaRootCertPath();
     method public java.lang.String getClientCertPath();
     method public int getEapMethod();
     method public java.lang.String getFqdn();
-    method public java.lang.String getImsi();
     method public java.lang.String getRealm();
     method public java.lang.String getUserName();
+    method public void setCredential(android.net.wifi.WifiEnterpriseConfig);
+    method public void setFqdn(java.lang.String);
+    method public void setRealm(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
   }
 
@@ -25059,7 +25041,9 @@
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
     field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+    field public static final java.lang.String COLUMN_LOCKED = "locked";
     field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
     field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
     field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
@@ -25097,10 +25081,12 @@
   }
 
   public static final class TvContract.Programs implements android.provider.TvContract.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
     field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
     field public static final java.lang.String COLUMN_DATA = "data";
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_GENRE = "genre";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
     field public static final java.lang.String COLUMN_TITLE = "title";
@@ -32902,6 +32888,7 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method public final void requestUnbufferedDispatch(android.view.MotionEvent);
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
     method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
@@ -36595,6 +36582,10 @@
     method public void setRowCount(int);
     method public void setRowOrderPreserved(boolean);
     method public void setUseDefaultMargins(boolean);
+    method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment, float);
+    method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment, float);
+    method public static android.widget.GridLayout.Spec spec(int, int, float);
+    method public static android.widget.GridLayout.Spec spec(int, float);
     method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment);
     method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment);
     method public static android.widget.GridLayout.Spec spec(int, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5fd288f..d9adba3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3010,8 +3010,8 @@
                 int h;
                 if (w < 0) {
                     Resources res = r.activity.getResources();
-                    int wId = com.android.internal.R.dimen.recents_thumbnail_width;
-                    int hId = com.android.internal.R.dimen.recents_thumbnail_height;
+                    int wId = com.android.internal.R.dimen.thumbnail_width;
+                    int hId = com.android.internal.R.dimen.thumbnail_height;
                     mThumbnailWidth = w = res.getDimensionPixelSize(wId);
                     mThumbnailHeight = h = res.getDimensionPixelSize(hId);
                 } else {
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 7617886..d08978bd 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -189,15 +189,17 @@
     final protected SharedElementListener mListener;
     protected ResultReceiver mResultReceiver;
     final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
+    final protected boolean mIsReturning;
 
     public ActivityTransitionCoordinator(Window window,
             ArrayList<String> allSharedElementNames,
             ArrayList<String> accepted, ArrayList<String> localNames,
-            SharedElementListener listener) {
+            SharedElementListener listener, boolean isReturning) {
         super(new Handler());
         mWindow = window;
         mListener = listener;
         mAllSharedElementNames = allSharedElementNames;
+        mIsReturning = isReturning;
         setSharedElements(accepted, localNames);
         if (getViewsTransition() != null) {
             getDecor().captureTransitioningViews(mTransitioningViews);
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 4cca355..bc97852 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -55,16 +55,14 @@
     private boolean mHasStopped;
     private Handler mHandler;
     private boolean mIsCanceled;
-    private boolean mIsReturning;
     private ObjectAnimator mBackgroundAnimator;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames,
             ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
         super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
-                getListener(activity, acceptedNames));
+                getListener(activity, acceptedNames), acceptedNames != null);
         mActivity = activity;
-        mIsReturning = acceptedNames != null;
         setResultReceiver(resultReceiver);
         prepareEnter();
         Bundle resultReceiverBundle = new Bundle();
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f36c36a..93eb53e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -58,16 +58,14 @@
 
     private Handler mHandler;
 
-    private boolean mIsReturning;
-
     private ObjectAnimator mBackgroundAnimator;
 
     private boolean mIsHidden;
 
     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
-        super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning));
-        mIsReturning = isReturning;
+        super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
+                isReturning);
         mIsBackgroundReady = !isReturning;
         mActivity = activity;
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index dfd927f..6e23b11 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -35,6 +35,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
@@ -46,7 +47,9 @@
 import java.lang.annotation.RetentionPolicy;
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * A class that represents how a persistent notification is to be presented to
@@ -767,7 +770,7 @@
      */
     public static class Action implements Parcelable {
         private final Bundle mExtras;
-        private RemoteInput[] mRemoteInputs;
+        private final RemoteInput[] mRemoteInputs;
 
         /**
          * Small icon representing the action.
@@ -910,25 +913,12 @@
              * Apply an extender to this action builder. Extenders may be used to add
              * metadata or change options on this builder.
              */
-            public Builder apply(Extender extender) {
-                extender.applyTo(this);
+            public Builder extend(Extender extender) {
+                extender.extend(this);
                 return this;
             }
 
             /**
-             * Extender interface for use with {@link #apply}. Extenders may be used to add
-             * metadata or change options on this builder.
-             */
-            public interface Extender {
-                /**
-                 * Apply this extender to a notification action builder.
-                 * @param builder the builder to be modified.
-                 * @return the build object for chaining.
-                 */
-                public Builder applyTo(Builder builder);
-            }
-
-            /**
              * Combine all of the options that have been set and return a new {@link Action}
              * object.
              * @return the built action
@@ -975,6 +965,121 @@
                 return new Action[size];
             }
         };
+
+        /**
+         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
+         * metadata or change options on an action builder.
+         */
+        public interface Extender {
+            /**
+             * Apply this extender to a notification action builder.
+             * @param builder the builder to be modified.
+             * @return the build object for chaining.
+             */
+            public Builder extend(Builder builder);
+        }
+
+        /**
+         * Wearable extender for notification actions. To add extensions to an action,
+         * create a new {@link android.app.Notification.Action.WearableExtender} object using
+         * the {@code WearableExtender()} constructor and apply it to a
+         * {@link android.app.Notification.Action.Builder} using
+         * {@link android.app.Notification.Action.Builder#extend}.
+         *
+         * <pre class="prettyprint">
+         * Notification.Action action = new Notification.Action.Builder(
+         *         R.drawable.archive_all, "Archive all", actionIntent)
+         *         .apply(new Notification.Action.WearableExtender()
+         *                 .setAvailableOffline(false))
+         *         .build();
+         * </pre>
+         */
+        public static final class WearableExtender implements Extender {
+            /** Notification action extra which contains wearable extensions */
+            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+            private static final String KEY_FLAGS = "flags";
+
+            // Flags bitwise-ored to mFlags
+            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
+
+            // Default value for flags integer
+            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
+
+            private int mFlags = DEFAULT_FLAGS;
+
+            /**
+             * Create a {@link android.app.Notification.Action.WearableExtender} with default
+             * options.
+             */
+            public WearableExtender() {
+            }
+
+            /**
+             * Create a {@link android.app.Notification.Action.WearableExtender} by reading
+             * wearable options present in an existing notification action.
+             * @param action the notification action to inspect.
+             */
+            public WearableExtender(Action action) {
+                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
+                if (wearableBundle != null) {
+                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
+                }
+            }
+
+            /**
+             * Apply wearable extensions to a notification action that is being built. This is
+             * typically called by the {@link android.app.Notification.Action.Builder#extend}
+             * method of {@link android.app.Notification.Action.Builder}.
+             */
+            @Override
+            public Action.Builder extend(Action.Builder builder) {
+                Bundle wearableBundle = new Bundle();
+
+                if (mFlags != DEFAULT_FLAGS) {
+                    wearableBundle.putInt(KEY_FLAGS, mFlags);
+                }
+
+                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
+                return builder;
+            }
+
+            @Override
+            public WearableExtender clone() {
+                WearableExtender that = new WearableExtender();
+                that.mFlags = this.mFlags;
+                return that;
+            }
+
+            /**
+             * Set whether this action is available when the wearable device is not connected to
+             * a companion device. The user can still trigger this action when the wearable device is
+             * offline, but a visual hint will indicate that the action may not be available.
+             * Defaults to true.
+             */
+            public WearableExtender setAvailableOffline(boolean availableOffline) {
+                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
+                return this;
+            }
+
+            /**
+             * Get whether this action is available when the wearable device is not connected to
+             * a companion device. The user can still trigger this action when the wearable device is
+             * offline, but a visual hint will indicate that the action may not be available.
+             * Defaults to true.
+             */
+            public boolean isAvailableOffline() {
+                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
+            }
+
+            private void setFlag(int mask, boolean value) {
+                if (value) {
+                    mFlags |= mask;
+                } else {
+                    mFlags &= ~mask;
+                }
+            }
+        }
     }
 
     /**
@@ -2169,24 +2274,11 @@
          * Apply an extender to this notification builder. Extenders may be used to add
          * metadata or change options on this builder.
          */
-        public Builder apply(Extender extender) {
-            extender.applyTo(this);
+        public Builder extend(Extender extender) {
+            extender.extend(this);
             return this;
         }
 
-        /**
-         * Extender interface for use with {@link #apply}. Extenders may be used to add
-         * metadata or change options on this builder.
-         */
-        public interface Extender {
-            /**
-             * Apply this extender to a notification builder.
-             * @param builder the builder to be modified.
-             * @return the build object for chaining.
-             */
-            public Builder applyTo(Builder builder);
-        }
-
         private void setFlag(int mask, boolean value) {
             if (value) {
                 mFlags |= mask;
@@ -3163,4 +3255,634 @@
             return big;
         }
     }
+
+    /**
+     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
+     * metadata or change options on a notification builder.
+     */
+    public interface Extender {
+        /**
+         * Apply this extender to a notification builder.
+         * @param builder the builder to be modified.
+         * @return the build object for chaining.
+         */
+        public Builder extend(Builder builder);
+    }
+
+    /**
+     * Helper class to add wearable extensions to notifications.
+     * <p class="note"> See
+     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
+     * for Android Wear</a> for more information on how to use this class.
+     * <p>
+     * To create a notification with wearable extensions:
+     * <ol>
+     *   <li>Create a {@link android.app.Notification.Builder}, setting any desired
+     *   properties.
+     *   <li>Create a {@link android.app.Notification.WearableExtender}.
+     *   <li>Set wearable-specific properties using the
+     *   {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}.
+     *   <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a
+     *   notification.
+     *   <li>Post the notification to the notification system with the
+     *   {@code NotificationManager.notify(...)} methods.
+     * </ol>
+     *
+     * <pre class="prettyprint">
+     * Notification notif = new Notification.Builder(mContext)
+     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+     *         .setContentText(subject)
+     *         .setSmallIcon(R.drawable.new_mail)
+     *         .extend(new Notification.WearableExtender()
+     *                 .setContentIcon(R.drawable.new_mail))
+     *         .build();
+     * NotificationManager notificationManger =
+     *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+     * notificationManger.notify(0, notif);</pre>
+     *
+     * <p>Wearable extensions can be accessed on an existing notification by using the
+     * {@code WearableExtender(Notification)} constructor,
+     * and then using the {@code get} methods to access values.
+     *
+     * <pre class="prettyprint">
+     * Notification.WearableExtender wearableExtender = new Notification.WearableExtender(
+     *         notification);
+     * List&lt;Notification&gt; pages = wearableExtender.getPages();
+     * </pre>
+     */
+    public static final class WearableExtender implements Extender {
+        /**
+         * Sentinel value for an action index that is unset.
+         */
+        public static final int UNSET_ACTION_INDEX = -1;
+
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification with
+         * default sizing.
+         * <p>For custom display notifications created using {@link #setDisplayIntent},
+         * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
+         * on their content.
+         */
+        public static final int SIZE_DEFAULT = 0;
+
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * with an extra small size.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_XSMALL = 1;
+
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * with a small size.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_SMALL = 2;
+
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * with a medium size.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_MEDIUM = 3;
+
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * with a large size.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_LARGE = 4;
+
+        /** Notification extra which contains wearable extensions */
+        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+        // Keys within EXTRA_WEARABLE_OPTIONS for wearable options.
+        private static final String KEY_ACTIONS = "actions";
+        private static final String KEY_FLAGS = "flags";
+        private static final String KEY_DISPLAY_INTENT = "displayIntent";
+        private static final String KEY_PAGES = "pages";
+        private static final String KEY_BACKGROUND = "background";
+        private static final String KEY_CONTENT_ICON = "contentIcon";
+        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
+        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
+        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
+        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
+        private static final String KEY_GRAVITY = "gravity";
+
+        // Flags bitwise-ored to mFlags
+        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
+        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
+        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
+        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
+
+        // Default value for flags integer
+        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
+
+        private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END;
+        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
+
+        private ArrayList<Action> mActions = new ArrayList<Action>();
+        private int mFlags = DEFAULT_FLAGS;
+        private PendingIntent mDisplayIntent;
+        private ArrayList<Notification> mPages = new ArrayList<Notification>();
+        private Bitmap mBackground;
+        private int mContentIcon;
+        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
+        private int mContentActionIndex = UNSET_ACTION_INDEX;
+        private int mCustomSizePreset = SIZE_DEFAULT;
+        private int mCustomContentHeight;
+        private int mGravity = DEFAULT_GRAVITY;
+
+        /**
+         * Create a {@link android.app.Notification.WearableExtender} with default
+         * options.
+         */
+        public WearableExtender() {
+        }
+
+        public WearableExtender(Notification notif) {
+            Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS);
+            if (wearableBundle != null) {
+                List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS);
+                if (actions != null) {
+                    mActions.addAll(actions);
+                }
+
+                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
+                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
+
+                Notification[] pages = getNotificationArrayFromBundle(
+                        wearableBundle, KEY_PAGES);
+                if (pages != null) {
+                    Collections.addAll(mPages, pages);
+                }
+
+                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
+                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
+                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
+                        DEFAULT_CONTENT_ICON_GRAVITY);
+                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
+                        UNSET_ACTION_INDEX);
+                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
+                        SIZE_DEFAULT);
+                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
+                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
+            }
+        }
+
+        /**
+         * Apply wearable extensions to a notification that is being built. This is typically
+         * called by the {@link android.app.Notification.Builder#extend} method of
+         * {@link android.app.Notification.Builder}.
+         */
+        @Override
+        public Notification.Builder extend(Notification.Builder builder) {
+            Bundle wearableBundle = new Bundle();
+
+            if (!mActions.isEmpty()) {
+                wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions);
+            }
+            if (mFlags != DEFAULT_FLAGS) {
+                wearableBundle.putInt(KEY_FLAGS, mFlags);
+            }
+            if (mDisplayIntent != null) {
+                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
+            }
+            if (!mPages.isEmpty()) {
+                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
+                        new Notification[mPages.size()]));
+            }
+            if (mBackground != null) {
+                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+            }
+            if (mContentIcon != 0) {
+                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
+            }
+            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
+                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
+            }
+            if (mContentActionIndex != UNSET_ACTION_INDEX) {
+                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
+                        mContentActionIndex);
+            }
+            if (mCustomSizePreset != SIZE_DEFAULT) {
+                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
+            }
+            if (mCustomContentHeight != 0) {
+                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
+            }
+            if (mGravity != DEFAULT_GRAVITY) {
+                wearableBundle.putInt(KEY_GRAVITY, mGravity);
+            }
+
+            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
+            return builder;
+        }
+
+        @Override
+        public WearableExtender clone() {
+            WearableExtender that = new WearableExtender();
+            that.mActions = new ArrayList<Action>(this.mActions);
+            that.mFlags = this.mFlags;
+            that.mDisplayIntent = this.mDisplayIntent;
+            that.mPages = new ArrayList<Notification>(this.mPages);
+            that.mBackground = this.mBackground;
+            that.mContentIcon = this.mContentIcon;
+            that.mContentIconGravity = this.mContentIconGravity;
+            that.mContentActionIndex = this.mContentActionIndex;
+            that.mCustomSizePreset = this.mCustomSizePreset;
+            that.mCustomContentHeight = this.mCustomContentHeight;
+            that.mGravity = this.mGravity;
+            return that;
+        }
+
+        /**
+         * Add a wearable action to this notification.
+         *
+         * <p>When wearable actions are added using this method, the set of actions that
+         * show on a wearable device splits from devices that only show actions added
+         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+         * of which actions display on different devices.
+         *
+         * @param action the action to add to this notification
+         * @return this object for method chaining
+         * @see android.app.Notification.Action
+         */
+        public WearableExtender addAction(Action action) {
+            mActions.add(action);
+            return this;
+        }
+
+        /**
+         * Adds wearable actions to this notification.
+         *
+         * <p>When wearable actions are added using this method, the set of actions that
+         * show on a wearable device splits from devices that only show actions added
+         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+         * of which actions display on different devices.
+         *
+         * @param actions the actions to add to this notification
+         * @return this object for method chaining
+         * @see android.app.Notification.Action
+         */
+        public WearableExtender addActions(List<Action> actions) {
+            mActions.addAll(actions);
+            return this;
+        }
+
+        /**
+         * Clear all wearable actions present on this builder.
+         * @return this object for method chaining.
+         * @see #addAction
+         */
+        public WearableExtender clearActions() {
+            mActions.clear();
+            return this;
+        }
+
+        /**
+         * Get the wearable actions present on this notification.
+         */
+        public List<Action> getActions() {
+            return mActions;
+        }
+
+        /**
+         * Set an intent to launch inside of an activity view when displaying
+         * this notification. This {@link PendingIntent} should be for an activity.
+         *
+         * @param intent the {@link PendingIntent} for an activity
+         * @return this object for method chaining
+         * @see android.app.Notification.WearableExtender#getDisplayIntent
+         */
+        public WearableExtender setDisplayIntent(PendingIntent intent) {
+            mDisplayIntent = intent;
+            return this;
+        }
+
+        /**
+         * Get the intent to launch inside of an activity view when displaying this
+         * notification. This {@code PendingIntent} should be for an activity.
+         */
+        public PendingIntent getDisplayIntent() {
+            return mDisplayIntent;
+        }
+
+        /**
+         * Add an additional page of content to display with this notification. The current
+         * notification forms the first page, and pages added using this function form
+         * subsequent pages. This field can be used to separate a notification into multiple
+         * sections.
+         *
+         * @param page the notification to add as another page
+         * @return this object for method chaining
+         * @see android.app.Notification.WearableExtender#getPages
+         */
+        public WearableExtender addPage(Notification page) {
+            mPages.add(page);
+            return this;
+        }
+
+        /**
+         * Add additional pages of content to display with this notification. The current
+         * notification forms the first page, and pages added using this function form
+         * subsequent pages. This field can be used to separate a notification into multiple
+         * sections.
+         *
+         * @param pages a list of notifications
+         * @return this object for method chaining
+         * @see android.app.Notification.WearableExtender#getPages
+         */
+        public WearableExtender addPages(List<Notification> pages) {
+            mPages.addAll(pages);
+            return this;
+        }
+
+        /**
+         * Clear all additional pages present on this builder.
+         * @return this object for method chaining.
+         * @see #addPage
+         */
+        public WearableExtender clearPages() {
+            mPages.clear();
+            return this;
+        }
+
+        /**
+         * Get the array of additional pages of content for displaying this notification. The
+         * current notification forms the first page, and elements within this array form
+         * subsequent pages. This field can be used to separate a notification into multiple
+         * sections.
+         * @return the pages for this notification
+         */
+        public List<Notification> getPages() {
+            return mPages;
+        }
+
+        /**
+         * Set a background image to be displayed behind the notification content.
+         * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
+         * will work with any notification style.
+         *
+         * @param background the background bitmap
+         * @return this object for method chaining
+         * @see android.app.Notification.WearableExtender#getBackground
+         */
+        public WearableExtender setBackground(Bitmap background) {
+            mBackground = background;
+            return this;
+        }
+
+        /**
+         * Get a background image to be displayed behind the notification content.
+         * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background
+         * will work with any notification style.
+         *
+         * @return the background image
+         * @see android.app.Notification.WearableExtender#setBackground
+         */
+        public Bitmap getBackground() {
+            return mBackground;
+        }
+
+        /**
+         * Set an icon that goes with the content of this notification.
+         */
+        public WearableExtender setContentIcon(int icon) {
+            mContentIcon = icon;
+            return this;
+        }
+
+        /**
+         * Get an icon that goes with the content of this notification.
+         */
+        public int getContentIcon() {
+            return mContentIcon;
+        }
+
+        /**
+         * Set the gravity that the content icon should have within the notification display.
+         * Supported values include {@link android.view.Gravity#START} and
+         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
+         * @see #setContentIcon
+         */
+        public WearableExtender setContentIconGravity(int contentIconGravity) {
+            mContentIconGravity = contentIconGravity;
+            return this;
+        }
+
+        /**
+         * Get the gravity that the content icon should have within the notification display.
+         * Supported values include {@link android.view.Gravity#START} and
+         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
+         * @see #getContentIcon
+         */
+        public int getContentIconGravity() {
+            return mContentIconGravity;
+        }
+
+        /**
+         * Set an action from this notification's actions to be clickable with the content of
+         * this notification page. This action will no longer display separately from the
+         * notification content. This action's icon will display with optional subtext provided
+         * by the action's title.
+         * @param actionIndex The index of the action to hoist on the current notification page.
+         *                    If wearable actions are present, this index will apply to that list,
+         *                    otherwise it will apply to the main notification's actions list.
+         */
+        public WearableExtender setContentAction(int actionIndex) {
+            mContentActionIndex = actionIndex;
+            return this;
+        }
+
+        /**
+         * Get the action index of an action from this notification to show as clickable with
+         * the content of this notification page. When the user clicks this notification page,
+         * this action will trigger. This action will no longer display separately from the
+         * notification content. The action's icon will display with optional subtext provided
+         * by the action's title.
+         *
+         * <p>If wearable specific actions are present, this index will apply to that list,
+         * otherwise it will apply to the main notification's actions list.
+         */
+        public int getContentAction() {
+            return mContentActionIndex;
+        }
+
+        /**
+         * Set the gravity that this notification should have within the available viewport space.
+         * Supported values include {@link android.view.Gravity#TOP},
+         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
+         * The default value is {@link android.view.Gravity#BOTTOM}.
+         */
+        public WearableExtender setGravity(int gravity) {
+            mGravity = gravity;
+            return this;
+        }
+
+        /**
+         * Get the gravity that this notification should have within the available viewport space.
+         * Supported values include {@link android.view.Gravity#TOP},
+         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
+         * The default value is {@link android.view.Gravity#BOTTOM}.
+         */
+        public int getGravity() {
+            return mGravity;
+        }
+
+        /**
+         * Set the custom size preset for the display of this notification out of the available
+         * presets found in {@link android.app.Notification.WearableExtender}, e.g.
+         * {@link #SIZE_LARGE}.
+         * <p>Some custom size presets are only applicable for custom display notifications created
+         * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the
+         * documentation for the preset in question. See also
+         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
+         */
+        public WearableExtender setCustomSizePreset(int sizePreset) {
+            mCustomSizePreset = sizePreset;
+            return this;
+        }
+
+        /**
+         * Get the custom size preset for the display of this notification out of the available
+         * presets found in {@link android.app.Notification.WearableExtender}, e.g.
+         * {@link #SIZE_LARGE}.
+         * <p>Some custom size presets are only applicable for custom display notifications created
+         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
+         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
+         */
+        public int getCustomSizePreset() {
+            return mCustomSizePreset;
+        }
+
+        /**
+         * Set the custom height in pixels for the display of this notification's content.
+         * <p>This option is only available for custom display notifications created
+         * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also
+         * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
+         * {@link #getCustomContentHeight}.
+         */
+        public WearableExtender setCustomContentHeight(int height) {
+            mCustomContentHeight = height;
+            return this;
+        }
+
+        /**
+         * Get the custom height in pixels for the display of this notification's content.
+         * <p>This option is only available for custom display notifications created
+         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
+         * {@link #setCustomContentHeight}.
+         */
+        public int getCustomContentHeight() {
+            return mCustomContentHeight;
+        }
+
+        /**
+         * Set whether the scrolling position for the contents of this notification should start
+         * at the bottom of the contents instead of the top when the contents are too long to
+         * display within the screen.  Default is false (start scroll at the top).
+         */
+        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
+            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
+            return this;
+        }
+
+        /**
+         * Get whether the scrolling position for the contents of this notification should start
+         * at the bottom of the contents instead of the top when the contents are too long to
+         * display within the screen. Default is false (start scroll at the top).
+         */
+        public boolean getStartScrollBottom() {
+            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
+        }
+
+        /**
+         * Set whether the content intent is available when the wearable device is not connected
+         * to a companion device.  The user can still trigger this intent when the wearable device
+         * is offline, but a visual hint will indicate that the content intent may not be available.
+         * Defaults to true.
+         */
+        public WearableExtender setContentIntentAvailableOffline(
+                boolean contentIntentAvailableOffline) {
+            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
+            return this;
+        }
+
+        /**
+         * Get whether the content intent is available when the wearable device is not connected
+         * to a companion device.  The user can still trigger this intent when the wearable device
+         * is offline, but a visual hint will indicate that the content intent may not be available.
+         * Defaults to true.
+         */
+        public boolean getContentIntentAvailableOffline() {
+            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
+        }
+
+        /**
+         * Set a hint that this notification's icon should not be displayed.
+         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
+         * @return this object for method chaining
+         */
+        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
+            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
+            return this;
+        }
+
+        /**
+         * Get a hint that this notification's icon should not be displayed.
+         * @return {@code true} if this icon should not be displayed, false otherwise.
+         * The default value is {@code false} if this was never set.
+         */
+        public boolean getHintHideIcon() {
+            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
+        }
+
+        /**
+         * Set a visual hint that only the background image of this notification should be
+         * displayed, and other semantic content should be hidden. This hint is only applicable
+         * to sub-pages added using {@link #addPage}.
+         */
+        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
+            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
+            return this;
+        }
+
+        /**
+         * Get a visual hint that only the background image of this notification should be
+         * displayed, and other semantic content should be hidden. This hint is only applicable
+         * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
+         */
+        public boolean getHintShowBackgroundOnly() {
+            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
+        }
+
+        private void setFlag(int mask, boolean value) {
+            if (value) {
+                mFlags |= mask;
+            } else {
+                mFlags &= ~mask;
+            }
+        }
+    }
+
+    /**
+     * Get an array of Notification objects from a parcelable array bundle field.
+     * Update the bundle to have a typed array so fetches in the future don't need
+     * to do an array copy.
+     */
+    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
+        Parcelable[] array = bundle.getParcelableArray(key);
+        if (array instanceof Notification[] || array == null) {
+            return (Notification[]) array;
+        }
+        Notification[] typedArray = Arrays.copyOf(array, array.length,
+                Notification[].class);
+        bundle.putParcelableArray(key, typedArray);
+        return typedArray;
+    }
 }
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 9cfc541..11420c5 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -64,18 +64,24 @@
     /** Extra added to a clip data intent object to hold the results bundle. */
     public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
 
+    // Flags bitwise-ored to mFlags
+    private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
+
+    // Default value for flags integer
+    private static final int DEFAULT_FLAGS = FLAG_ALLOW_FREE_FORM_INPUT;
+
     private final String mResultKey;
     private final CharSequence mLabel;
     private final CharSequence[] mChoices;
-    private final boolean mAllowFreeFormInput;
+    private final int mFlags;
     private final Bundle mExtras;
 
     private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
-            boolean allowFreeFormInput, Bundle extras) {
+            int flags, Bundle extras) {
         this.mResultKey = resultKey;
         this.mLabel = label;
         this.mChoices = choices;
-        this.mAllowFreeFormInput = allowFreeFormInput;
+        this.mFlags = flags;
         this.mExtras = extras;
     }
 
@@ -108,7 +114,7 @@
      * if you set this to false and {@link #getChoices} returns {@code null} or empty.
      */
     public boolean getAllowFreeFormInput() {
-        return mAllowFreeFormInput;
+        return (mFlags & FLAG_ALLOW_FREE_FORM_INPUT) != 0;
     }
 
     /**
@@ -125,7 +131,7 @@
         private final String mResultKey;
         private CharSequence mLabel;
         private CharSequence[] mChoices;
-        private boolean mAllowFreeFormInput = true;
+        private int mFlags = DEFAULT_FLAGS;
         private Bundle mExtras = new Bundle();
 
         /**
@@ -178,7 +184,7 @@
          * @return this object for method chaining
          */
         public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
-            mAllowFreeFormInput = allowFreeFormInput;
+            setFlag(mFlags, allowFreeFormInput);
             return this;
         }
 
@@ -205,12 +211,20 @@
             return mExtras;
         }
 
+        private void setFlag(int mask, boolean value) {
+            if (value) {
+                mFlags |= mask;
+            } else {
+                mFlags &= ~mask;
+            }
+        }
+
         /**
          * Combine all of the options that have been set and return a new {@link RemoteInput}
          * object.
          */
         public RemoteInput build() {
-            return new RemoteInput(mResultKey, mLabel, mChoices, mAllowFreeFormInput, mExtras);
+            return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mExtras);
         }
     }
 
@@ -218,7 +232,7 @@
         mResultKey = in.readString();
         mLabel = in.readCharSequence();
         mChoices = in.readCharSequenceArray();
-        mAllowFreeFormInput = in.readInt() != 0;
+        mFlags = in.readInt();
         mExtras = in.readBundle();
     }
 
@@ -279,7 +293,7 @@
         out.writeString(mResultKey);
         out.writeCharSequence(mLabel);
         out.writeCharSequenceArray(mChoices);
-        out.writeInt(mAllowFreeFormInput ? 1 : 0);
+        out.writeInt(mFlags);
         out.writeBundle(mExtras);
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 18e2a95..77b1acf 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.service.trust.TrustAgentService;
 import android.util.Log;
@@ -1983,6 +1984,43 @@
     }
 
     /**
+     * Called by a device owner to create a user with the specified name. The UserHandle returned
+     * by this method should not be persisted as user handles are recycled as users are removed and
+     * created. If you need to persist an identifier for this user, use
+     * {@link UserManager#getSerialNumberForUser}.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param name the user's name
+     * @see UserHandle
+     * @return the UserHandle object for the created user, or null if the user could not be created.
+     */
+    public UserHandle createUser(ComponentName admin, String name) {
+        try {
+            return mService.createUser(admin, name);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not create a user", re);
+        }
+        return null;
+    }
+
+    /**
+     * Called by a device owner to remove a user and all associated data. The primary user can
+     * not be removed.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param userHandle the user to remove.
+     * @return {@code true} if the user was removed, {@code false} otherwise.
+     */
+    public boolean removeUser(ComponentName admin, UserHandle userHandle) {
+        try {
+            return mService.removeUser(admin, userHandle);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not remove user ", re);
+            return false;
+        }
+    }
+
+    /**
      * Called by a profile or device owner to get the application restrictions for a given target
      * application running in the managed profile.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7257158..3c08c14 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.RemoteCallback;
+import android.os.UserHandle;
 
 /**
  * Internal IPC interface to the device policy service.
@@ -128,6 +129,9 @@
     int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked);
     boolean isApplicationBlocked(in ComponentName admin, in String packageName);
 
+    UserHandle createUser(in ComponentName who, in String name);
+    boolean removeUser(in ComponentName who, in UserHandle userHandle);
+
     void enableSystemApp(in ComponentName admin, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
 
diff --git a/core/java/android/app/wearable/WearableActionExtensions.java b/core/java/android/app/wearable/WearableActionExtensions.java
deleted file mode 100644
index c296ef2..0000000
--- a/core/java/android/app/wearable/WearableActionExtensions.java
+++ /dev/null
@@ -1,176 +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.app.wearable;
-
-import android.app.Notification;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wearable extensions to notification actions. To add extensions to an action,
- * create a new {@link WearableActionExtensions} object using
- * {@link WearableActionExtensions.Builder} and apply it to a
- * {@link android.app.Notification.Action.Builder}.
- *
- * <pre class="prettyprint">
- * Notification.Action action = new Notification.Action.Builder(
- *         R.drawable.archive_all, "Archive all", actionIntent)
- *         .apply(new WearableActionExtensions.Builder()
- *                 .setAvailableOffline(false)
- *                 .build())
- *         .build();
- * </pre>
- */
-public final class WearableActionExtensions implements Notification.Action.Builder.Extender,
-        Parcelable {
-    /** Notification action extra which contains wearable extensions */
-    private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
-    // Flags bitwise-ored to mFlags
-    private static final int FLAG_AVAILABLE_OFFLINE = 1 << 0;
-
-    // Default value for flags integer
-    private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
-
-    private final int mFlags;
-
-    private WearableActionExtensions(int flags) {
-        mFlags = flags;
-    }
-
-    private WearableActionExtensions(Parcel in) {
-        mFlags = in.readInt();
-    }
-
-    /**
-     * Create a {@link WearableActionExtensions} by reading wearable extensions present on an
-     * existing notification action.
-     * @param action the notification action to inspect.
-     * @return a new {@link WearableActionExtensions} object.
-     */
-    public static WearableActionExtensions from(Notification.Action action) {
-        WearableActionExtensions extensions = action.getExtras().getParcelable(
-                EXTRA_WEARABLE_EXTENSIONS);
-        if (extensions != null) {
-            return extensions;
-        } else {
-            // Return a WearableActionExtensions with default values.
-            return new Builder().build();
-        }
-    }
-
-    /**
-     * Get whether this action is available when the wearable device is not connected to
-     * a companion device. The user can still trigger this action when the wearable device is
-     * offline, but a visual hint will indicate that the action may not be available.
-     * Defaults to true.
-     */
-    public boolean isAvailableOffline() {
-        return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
-    }
-
-    @Override
-    public Notification.Action.Builder applyTo(Notification.Action.Builder builder) {
-        builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
-        return builder;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mFlags);
-    }
-
-    /**
-     * Builder for {@link WearableActionExtensions} objects, which adds wearable extensions to
-     * notification actions. To extend an action, create an instance of this class, call the set
-     * methods present, call {@link #build}, and finally apply the options to a
-     * {@link Notification.Action.Builder} using its
-     * {@link android.app.Notification.Action.Builder#apply} method.
-     */
-    public static final class Builder {
-        private int mFlags = DEFAULT_FLAGS;
-
-        /**
-         * Construct a builder to be used for adding wearable extensions to notification actions.
-         *
-         * <pre class="prettyprint">
-         * Notification.Action action = new Notification.Action.Builder(
-         *         R.drawable.archive_all, "Archive all", actionIntent)
-         *         .apply(new WearableActionExtensions.Builder()
-         *                 .setAvailableOffline(false)
-         *                 .build())
-         *         .build();</pre>
-         */
-        public Builder() {
-        }
-
-        /**
-         * Create a {@link Builder} by reading wearable extensions present on an
-         * existing {@code WearableActionExtensions} object.
-         * @param other the existing extensions to inspect.
-         */
-        public Builder(WearableActionExtensions other) {
-            mFlags = other.mFlags;
-        }
-
-        /**
-         * Set whether this action is available when the wearable device is not connected to
-         * a companion device. The user can still trigger this action when the wearable device is
-         * offline, but a visual hint will indicate that the action may not be available.
-         * Defaults to true.
-         */
-        public Builder setAvailableOffline(boolean availableOffline) {
-            setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
-            return this;
-        }
-
-        /**
-         * Build a new {@link WearableActionExtensions} object with the extensions
-         * currently present on this builder.
-         * @return the extensions object.
-         */
-        public WearableActionExtensions build() {
-            return new WearableActionExtensions(mFlags);
-        }
-
-        private void setFlag(int mask, boolean value) {
-            if (value) {
-                mFlags |= mask;
-            } else {
-                mFlags &= ~mask;
-            }
-        }
-    }
-
-    public static final Creator<WearableActionExtensions> CREATOR =
-            new Creator<WearableActionExtensions>() {
-        @Override
-        public WearableActionExtensions createFromParcel(Parcel in) {
-            return new WearableActionExtensions(in);
-        }
-
-        @Override
-        public WearableActionExtensions[] newArray(int size) {
-            return new WearableActionExtensions[size];
-        }
-    };
-}
diff --git a/core/java/android/app/wearable/WearableNotificationExtensions.java b/core/java/android/app/wearable/WearableNotificationExtensions.java
deleted file mode 100644
index d433613..0000000
--- a/core/java/android/app/wearable/WearableNotificationExtensions.java
+++ /dev/null
@@ -1,702 +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.app.wearable;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.graphics.Bitmap;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.Gravity;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Helper class that contains wearable extensions for notifications.
- * <p class="note"> See
- * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
- * for Android Wear</a> for more information on how to use this class.
- * <p>
- * To create a notification with wearable extensions:
- * <ol>
- *   <li>Create a {@link Notification.Builder}, setting any desired
- *   properties.
- *   <li>Create a {@link WearableNotificationExtensions.Builder}.
- *   <li>Set wearable-specific properties using the
- *   {@code add} and {@code set} methods of {@link WearableNotificationExtensions.Builder}.
- *   <li>Call {@link WearableNotificationExtensions.Builder#build} to build the extensions
- *   object.
- *   <li>Call {@link Notification.Builder#apply} to apply the extensions to a notification.
- *   <li>Post the notification to the notification system with the
- *   {@code NotificationManager.notify(...)} methods.
- * </ol>
- *
- * <pre class="prettyprint">
- * Notification notif = new Notification.Builder(mContext)
- *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
- *         .setContentText(subject)
- *         .setSmallIcon(R.drawable.new_mail)
- *         .apply(new new WearableNotificationExtensions.Builder()
- *                 .setContentIcon(R.drawable.new_mail)
- *                 .build())
- *         .build();
- * NotificationManager notificationManger =
- *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- * notificationManger.notify(0, notif);</pre>
- *
- * <p>Wearable extensions can be accessed on an existing notification by using the
- * {@link WearableNotificationExtensions#from} function.
- *
- * <pre class="prettyprint">
- * WearableNotificationExtensions wearableExtensions = WearableNotificationExtensions.from(
- *         notification);
- * Notification[] pages = wearableExtensions.getPages();
- * </pre>
- */
-public final class WearableNotificationExtensions implements Notification.Builder.Extender,
-        Parcelable {
-    /**
-     * Sentinel value for an action index that is unset.
-     */
-    public static final int UNSET_ACTION_INDEX = -1;
-
-    /**
-     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification with
-     * default sizing.
-     * <p>For custom display notifications created using {@link Builder#setDisplayIntent},
-     * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
-     * on their content.
-     */
-    public static final int SIZE_DEFAULT = 0;
-
-    /**
-     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
-     * with an extra small size.
-     * <p>This value is only applicable for custom display notifications created using
-     * {@link Builder#setDisplayIntent}.
-     */
-    public static final int SIZE_XSMALL = 1;
-
-    /**
-     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
-     * with a small size.
-     * <p>This value is only applicable for custom display notifications created using
-     * {@link Builder#setDisplayIntent}.
-     */
-    public static final int SIZE_SMALL = 2;
-
-    /**
-     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
-     * with a medium size.
-     * <p>This value is only applicable for custom display notifications created using
-     * {@link Builder#setDisplayIntent}.
-     */
-    public static final int SIZE_MEDIUM = 3;
-
-    /**
-     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
-     * with a large size.
-     * <p>This value is only applicable for custom display notifications created using
-     * {@link Builder#setDisplayIntent}.
-     */
-    public static final int SIZE_LARGE = 4;
-
-    /** Notification extra which contains wearable extensions */
-    static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
-    // Flags bitwise-ored to mFlags
-    static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 1 << 0;
-    static final int FLAG_HINT_HIDE_ICON = 1 << 1;
-    static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
-    static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
-
-    // Default value for flags integer
-    static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
-
-    private final Notification.Action[] mActions;
-    private final int mFlags;
-    private final PendingIntent mDisplayIntent;
-    private final Notification[] mPages;
-    private final Bitmap mBackground;
-    private final int mContentIcon;
-    private final int mContentIconGravity;
-    private final int mContentActionIndex;
-    private final int mCustomSizePreset;
-    private final int mCustomContentHeight;
-    private final int mGravity;
-
-    private WearableNotificationExtensions(Notification.Action[] actions, int flags,
-            PendingIntent displayIntent, Notification[] pages, Bitmap background,
-            int contentIcon, int contentIconGravity, int contentActionIndex,
-            int customSizePreset, int customContentHeight, int gravity) {
-        mActions = actions;
-        mFlags = flags;
-        mDisplayIntent = displayIntent;
-        mPages = pages;
-        mBackground = background;
-        mContentIcon = contentIcon;
-        mContentIconGravity = contentIconGravity;
-        mContentActionIndex = contentActionIndex;
-        mCustomSizePreset = customSizePreset;
-        mCustomContentHeight = customContentHeight;
-        mGravity = gravity;
-    }
-
-    private WearableNotificationExtensions(Parcel in) {
-        mActions = in.createTypedArray(Notification.Action.CREATOR);
-        mFlags = in.readInt();
-        mDisplayIntent = in.readParcelable(PendingIntent.class.getClassLoader());
-        mPages = in.createTypedArray(Notification.CREATOR);
-        mBackground = in.readParcelable(Bitmap.class.getClassLoader());
-        mContentIcon = in.readInt();
-        mContentIconGravity = in.readInt();
-        mContentActionIndex = in.readInt();
-        mCustomSizePreset = in.readInt();
-        mCustomContentHeight = in.readInt();
-        mGravity = in.readInt();
-    }
-
-    /**
-     * Create a {@link WearableNotificationExtensions} by reading wearable extensions present on an
-     * existing notification.
-     * @param notif the notification to inspect.
-     * @return a new {@link WearableNotificationExtensions} object.
-     */
-    public static WearableNotificationExtensions from(Notification notif) {
-        WearableNotificationExtensions extensions = notif.extras.getParcelable(
-                EXTRA_WEARABLE_EXTENSIONS);
-        if (extensions != null) {
-            return extensions;
-        } else {
-            // Return a WearableNotificationExtensions with default values.
-            return new Builder().build();
-        }
-    }
-
-    /**
-     * Apply wearable extensions to a notification that is being built. This is typically
-     * called by {@link Notification.Builder#apply} method of {@link Notification.Builder}.
-     */
-    @Override
-    public Notification.Builder applyTo(Notification.Builder builder) {
-        builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
-        return builder;
-    }
-
-    /**
-     * Get the number of wearable actions present on this notification.
-     *
-     * @return the number of wearable actions for this notification
-     */
-    public int getActionCount() {
-        return mActions.length;
-    }
-
-    /**
-     * Get a {@link Notification.Action} for the wearable action at {@code actionIndex}.
-     * @param actionIndex the index of the desired wearable action
-     */
-    public Notification.Action getAction(int actionIndex) {
-        return mActions[actionIndex];
-    }
-
-    /**
-     * Get the wearable actions present on this notification.
-     */
-    public Notification.Action[] getActions() {
-        return mActions;
-    }
-
-    /**
-     * Get the intent to launch inside of an activity view when displaying this
-     * notification. This {@code PendingIntent} should be for an activity.
-     */
-    public PendingIntent getDisplayIntent() {
-        return mDisplayIntent;
-    }
-
-    /**
-     * Get the array of additional pages of content for displaying this notification. The
-     * current notification forms the first page, and elements within this array form
-     * subsequent pages. This field can be used to separate a notification into multiple
-     * sections.
-     * @return the pages for this notification
-     */
-    public Notification[] getPages() {
-        return mPages;
-    }
-
-    /**
-     * Get a background image to be displayed behind the notification content.
-     * Contrary to the {@link Notification.BigPictureStyle}, this background
-     * will work with any notification style.
-     *
-     * @return the background image
-     * @see Builder#setBackground
-     */
-    public Bitmap getBackground() {
-        return mBackground;
-    }
-
-    /**
-     * Get an icon that goes with the content of this notification.
-     */
-    public int getContentIcon() {
-        return mContentIcon;
-    }
-
-    /**
-     * Get the gravity that the content icon should have within the notification display.
-     * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
-     * value is {@link android.view.Gravity#END}.
-     * @see #getContentIcon
-     */
-    public int getContentIconGravity() {
-        return mContentIconGravity;
-    }
-
-    /**
-     * Get the action index of an action from this notification to show as clickable with
-     * the content of this notification page. When the user clicks this notification page,
-     * this action will trigger. This action will no longer display separately from the
-     * notification content. The action's icon will display with optional subtext provided
-     * by the action's title.
-     *
-     * <p>If wearable specific actions are present, this index will apply to that list,
-     * otherwise it will apply to the main notification's actions list.
-     */
-    public int getContentAction() {
-        return mContentActionIndex;
-    }
-
-    /**
-     * Get the gravity that this notification should have within the available viewport space.
-     * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
-     * {@link android.view.Gravity#BOTTOM}. The default value is
-     * {@link android.view.Gravity#BOTTOM}.
-     */
-    public int getGravity() {
-        return mGravity;
-    }
-
-    /**
-     * Get the custom size preset for the display of this notification out of the available
-     * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
-     * <p>Some custom size presets are only applicable for custom display notifications created
-     * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in question.
-     * See also {@link Builder#setCustomContentHeight} and {@link Builder#setCustomSizePreset}.
-     */
-    public int getCustomSizePreset() {
-        return mCustomSizePreset;
-    }
-
-    /**
-     * Get the custom height in pixels for the display of this notification's content.
-     * <p>This option is only available for custom display notifications created
-     * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
-     * {@link Builder#setCustomContentHeight}.
-     */
-    public int getCustomContentHeight() {
-        return mCustomContentHeight;
-    }
-
-    /**
-     * Get whether the scrolling position for the contents of this notification should start
-     * at the bottom of the contents instead of the top when the contents are too long to
-     * display within the screen. Default is false (start scroll at the top).
-     */
-    public boolean getStartScrollBottom() {
-        return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
-    }
-
-    /**
-     * Get whether the content intent is available when the wearable device is not connected
-     * to a companion device.  The user can still trigger this intent when the wearable device is
-     * offline, but a visual hint will indicate that the content intent may not be available.
-     * Defaults to true.
-     */
-    public boolean getContentIntentAvailableOffline() {
-        return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
-    }
-
-    /**
-     * Get a hint that this notification's icon should not be displayed.
-     * @return {@code true} if this icon should not be displayed, false otherwise.
-     * The default value is {@code false} if this was never set.
-     */
-    public boolean getHintHideIcon() {
-        return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
-    }
-
-    /**
-     * Get a visual hint that only the background image of this notification should be
-     * displayed, and other semantic content should be hidden. This hint is only applicable
-     * to sub-pages added using {@link Builder#addPage}.
-     */
-    public boolean getHintShowBackgroundOnly() {
-        return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeTypedArray(mActions, flags);
-        out.writeInt(mFlags);
-        out.writeParcelable(mDisplayIntent, flags);
-        out.writeTypedArray(mPages, flags);
-        out.writeParcelable(mBackground, flags);
-        out.writeInt(mContentIcon);
-        out.writeInt(mContentIconGravity);
-        out.writeInt(mContentActionIndex);
-        out.writeInt(mCustomSizePreset);
-        out.writeInt(mCustomContentHeight);
-        out.writeInt(mGravity);
-    }
-
-    /**
-     * Builder to apply wearable notification extensions to a {@link Notification.Builder}
-     * object.
-     *
-     * <p>You can chain the "set" methods for this builder in any order,
-     * but you must call the {@link #build} method and then the {@link Notification.Builder#apply}
-     * method to apply your extensions to a notification.
-     *
-     * <pre class="prettyprint">
-     * Notification notif = new Notification.Builder(mContext)
-     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
-     *         .setContentText(subject)
-     *         .setSmallIcon(R.drawable.new_mail);
-     *         .apply(new WearableNotificationExtensions.Builder()
-     *                 .setContentIcon(R.drawable.new_mail)
-     *                 .build())
-     *         .build();
-     * NotificationManager notificationManger =
-     *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-     * notificationManager.notify(0, notif);</pre>
-     */
-    public static final class Builder {
-        private final List<Notification.Action> mActions =
-                new ArrayList<Notification.Action>();
-        private int mFlags = DEFAULT_FLAGS;
-        private PendingIntent mDisplayIntent;
-        private final List<Notification> mPages = new ArrayList<Notification>();
-        private Bitmap mBackground;
-        private int mContentIcon;
-        private int mContentIconGravity = Gravity.END;
-        private int mContentActionIndex = UNSET_ACTION_INDEX;
-        private int mCustomContentHeight;
-        private int mCustomSizePreset = SIZE_DEFAULT;
-        private int mGravity = Gravity.BOTTOM;
-
-        /**
-         * Construct a builder to be used for adding wearable extensions to notifications.
-         *
-         * <pre class="prettyprint">
-         * Notification notif = new Notification.Builder(mContext)
-         *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
-         *         .setContentText(subject)
-         *         .setSmallIcon(R.drawable.new_mail);
-         *         .apply(new WearableNotificationExtensions.Builder()
-         *                 .setContentIcon(R.drawable.new_mail)
-         *                 .build())
-         *         .build();
-         * NotificationManager notificationManger =
-         *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-         * notificationManager.notify(0, notif);</pre>
-         */
-        public Builder() {
-        }
-
-        /**
-         * Create a {@link Builder} by reading wearable extensions present on an
-         * existing {@code WearableNotificationExtensions} object.
-         * @param other the existing extensions to inspect.
-         */
-        public Builder(WearableNotificationExtensions other) {
-            Collections.addAll(mActions, other.mActions);
-            mFlags = other.mFlags;
-            mDisplayIntent = other.mDisplayIntent;
-            Collections.addAll(mPages, other.mPages);
-            mBackground = other.mBackground;
-            mContentIcon = other.mContentIcon;
-            mContentIconGravity = other.mContentIconGravity;
-            mContentActionIndex = other.mContentActionIndex;
-            mCustomContentHeight = other.mCustomContentHeight;
-            mCustomSizePreset = other.mCustomSizePreset;
-            mGravity = other.mGravity;
-        }
-
-        /**
-         * Add a wearable action to this notification.
-         *
-         * <p>When wearable actions are added using this method, the set of actions that
-         * show on a wearable device splits from devices that only show actions added
-         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
-         * of which actions display on different devices.
-         *
-         * @param action the action to add to this notification
-         * @return this object for method chaining
-         * @see Notification.Action
-         */
-        public Builder addAction(Notification.Action action) {
-            mActions.add(action);
-            return this;
-        }
-
-        /**
-         * Adds wearable actions to this notification.
-         *
-         * <p>When wearable actions are added using this method, the set of actions that
-         * show on a wearable device splits from devices that only show actions added
-         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
-         * of which actions display on different devices.
-         *
-         * @param actions the actions to add to this notification
-         * @return this object for method chaining
-         * @see Notification.Action
-         */
-        public Builder addActions(List<Notification.Action> actions) {
-            mActions.addAll(actions);
-            return this;
-        }
-
-        /**
-         * Clear all wearable actions present on this builder.
-         * @return this object for method chaining.
-         * @see #addAction
-         */
-        public Builder clearActions() {
-            mActions.clear();
-            return this;
-        }
-
-        /**
-         * Set an intent to launch inside of an activity view when displaying
-         * this notification. This {@link android.app.PendingIntent} should be for an activity.
-         *
-         * @param intent the {@link android.app.PendingIntent} for an activity
-         * @return this object for method chaining
-         * @see WearableNotificationExtensions#getDisplayIntent
-         */
-        public Builder setDisplayIntent(PendingIntent intent) {
-            mDisplayIntent = intent;
-            return this;
-        }
-
-        /**
-         * Add an additional page of content to display with this notification. The current
-         * notification forms the first page, and pages added using this function form
-         * subsequent pages. This field can be used to separate a notification into multiple
-         * sections.
-         *
-         * @param page the notification to add as another page
-         * @return this object for method chaining
-         * @see WearableNotificationExtensions#getPages
-         */
-        public Builder addPage(Notification page) {
-            mPages.add(page);
-            return this;
-        }
-
-        /**
-         * Add additional pages of content to display with this notification. The current
-         * notification forms the first page, and pages added using this function form
-         * subsequent pages. This field can be used to separate a notification into multiple
-         * sections.
-         *
-         * @param pages a list of notifications
-         * @return this object for method chaining
-         * @see WearableNotificationExtensions#getPages
-         */
-        public Builder addPages(List<Notification> pages) {
-            mPages.addAll(pages);
-            return this;
-        }
-
-        /**
-         * Clear all additional pages present on this builder.
-         * @return this object for method chaining.
-         * @see #addPage
-         */
-        public Builder clearPages() {
-            mPages.clear();
-            return this;
-        }
-
-        /**
-         * Set a background image to be displayed behind the notification content.
-         * Contrary to the {@link Notification.BigPictureStyle}, this background
-         * will work with any notification style.
-         *
-         * @param background the background bitmap
-         * @return this object for method chaining
-         * @see WearableNotificationExtensions#getBackground
-         */
-        public Builder setBackground(Bitmap background) {
-            mBackground = background;
-            return this;
-        }
-
-        /**
-         * Set an icon that goes with the content of this notification.
-         */
-        public Builder setContentIcon(int icon) {
-            mContentIcon = icon;
-            return this;
-        }
-
-        /**
-         * Set the gravity that the content icon should have within the notification display.
-         * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
-         * value is {@link android.view.Gravity#END}.
-         * @see #setContentIcon
-         */
-        public Builder setContentIconGravity(int contentIconGravity) {
-            mContentIconGravity = contentIconGravity;
-            return this;
-        }
-
-        /**
-         * Set an action from this notification's actions to be clickable with the content of
-         * this notification page. This action will no longer display separately from the
-         * notification content. This action's icon will display with optional subtext provided
-         * by the action's title.
-         * @param actionIndex The index of the action to hoist on the current notification page.
-         *                    If wearable actions are present, this index will apply to that list,
-         *                    otherwise it will apply to the main notification's actions list.
-         */
-        public Builder setContentAction(int actionIndex) {
-            mContentActionIndex = actionIndex;
-            return this;
-        }
-
-        /**
-         * Set the gravity that this notification should have within the available viewport space.
-         * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
-         * {@link Gravity#BOTTOM}. The default value is {@link Gravity#BOTTOM}.
-         */
-        public Builder setGravity(int gravity) {
-            mGravity = gravity;
-            return this;
-        }
-
-        /**
-         * Set the custom size preset for the display of this notification out of the available
-         * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
-         * <p>Some custom size presets are only applicable for custom display notifications created
-         * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in
-         * question. See also {@link Builder#setCustomContentHeight} and
-         * {@link #getCustomSizePreset}.
-         */
-        public Builder setCustomSizePreset(int sizePreset) {
-            mCustomSizePreset = sizePreset;
-            return this;
-        }
-
-        /**
-         * Set the custom height in pixels for the display of this notification's content.
-         * <p>This option is only available for custom display notifications created
-         * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
-         * {@link #getCustomContentHeight}.
-         */
-        public Builder setCustomContentHeight(int height) {
-            mCustomContentHeight = height;
-            return this;
-        }
-
-        /**
-         * Set whether the scrolling position for the contents of this notification should start
-         * at the bottom of the contents instead of the top when the contents are too long to
-         * display within the screen.  Default is false (start scroll at the top).
-         */
-        public Builder setStartScrollBottom(boolean startScrollBottom) {
-            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
-            return this;
-        }
-
-        /**
-         * Set whether the content intent is available when the wearable device is not connected
-         * to a companion device.  The user can still trigger this intent when the wearable device
-         * is offline, but a visual hint will indicate that the content intent may not be available.
-         * Defaults to true.
-         */
-        public Builder setContentIntentAvailableOffline(boolean contentIntentAvailableOffline) {
-            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
-            return this;
-        }
-
-        /**
-         * Set a hint that this notification's icon should not be displayed.
-         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
-         * @return this object for method chaining
-         */
-        public Builder setHintHideIcon(boolean hintHideIcon) {
-            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
-            return this;
-        }
-
-        /**
-         * Set a visual hint that only the background image of this notification should be
-         * displayed, and other semantic content should be hidden. This hint is only applicable
-         * to sub-pages added using {@link #addPage}.
-         */
-        public Builder setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
-            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
-            return this;
-        }
-
-        /**
-         * Build a new {@link WearableNotificationExtensions} object with the extensions
-         * currently present on this builder.
-         * @return the extensions object.
-         */
-        public WearableNotificationExtensions build() {
-            return new WearableNotificationExtensions(
-                    mActions.toArray(new Notification.Action[mActions.size()]), mFlags,
-                    mDisplayIntent, mPages.toArray(new Notification[mPages.size()]),
-                    mBackground, mContentIcon, mContentIconGravity, mContentActionIndex,
-                    mCustomSizePreset, mCustomContentHeight, mGravity);
-        }
-
-        private void setFlag(int mask, boolean value) {
-            if (value) {
-                mFlags |= mask;
-            } else {
-                mFlags &= ~mask;
-            }
-        }
-    }
-
-    public static final Creator<WearableNotificationExtensions> CREATOR =
-            new Creator<WearableNotificationExtensions>() {
-        @Override
-        public WearableNotificationExtensions createFromParcel(Parcel in) {
-            return new WearableNotificationExtensions(in);
-        }
-
-        @Override
-        public WearableNotificationExtensions[] newArray(int size) {
-            return new WearableNotificationExtensions[size];
-        }
-    };
-}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d0ac9c9..6ae006c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1994,6 +1994,7 @@
             WIFI_PASSPOINT_SERVICE,
             WIFI_P2P_SERVICE,
             WIFI_SCANNING_SERVICE,
+            //@hide: ETHERNET_SERVICE,
             NSD_SERVICE,
             AUDIO_SERVICE,
             MEDIA_ROUTER_SERVICE,
@@ -2069,9 +2070,6 @@
      *  <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
      *  <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
      * Wi-Fi Direct connectivity.
-     *  <dt> {@link #ETHERNET_SERVICE} ("ethernet")
-     *  <dd> A {@link android.net.ethernet.EthernetManager EthernetManager} for
-     * management of Ethernet connectivity.
      * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
      * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
      * for management of input methods.
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d7bd473..4672015 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -19,9 +19,12 @@
 import android.app.PackageInstallObserver;
 import android.app.PackageUninstallObserver;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.FileBridge;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
+import java.io.OutputStream;
+
 /** {@hide} */
 public class PackageInstaller {
     private final PackageManager mPm;
@@ -127,10 +130,17 @@
             }
         }
 
-        public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes,
-                long lengthBytes) {
+        /**
+         * Open an APK file for writing, starting at the given offset. You can
+         * then stream data into the file, periodically calling
+         * {@link OutputStream#flush()} to ensure bytes have been written to
+         * disk.
+         */
+        public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) {
             try {
-                return mSession.openWrite(overlayName, offsetBytes, lengthBytes);
+                final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName,
+                        offsetBytes, lengthBytes);
+                return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             }
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 220b40d..2dce425 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.os.Debug;
 import android.os.UserHandle;
+import dalvik.system.VMRuntime;
 
 import java.nio.ByteBuffer;
 
@@ -126,8 +127,21 @@
         //    appName = "unknown";
         String appName = DdmHandleAppName.getAppName();
 
-        ByteBuffer out = ByteBuffer.allocate(20
-                            + vmIdent.length()*2 + appName.length()*2);
+        VMRuntime vmRuntime = VMRuntime.getRuntime();
+        String instructionSetDescription =
+            vmRuntime.is64Bit() ? "64-bit" : "32-bit";
+        String vmInstructionSet = vmRuntime.vmInstructionSet();
+        if (vmInstructionSet != null && vmInstructionSet.length() > 0) {
+          instructionSetDescription += " (" + vmInstructionSet + ")";
+        }
+        String vmFlags = "CheckJNI="
+            + (vmRuntime.isCheckJniEnabled() ? "true" : "false");
+
+        ByteBuffer out = ByteBuffer.allocate(28
+                            + vmIdent.length() * 2
+                            + appName.length() * 2
+                            + instructionSetDescription.length() * 2
+                            + vmFlags.length() * 2);
         out.order(ChunkHandler.CHUNK_ORDER);
         out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
         out.putInt(android.os.Process.myPid());
@@ -136,6 +150,10 @@
         putString(out, vmIdent);
         putString(out, appName);
         out.putInt(UserHandle.myUserId());
+        out.putInt(instructionSetDescription.length());
+        putString(out, instructionSetDescription);
+        out.putInt(vmFlags.length());
+        putString(out, vmFlags);
 
         Chunk reply = new Chunk(CHUNK_HELO, out);
 
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 86208fc..c593e9e 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -613,6 +613,7 @@
     }
 
     /**
+     * @hide
      * @return The permission required to access this sensor. If empty, no permission is required.
      */
     public String getRequiredPermission() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 71adf8b..22ff9c6 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -93,7 +93,7 @@
      * {@link CameraDeviceStateListener#onConfiguring()} will be called.
      * </p>
      *
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setConfiguring() {
         doStateTransition(STATE_CONFIGURING);
@@ -108,7 +108,7 @@
      * {@link CameraDeviceStateListener#onIdle()} will be called.
      * </p>
      *
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setIdle() {
         doStateTransition(STATE_IDLE);
@@ -124,7 +124,7 @@
      * </p>
      *
      * @param request A {@link RequestHolder} containing the request for the current capture.
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setCaptureStart(final RequestHolder request) {
         mCurrentRequest = request;
@@ -144,7 +144,7 @@
      *
      * @param request the {@link RequestHolder} request that created this result.
      * @param result the {@link CameraMetadataNative} result to set.
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setCaptureResult(final RequestHolder request,
                                              final CameraMetadataNative result) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 80a9598..2f2aba3 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -35,15 +35,17 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.util.Protocol;
+
 import java.net.InetAddress;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.HashMap;
 
-import com.android.internal.util.Protocol;
-
 /**
  * Class that answers queries about the state of network connectivity. It also
  * notifies applications when network connectivity changes. Get an instance
@@ -940,34 +942,18 @@
     }
 
     /**
-     * Gets the value of the setting for enabling Mobile data.
-     *
-     * @return Whether mobile data is enabled.
-     *
-     * <p>This method requires the call to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * @hide
+     * @deprecated Talk to TelephonyManager directly
      */
     public boolean getMobileDataEnabled() {
-        try {
-            return mService.getMobileDataEnabled();
-        } catch (RemoteException e) {
-            return true;
+        IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
+        if (b != null) {
+            try {
+                ITelephony it = ITelephony.Stub.asInterface(b);
+                return it.getDataEnabled();
+            } catch (RemoteException e) { }
         }
-    }
-
-    /**
-     * Sets the persisted value for enabling/disabling Mobile data.
-     *
-     * @param enabled Whether the user wants the mobile data connection used
-     *            or not.
-     * @hide
-     */
-    public void setMobileDataEnabled(boolean enabled) {
-        try {
-            mService.setMobileDataEnabled(enabled);
-        } catch (RemoteException e) {
-        }
+        return false;
     }
 
     /**
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 70cc708..5df4baf 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -47,7 +47,7 @@
     }
 
     /**
-     * Get Ethernet configuration
+     * Get Ethernet configuration.
      * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
      */
     public IpConfiguration getConfiguration() {
@@ -61,8 +61,7 @@
     }
 
     /**
-     * Set Ethernet configuration
-     * @return true if setting success
+     * Set Ethernet configuration.
      */
     public void setConfiguration(IpConfiguration config) {
         try {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d97b1e9..baec36a 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -74,9 +74,6 @@
 
     boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName);
 
-    boolean getMobileDataEnabled();
-    void setMobileDataEnabled(boolean enabled);
-
     /** Policy control over specific {@link NetworkStateTracker}. */
     void setPolicyDataEnable(int networkType, boolean enabled);
 
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index e0d69e3..e489e05 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,10 +16,13 @@
 
 package android.net;
 
+import android.net.NetworkUtils;
 import android.os.Parcelable;
 import android.os.Parcel;
 
+import java.io.IOException;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
 import javax.net.SocketFactory;
@@ -38,6 +41,8 @@
      */
     public final int netId;
 
+    private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
+
     /**
      * @hide
      */
@@ -79,6 +84,59 @@
     }
 
     /**
+     * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
+     */
+    private class NetworkBoundSocketFactory extends SocketFactory {
+        private final int mNetId;
+
+        public NetworkBoundSocketFactory(int netId) {
+            super();
+            mNetId = netId;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+            Socket socket = createSocket();
+            socket.bind(new InetSocketAddress(localHost, localPort));
+            socket.connect(new InetSocketAddress(host, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+                int localPort) throws IOException {
+            Socket socket = createSocket();
+            socket.bind(new InetSocketAddress(localAddress, localPort));
+            socket.connect(new InetSocketAddress(address, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port) throws IOException {
+            Socket socket = createSocket();
+            socket.connect(new InetSocketAddress(host, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port) throws IOException {
+            Socket socket = createSocket();
+            socket.connect(new InetSocketAddress(host, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket() throws IOException {
+            Socket socket = new Socket();
+            // Query a property of the underlying socket to ensure the underlying
+            // socket exists so a file descriptor is available to bind to a network.
+            socket.getReuseAddress();
+            NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId);
+            return socket;
+        }
+    }
+
+    /**
      * Returns a {@link SocketFactory} bound to this network.  Any {@link Socket} created by
      * this factory will have its traffic sent over this {@code Network}.  Note that if this
      * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
@@ -88,7 +146,10 @@
      *         {@code Network}.
      */
     public SocketFactory socketFactory() {
-        return null;
+        if (mNetworkBoundSocketFactory == null) {
+            mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+        }
+        return mNetworkBoundSocketFactory;
     }
 
     /**
@@ -99,6 +160,29 @@
      * doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}.
      */
     public void bindProcess() {
+        NetworkUtils.bindProcessToNetwork(netId);
+    }
+
+    /**
+     * Binds host resolutions performed by this process to this network.  {@link #bindProcess}
+     * takes precedence over this setting.
+     *
+     * @hide
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public void bindProcessForHostResolution() {
+        NetworkUtils.bindProcessToNetworkForHostResolution(netId);
+    }
+
+    /**
+     * Clears any process specific {@link Network} binding for host resolution.  This does
+     * not clear bindings enacted via {@link #bindProcess}.
+     *
+     * @hide
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public void unbindProcessForHostResolution() {
+        NetworkUtils.unbindProcessToNetworkForHostResolution();
     }
 
     /**
@@ -107,7 +191,7 @@
      * @return {@code Network} to which this process is bound.
      */
     public static Network getProcessBoundNetwork() {
-        return null;
+        return new Network(NetworkUtils.getNetworkBoundToProcess());
     }
 
     /**
@@ -115,6 +199,7 @@
      * {@link Network#bindProcess}.
      */
     public static void unbindProcess() {
+        NetworkUtils.unbindProcessToNetwork();
     }
 
     // implement the Parcelable interface
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b24d396..edb3286 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -109,6 +109,50 @@
     public native static void markSocket(int socketfd, int mark);
 
     /**
+     * Binds the current process to the network designated by {@code netId}.  All sockets created
+     * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
+     * {@link Network#socketFactory}) will be bound to this network.  Note that if this
+     * {@code Network} ever disconnects all sockets created in this way will cease to work.  This
+     * is by design so an application doesn't accidentally use sockets it thinks are still bound to
+     * a particular {@code Network}.
+     */
+    public native static void bindProcessToNetwork(int netId);
+
+    /**
+     * Clear any process specific {@code Network} binding.  This reverts a call to
+     * {@link #bindProcessToNetwork}.
+     */
+    public native static void unbindProcessToNetwork();
+
+    /**
+     * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
+     * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
+     */
+    public native static int getNetworkBoundToProcess();
+
+    /**
+     * Binds host resolutions performed by this process to the network designated by {@code netId}.
+     * {@link #bindProcessToNetwork} takes precedence over this setting.
+     *
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public native static void bindProcessToNetworkForHostResolution(int netId);
+
+    /**
+     * Clears any process specific {@link Network} binding for host resolution.  This does
+     * not clear bindings enacted via {@link #bindProcessToNetwork}.
+     *
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public native static void unbindProcessToNetworkForHostResolution();
+
+    /**
+     * Explicitly binds {@code socketfd} to the network designated by {@code netId}.  This
+     * overrides any binding via {@link #bindProcessToNetwork}.
+     */
+    public native static void bindSocketToNetwork(int socketfd, int netId);
+
+    /**
      * Convert a IPv4 address from an integer to an InetAddress.
      * @param hostAddress an int corresponding to the IPv4 address in network byte order
      */
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
new file mode 100644
index 0000000..7f8bc9f
--- /dev/null
+++ b/core/java/android/os/FileBridge.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 android.os;
+
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import libcore.io.IoBridge;
+import libcore.io.IoUtils;
+import libcore.io.Memory;
+import libcore.io.Streams;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.SyncFailedException;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Simple bridge that allows file access across process boundaries without
+ * returning the underlying {@link FileDescriptor}. This is useful when the
+ * server side needs to strongly assert that a client side is completely
+ * hands-off.
+ *
+ * @hide
+ */
+public class FileBridge extends Thread {
+    private static final String TAG = "FileBridge";
+
+    // TODO: consider extending to support bidirectional IO
+
+    private static final int MSG_LENGTH = 8;
+
+    /** CMD_WRITE [len] [data] */
+    private static final int CMD_WRITE = 1;
+    /** CMD_FSYNC */
+    private static final int CMD_FSYNC = 2;
+
+    private FileDescriptor mTarget;
+
+    private final FileDescriptor mServer = new FileDescriptor();
+    private final FileDescriptor mClient = new FileDescriptor();
+
+    private volatile boolean mClosed;
+
+    public FileBridge() {
+        try {
+            Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
+        } catch (ErrnoException e) {
+            throw new RuntimeException("Failed to create bridge");
+        }
+    }
+
+    public boolean isClosed() {
+        return mClosed;
+    }
+
+    public void setTargetFile(FileDescriptor target) {
+        mTarget = target;
+    }
+
+    public FileDescriptor getClientSocket() {
+        return mClient;
+    }
+
+    @Override
+    public void run() {
+        final byte[] temp = new byte[8192];
+        try {
+            while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
+                final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
+
+                if (cmd == CMD_WRITE) {
+                    // Shuttle data into local file
+                    int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
+                    while (len > 0) {
+                        int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
+                        IoBridge.write(mTarget, temp, 0, n);
+                        len -= n;
+                    }
+
+                } else if (cmd == CMD_FSYNC) {
+                    // Sync and echo back to confirm
+                    Os.fsync(mTarget);
+                    IoBridge.write(mServer, temp, 0, MSG_LENGTH);
+                }
+            }
+
+            // Client was closed; one last fsync
+            Os.fsync(mTarget);
+
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed during bridge: ", e);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed during bridge: ", e);
+        } finally {
+            IoUtils.closeQuietly(mTarget);
+            IoUtils.closeQuietly(mServer);
+            IoUtils.closeQuietly(mClient);
+            mClosed = true;
+        }
+    }
+
+    public static class FileBridgeOutputStream extends OutputStream {
+        private final FileDescriptor mClient;
+        private final byte[] mTemp = new byte[MSG_LENGTH];
+
+        public FileBridgeOutputStream(FileDescriptor client) {
+            mClient = client;
+        }
+
+        @Override
+        public void close() throws IOException {
+            IoBridge.closeAndSignalBlockedThreads(mClient);
+        }
+
+        @Override
+        public void flush() throws IOException {
+            Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN);
+            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+
+            // Wait for server to ack
+            if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
+                if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) {
+                    return;
+                }
+            }
+
+            throw new SyncFailedException("Failed to fsync() across bridge");
+        }
+
+        @Override
+        public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+            Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
+            Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
+            Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
+            IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
+            IoBridge.write(mClient, buffer, byteOffset, byteCount);
+        }
+
+        @Override
+        public void write(int oneByte) throws IOException {
+            Streams.writeSingleByte(this, oneByte);
+        }
+    }
+}
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 5ffffb5..e4f93a8 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -462,7 +462,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 by default.
+         * not specified, this value is set to 1 (browsable) by default.
          * </p><p>
          * Type: INTEGER (boolean)
          * </p>
@@ -470,6 +470,36 @@
         public static final String COLUMN_BROWSABLE = "browsable";
 
         /**
+         * The flag indicating whether this TV channel is searchable or not.
+         * <p>
+         * In some regions, it is not allowed to surface search results for a given channel without
+         * broadcaster's consent. This is used to impose such restriction. A value of 1 indicates
+         * the channel is searchable and can be included in search results, a value of 0 indicates
+         * the channel and its TV programs are hidden from search. If not specified, this value is
+         * set to 1 (searchable) by default.
+         * </p>
+         * <p>
+         * Type: INTEGER (boolean)
+         * </p>
+         */
+        public static final String COLUMN_SEARCHABLE = "searchable";
+
+        /**
+         * The flag indicating whether this TV channel is locked or not.
+         * <p>
+         * This is primarily used for alternative parental control to prevent unauthorized users
+         * from watching the current channel regardless of the content rating. A value of 1
+         * indicates the channel is locked and the user is required to enter passcode to unlock it
+         * in order to watch the current program from the channel, a value of 0 indicates the
+         * channel is not locked thus the user is not prompted to enter passcode If not specified,
+         * this value is set to 0 (not locked) by default.
+         * </p><p>
+         * Type: INTEGER (boolean)
+         * </p>
+         */
+        public static final String COLUMN_LOCKED = "locked";
+
+        /**
          * Generic data used by individual TV input services.
          * <p>
          * Type: BLOB
@@ -544,6 +574,33 @@
         public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
 
         /**
+         * The comma-separated genre string of this TV program.
+         * <p>
+         * Use the same language appeared in the underlying broadcast standard, if applicable. (For
+         * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, use one of the
+         * following genres:
+         * <ul>
+         *     <li>Family/Kids</li>
+         *     <li>Sports</li>
+         *     <li>Shopping</li>
+         *     <li>Movies</li>
+         *     <li>Comedy</li>
+         *     <li>Travel</li>
+         *     <li>Drama</li>
+         *     <li>Education</li>
+         *     <li>Animal/Wildlife</li>
+         *     <li>News</li>
+         *     <li>Gaming</li>
+         *     <li>Others</li>
+         * </ul>
+         * </p><p>
+         * Type: TEXT
+         * </p>
+         */
+        public static final String COLUMN_GENRE = "genre";
+
+        /**
          * The description of this TV program that is displayed to the user by default.
          * <p>
          * The maximum length of this field is 256 characters.
@@ -566,6 +623,17 @@
         public static final String COLUMN_LONG_DESCRIPTION = "long_description";
 
         /**
+         * The comma-separated audio languages of this TV program.
+         * <p>
+         * This is used to describe available audio languages included in the program. Use
+         * 3-character language code as specified by ISO 639-2.
+         * </p><p>
+         * Type: TEXT
+         * </p>
+         */
+        public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
+
+        /**
          * Generic data used by TV input services.
          * <p>
          * Type: BLOB
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index a272296..424d860 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -41,10 +41,6 @@
  * An implementation of Canvas on top of OpenGL ES 2.0.
  */
 class GLES20Canvas extends HardwareCanvas {
-    // Must match modifiers used in the JNI layer
-    private static final int MODIFIER_NONE = 0;
-    private static final int MODIFIER_SHADER = 2;
-
     private final boolean mOpaque;
     protected long mRenderer;
 
@@ -650,13 +646,8 @@
     @Override
     public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
-                    startAngle, sweepAngle, useCenter, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
+                startAngle, sweepAngle, useCenter, paint.mNativePaint);
     }
 
     private static native void nDrawArc(long renderer, float left, float top,
@@ -672,7 +663,6 @@
     public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing patches
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
@@ -682,7 +672,6 @@
     public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing patches
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
@@ -694,14 +683,8 @@
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
     }
 
     private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -710,15 +693,9 @@
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
-                    matrix.native_instance, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
+                matrix.native_instance, nativePaint);
     }
 
     private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -727,55 +704,43 @@
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
 
-            int left, top, right, bottom;
-            if (src == null) {
-                left = top = 0;
-                right = bitmap.getWidth();
-                bottom = bitmap.getHeight();
-            } else {
-                left = src.left;
-                right = src.right;
-                top = src.top;
-                bottom = src.bottom;
-            }
-
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
-                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        int left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
         }
+
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-    
-            float left, top, right, bottom;
-            if (src == null) {
-                left = top = 0;
-                right = bitmap.getWidth();
-                bottom = bitmap.getHeight();
-            } else {
-                left = src.left;
-                right = src.right;
-                top = src.top;
-                bottom = src.bottom;
-            }
-    
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
-                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+        float left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
         }
+
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
     private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -805,7 +770,6 @@
             throw new ArrayIndexOutOfBoundsException();
         }
 
-        // Shaders are ignored when drawing bitmaps
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, colors, offset, stride, x, y,
                 width, height, hasAlpha, nativePaint);
@@ -817,7 +781,6 @@
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
             int width, int height, boolean hasAlpha, Paint paint) {
-        // Shaders are ignored when drawing bitmaps
         drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
     }
 
@@ -840,14 +803,9 @@
             checkRange(colors.length, colorOffset, count);
         }
 
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
-                    verts, vertOffset, colors, colorOffset, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+                verts, vertOffset, colors, colorOffset, nativePaint);
     }
 
     private static native void nDrawBitmapMesh(long renderer, long bitmap, byte[] buffer,
@@ -856,12 +814,7 @@
 
     @Override
     public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
     }
 
     private static native void nDrawCircle(long renderer, float cx, float cy,
@@ -906,12 +859,7 @@
         if ((offset | count) < 0 || offset + count > pts.length) {
             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
         }
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
     }
 
     private static native void nDrawLines(long renderer, float[] points,
@@ -924,12 +872,7 @@
 
     @Override
     public void drawOval(RectF oval, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
     }
 
     private static native void nDrawOval(long renderer, float left, float top,
@@ -944,17 +887,12 @@
 
     @Override
     public void drawPath(Path path, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            if (path.isSimplePath) {
-                if (path.rects != null) {
-                    nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
-                }
-            } else {
-                nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+        if (path.isSimplePath) {
+            if (path.rects != null) {
+                nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
             }
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        } else {
+            nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
         }
     }
 
@@ -962,12 +900,7 @@
     private static native void nDrawRects(long renderer, long region, long paint);
 
     void drawRects(float[] rects, int count, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawRects(mRenderer, rects, count, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawRects(mRenderer, rects, count, paint.mNativePaint);
     }
 
     private static native void nDrawRects(long renderer, float[] rects, int count, long paint);
@@ -1029,12 +962,7 @@
     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
         if (count < 2) return;
 
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
     }
 
     private static native void nDrawPoints(long renderer, float[] points,
@@ -1047,12 +975,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
     }
 
     private static native void nDrawPosText(long renderer, char[] text, int index, int count,
@@ -1065,12 +988,7 @@
             throw new ArrayIndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
     }
 
     private static native void nDrawPosText(long renderer, String text, int start, int end,
@@ -1079,12 +997,7 @@
     @Override
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
         if (left == right || top == bottom) return;
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
     }
 
     private static native void nDrawRect(long renderer, float left, float top,
@@ -1108,12 +1021,7 @@
     @Override
     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint);
     }
 
     private static native void nDrawRoundRect(long renderer, float left, float top,
@@ -1125,13 +1033,8 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint,
-                paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawText(mRenderer, text, index, count, x, y,
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
     
     private static native void nDrawText(long renderer, char[] text, int index, int count,
@@ -1139,24 +1042,18 @@
 
     @Override
     public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
-        int modifiers = setupModifiers(paint);
-        try {
-            if (text instanceof String || text instanceof SpannedString ||
-                    text instanceof SpannableString) {
-                nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
-                        paint.mNativePaint, paint.mNativeTypeface);
-            } else if (text instanceof GraphicsOperations) {
-                ((GraphicsOperations) text).drawText(this, start, end, x, y,
-                                                         paint);
-            } else {
-                char[] buf = TemporaryBuffer.obtain(end - start);
-                TextUtils.getChars(text, start, end, buf, 0);
-                nDrawText(mRenderer, buf, 0, end - start, x, y,
-                        paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
-                TemporaryBuffer.recycle(buf);
-            }
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+                    paint.mNativePaint, paint.mNativeTypeface);
+        } else if (text instanceof GraphicsOperations) {
+            ((GraphicsOperations) text).drawText(this, start, end, x, y, paint);
+        } else {
+            char[] buf = TemporaryBuffer.obtain(end - start);
+            TextUtils.getChars(text, start, end, buf, 0);
+            nDrawText(mRenderer, buf, 0, end - start, x, y,
+                    paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
+            TemporaryBuffer.recycle(buf);
         }
     }
 
@@ -1166,13 +1063,8 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint,
-                paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawText(mRenderer, text, start, end, x, y,
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawText(long renderer, String text, int start, int end,
@@ -1180,13 +1072,8 @@
 
     @Override
     public void drawText(String text, float x, float y, Paint paint) {
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
-                    paint.mNativePaint, paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawText(mRenderer, text, 0, text.length(), x, y,
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     @Override
@@ -1196,13 +1083,8 @@
             throw new ArrayIndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
-                    paint.mBidiFlags, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
+                paint.mBidiFlags, paint.mNativePaint);
     }
 
     private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count,
@@ -1212,13 +1094,8 @@
     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
         if (text.length() == 0) return;
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
-                    paint.mBidiFlags, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
+                paint.mBidiFlags, paint.mNativePaint);
     }
 
     private static native void nDrawTextOnPath(long renderer, String text, int start, int end,
@@ -1234,13 +1111,8 @@
             throw new IllegalArgumentException("Unknown direction: " + dir);
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
-                    paint.mNativePaint, paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+                paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawTextRun(long renderer, char[] text, int index, int count,
@@ -1253,27 +1125,22 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            int flags = dir == 0 ? 0 : 1;
-            if (text instanceof String || text instanceof SpannedString ||
-                    text instanceof SpannableString) {
-                nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
-                        contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
-            } else if (text instanceof GraphicsOperations) {
-                ((GraphicsOperations) text).drawTextRun(this, start, end,
-                        contextStart, contextEnd, x, y, flags, paint);
-            } else {
-                int contextLen = contextEnd - contextStart;
-                int len = end - start;
-                char[] buf = TemporaryBuffer.obtain(contextLen);
-                TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
-                nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
-                        x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
-                TemporaryBuffer.recycle(buf);
-            }
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        int flags = dir == 0 ? 0 : 1;
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+                    contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+        } else if (text instanceof GraphicsOperations) {
+            ((GraphicsOperations) text).drawTextRun(this, start, end,
+                    contextStart, contextEnd, x, y, flags, paint);
+        } else {
+            int contextLen = contextEnd - contextStart;
+            int len = end - start;
+            char[] buf = TemporaryBuffer.obtain(contextLen);
+            TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+            nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+                    x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+            TemporaryBuffer.recycle(buf);
         }
     }
 
@@ -1286,40 +1153,4 @@
             int indexOffset, int indexCount, Paint paint) {
         // TODO: Implement
     }
-
-    private int setupModifiers(Bitmap b, Paint paint) {
-        if (b.getConfig() != Bitmap.Config.ALPHA_8) {
-            return MODIFIER_NONE;
-        } else {
-            return setupModifiers(paint);
-        }
-    }
-
-    private int setupModifiers(Paint paint) {
-        int modifiers = MODIFIER_NONE;
-
-        final Shader shader = paint.getShader();
-        if (shader != null) {
-            nSetupShader(mRenderer, shader.native_shader);
-            modifiers |= MODIFIER_SHADER;
-        }
-
-        return modifiers;
-    }
-
-    private int setupModifiers(Paint paint, int flags) {
-        int modifiers = MODIFIER_NONE;
-
-        final Shader shader = paint.getShader();
-        if (shader != null && (flags & MODIFIER_SHADER) != 0) {
-            nSetupShader(mRenderer, shader.native_shader);
-            modifiers |= MODIFIER_SHADER;
-        }
-
-        return modifiers;
-    }
-
-    private static native void nSetupShader(long renderer, long shader);
-
-    private static native void nResetModifiers(long renderer, int modifiers);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0f21c1d..6396781 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9236,6 +9236,30 @@
     }
 
     /**
+     * Request unbuffered dispatch of the given stream of MotionEvents to this View.
+     *
+     * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input
+     * system not batch {@link MotionEvent}s but instead deliver them as soon as they're
+     * available. This method should only be called for touch events.
+     *
+     * <p class="note">This api is not intended for most applications. Buffered dispatch
+     * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
+     * streams will not improve your input latency. Side effects include: increased latency,
+     * jittery scrolls and inability to take advantage of system resampling. Talk to your input
+     * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
+     * you.</p>
+     */
+    public final void requestUnbufferedDispatch(MotionEvent event) {
+        final int action = event.getAction();
+        if (mAttachInfo == null
+                || action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE
+                || !event.isTouchEvent()) {
+            return;
+        }
+        mAttachInfo.mUnbufferedDispatchRequested = true;
+    }
+
+    /**
      * Set flags controlling behavior of this view.
      *
      * @param flags Constant indicating the value which should be set
@@ -19760,6 +19784,12 @@
         boolean mInTouchMode;
 
         /**
+         * Indicates whether the view has requested unbuffered input dispatching for the current
+         * event stream.
+         */
+        boolean mUnbufferedDispatchRequested;
+
+        /**
          * Indicates that ViewAncestor should trigger a global layout change
          * the next time it performs a traversal
          */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 799a406..148d9ec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -230,6 +230,7 @@
     QueuedInputEvent mPendingInputEventTail;
     int mPendingInputEventCount;
     boolean mProcessInputEventsScheduled;
+    boolean mUnbufferedInputDispatch;
     String mPendingInputEventQueueLengthCounterName = "pq";
 
     InputStage mFirstInputStage;
@@ -1005,7 +1006,9 @@
             mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
             mChoreographer.postCallback(
                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
-            scheduleConsumeBatchedInput();
+            if (!mUnbufferedInputDispatch) {
+                scheduleConsumeBatchedInput();
+            }
         }
     }
 
@@ -2604,7 +2607,7 @@
         }
 
         final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
-        final Rect bounds = mView.mAttachInfo.mTmpInvalRect;
+        final Rect bounds = mAttachInfo.mTmpInvalRect;
         if (provider == null) {
             host.getBoundsOnScreen(bounds);
         } else if (mAccessibilityFocusedVirtualView != null) {
@@ -3886,6 +3889,18 @@
             }
         }
 
+        @Override
+        protected void onDeliverToNext(QueuedInputEvent q) {
+            if (mUnbufferedInputDispatch
+                    && q.mEvent instanceof MotionEvent
+                    && ((MotionEvent)q.mEvent).isTouchEvent()
+                    && isTerminalInputEvent(q.mEvent)) {
+                mUnbufferedInputDispatch = false;
+                scheduleConsumeBatchedInput();
+            }
+            super.onDeliverToNext(q);
+        }
+
         private int processKeyEvent(QueuedInputEvent q) {
             final KeyEvent event = (KeyEvent)q.mEvent;
 
@@ -3998,10 +4013,15 @@
         private int processPointerEvent(QueuedInputEvent q) {
             final MotionEvent event = (MotionEvent)q.mEvent;
 
-            if (mView.dispatchPointerEvent(event)) {
-                return FINISH_HANDLED;
+            mAttachInfo.mUnbufferedDispatchRequested = false;
+            boolean handled = mView.dispatchPointerEvent(event);
+            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
+                mUnbufferedInputDispatch = true;
+                if (mConsumeBatchedInputScheduled) {
+                    scheduleConsumeBatchedInputImmediately();
+                }
             }
-            return FORWARD;
+            return handled ? FINISH_HANDLED : FORWARD;
         }
 
         private int processTrackballEvent(QueuedInputEvent q) {
@@ -5266,6 +5286,8 @@
                 writer.print(" mRemoved="); writer.println(mRemoved);
         writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
                 writer.println(mConsumeBatchedInputScheduled);
+        writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled=");
+                writer.println(mConsumeBatchedInputImmediatelyScheduled);
         writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
                 writer.println(mPendingInputEventCount);
         writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
@@ -5676,6 +5698,7 @@
     private void finishInputEvent(QueuedInputEvent q) {
         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                 q.mEvent.getSequenceNumber());
+
         if (q.mReceiver != null) {
             boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
             q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -5715,15 +5738,25 @@
         }
     }
 
+    void scheduleConsumeBatchedInputImmediately() {
+        if (!mConsumeBatchedInputImmediatelyScheduled) {
+            unscheduleConsumeBatchedInput();
+            mConsumeBatchedInputImmediatelyScheduled = true;
+            mHandler.post(mConsumeBatchedInputImmediatelyRunnable);
+        }
+    }
+
     void doConsumeBatchedInput(long frameTimeNanos) {
         if (mConsumeBatchedInputScheduled) {
             mConsumeBatchedInputScheduled = false;
             if (mInputEventReceiver != null) {
-                if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) {
+                if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
+                        && frameTimeNanos != -1) {
                     // If we consumed a batch here, we want to go ahead and schedule the
                     // consumption of batched input events on the next frame. Otherwise, we would
                     // wait until we have more input events pending and might get starved by other
-                    // things occurring in the process.
+                    // things occurring in the process. If the frame time is -1, however, then
+                    // we're in a non-batching mode, so there's no need to schedule this.
                     scheduleConsumeBatchedInput();
                 }
             }
@@ -5751,7 +5784,11 @@
 
         @Override
         public void onBatchedInputEventPending() {
-            scheduleConsumeBatchedInput();
+            if (mUnbufferedInputDispatch) {
+                super.onBatchedInputEventPending();
+            } else {
+                scheduleConsumeBatchedInput();
+            }
         }
 
         @Override
@@ -5772,6 +5809,16 @@
             new ConsumeBatchedInputRunnable();
     boolean mConsumeBatchedInputScheduled;
 
+    final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
+        @Override
+        public void run() {
+            doConsumeBatchedInput(-1);
+        }
+    }
+    final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable =
+            new ConsumeBatchedInputImmediatelyRunnable();
+    boolean mConsumeBatchedInputImmediatelyScheduled;
+
     final class InvalidateOnAnimationRunnable implements Runnable {
         private boolean mPosted;
         private final ArrayList<View> mViews = new ArrayList<View>();
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 8511601..97f89aa 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -104,14 +104,16 @@
  *
  * <h4>Excess Space Distribution</h4>
  *
- * GridLayout's distribution of excess space is based on <em>priority</em>
- * rather than <em>weight</em>.
+ * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
+ * In the event that no weights are specified, the previous conventions are respected and
+ * columns and rows are taken as flexible if their views specify some form of alignment
+ * within their groups.
  * <p>
- * A child's ability to stretch is inferred from the alignment properties of
- * its row and column groups (which are typically set by setting the
- * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
- * If alignment was defined along a given axis then the component
- * is taken as <em>flexible</em> in that direction. If no alignment was set,
+ * The flexibility of a view is therefore influenced by its alignment which is,
+ * in turn, typically defined by setting the
+ * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
+ * If either a weight or alignment were defined along a given axis then the component
+ * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
  * the component is instead assumed to be <em>inflexible</em>.
  * <p>
  * Multiple components in the same row or column group are
@@ -122,12 +124,16 @@
  * elements is flexible if <em>one</em> of its elements is flexible.
  * <p>
  * To make a column stretch, make sure all of the components inside it define a
- * gravity. To prevent a column from stretching, ensure that one of the components
- * in the column does not define a gravity.
+ * weight or a gravity. To prevent a column from stretching, ensure that one of the components
+ * in the column does not define a weight or a gravity.
  * <p>
  * When the principle of flexibility does not provide complete disambiguation,
  * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
- * and <em>bottom</em> edges.
+ * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
+ * parameters as a constraint in the a set of variables that define the grid-lines along a
+ * given axis. During layout, GridLayout solves the constraints so as to return the unique
+ * solution to those constraints for which all variables are less-than-or-equal-to
+ * the corresponding value in any other valid solution.
  *
  * <h4>Interpretation of GONE</h4>
  *
@@ -140,18 +146,6 @@
  * had never been added to it.
  * These statements apply equally to rows as well as columns, and to groups of rows or columns.
  *
- * <h5>Limitations</h5>
- *
- * GridLayout does not provide support for the principle of <em>weight</em>, as defined in
- * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
- * to configure a GridLayout to distribute excess space between multiple components.
- * <p>
- * Some common use-cases may nevertheless be accommodated as follows.
- * To place equal amounts of space around a component in a cell group;
- * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}).
- * For complete control over excess space distribution in a row or column;
- * use a {@link LinearLayout} subview to hold the components in the associated cell group.
- * When using either of these techniques, bear in mind that cell groups may be defined to overlap.
  * <p>
  * See {@link GridLayout.LayoutParams} for a full description of the
  * layout parameters used by GridLayout.
@@ -1693,8 +1687,74 @@
             return trailingMargins;
         }
 
-        private void computeLocations(int[] a) {
+        private void solve(int[] a) {
             solve(getArcs(), a);
+        }
+
+        private void resetDeltas() {
+            Bounds[] values = getGroupBounds().values;
+            for (int i = 0, N = values.length; i < N; i++) {
+                values[i].shareOfDelta = 0;
+            }
+        }
+
+        private boolean requiresWeightDistribution() {
+            Bounds[] values = getGroupBounds().values;
+            for (int i = 0, N = values.length; i < N; i++) {
+                if (values[i].weight != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void shareOutDelta(int[] locations) {
+            PackedMap<Spec, Bounds> groupBounds = getGroupBounds();
+            Spec[] specs = groupBounds.keys;
+            Bounds[] bounds = groupBounds.values;
+            int totalDelta = 0;
+            float totalWeight = 0;
+            final int N = bounds.length;
+
+            for (int i = 0; i < N; i++) {
+                Spec spec = specs[i];
+                Bounds bound = bounds[i];
+                if (bound.weight != 0) {
+                    int minSize = bound.size(true);
+                    Interval span = spec.span;
+                    int actualSize = locations[span.max] - locations[span.min];
+                    int delta = actualSize - minSize;
+                    totalDelta += delta;
+                    totalWeight += bound.weight;
+                }
+            }
+            for (int i = 0; i < N; i++) {
+                Bounds bound = bounds[i];
+                float weight = bound.weight;
+                if (weight != 0) {
+                    int delta = Math.round((weight * totalDelta / totalWeight));
+                    bound.shareOfDelta = delta;
+                    // the two adjustments below compensate for the rounding above
+                    totalDelta -= delta;
+                    totalWeight -= weight;
+                }
+            }
+        }
+
+        private void solveAndDistributeSpace(int[] a) {
+            resetDeltas();
+            solve(a);
+            if (requiresWeightDistribution()) {
+                shareOutDelta(a);
+                arcsValid = false;
+                forwardLinksValid = false;
+                backwardLinksValid = false;
+                solve(a);
+            }
+        }
+
+        private void computeLocations(int[] a) {
+            solveAndDistributeSpace(a);
             if (!orderPreserved) {
                 // Solve returns the smallest solution to the constraint system for which all
                 // values are positive. One value is therefore zero - though if the row/col
@@ -1810,6 +1870,23 @@
      * both aspects of alignment within the cell group. It is also possible to specify a child's
      * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
      * method.
+     * <p>
+     * The weight property is also included in Spec and specifies the proportion of any
+     * excess space that is due to the enclosing row or column.
+     * GridLayout's model of flexibility and weight is broadly based on the physical properties of
+     * systems of mechanical springs. For example, a column in a GridLayout is modeled as
+     * if a set of springs were attached in parallel at their ends. Columns are therefore
+     * flexible only if and only if all views inside them are flexible. Similarly, the combined
+     * weight of a column is defined as the <em>minimum</em> of the weights of the components it
+     * contains. So, if any one component in a column of components has a weight of zero,
+     * the entire group has a weight of zero and will not receive any of the
+     * excess space that GridLayout distributes in its second
+     * (internal) layout pass. The default weight of a component is zero,
+     * reflecting inflexibility, but the default weight of a column (with nothing in it)
+     * is effectively infinite, reflecting the fact that a parallel group of
+     * springs is infinitely flexible when it contains no springs.
+     * <p>
+     * The above comments apply equally to rows and groups of rows and columns.
      *
      * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
      *
@@ -1851,9 +1928,11 @@
      *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
      *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
      *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
+     *     <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
      *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
      *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
      *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
+     *     <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
      * </ul>
      *
      * See {@link GridLayout} for a more complete description of the conventions
@@ -1861,8 +1940,10 @@
      *
      * @attr ref android.R.styleable#GridLayout_Layout_layout_row
      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
+     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
      * @attr ref android.R.styleable#GridLayout_Layout_layout_column
      * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
+     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
      * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
      */
     public static class LayoutParams extends MarginLayoutParams {
@@ -1889,9 +1970,11 @@
 
         private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
         private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
+        private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
 
         private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
         private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
+        private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
 
         private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
 
@@ -2034,11 +2117,13 @@
 
                 int column = a.getInt(COLUMN, DEFAULT_COLUMN);
                 int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
-                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
+                float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
+                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
 
                 int row = a.getInt(ROW, DEFAULT_ROW);
                 int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
-                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
+                float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
+                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
             } finally {
                 a.recycle();
             }
@@ -2244,6 +2329,8 @@
         public int before;
         public int after;
         public int flexibility; // we're flexible iff all included specs are flexible
+        public float weight; // the min of the weights of the individual specs
+        public int shareOfDelta;
 
         private Bounds() {
             reset();
@@ -2253,6 +2340,7 @@
             before = Integer.MIN_VALUE;
             after = Integer.MIN_VALUE;
             flexibility = CAN_STRETCH; // from the above, we're flexible when empty
+            weight = Float.MAX_VALUE; // default  is large => row/cols take all slack when empty
         }
 
         protected void include(int before, int after) {
@@ -2266,7 +2354,7 @@
                     return MAX_SIZE;
                 }
             }
-            return before + after;
+            return before + after + shareOfDelta;
         }
 
         protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
@@ -2275,6 +2363,7 @@
 
         protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
             this.flexibility &= spec.getFlexibility();
+            weight = Math.min(weight, spec.weight);
             boolean horizontal = axis.horizontal;
             int size = gl.getMeasurementIncludingMargin(c, horizontal);
             Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
@@ -2401,36 +2490,43 @@
      *   <li>{@link #spec(int, int)}</li>
      *   <li>{@link #spec(int, Alignment)}</li>
      *   <li>{@link #spec(int, int, Alignment)}</li>
+     *   <li>{@link #spec(int, float)}</li>
+     *   <li>{@link #spec(int, int, float)}</li>
+     *   <li>{@link #spec(int, Alignment, float)}</li>
+     *   <li>{@link #spec(int, int, Alignment, float)}</li>
      * </ul>
      *
      */
     public static class Spec {
         static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
+        static final float DEFAULT_WEIGHT = 0;
 
         final boolean startDefined;
         final Interval span;
         final Alignment alignment;
+        final float weight;
 
-        private Spec(boolean startDefined, Interval span, Alignment alignment) {
+        private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
             this.startDefined = startDefined;
             this.span = span;
             this.alignment = alignment;
+            this.weight = weight;
         }
 
-        private Spec(boolean startDefined, int start, int size, Alignment alignment) {
-            this(startDefined, new Interval(start, start + size), alignment);
+        private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
+            this(startDefined, new Interval(start, start + size), alignment, weight);
         }
 
         final Spec copyWriteSpan(Interval span) {
-            return new Spec(startDefined, span, alignment);
+            return new Spec(startDefined, span, alignment, weight);
         }
 
         final Spec copyWriteAlignment(Alignment alignment) {
-            return new Spec(startDefined, span, alignment);
+            return new Spec(startDefined, span, alignment, weight);
         }
 
         final int getFlexibility() {
-            return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
+            return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
         }
 
         /**
@@ -2478,6 +2574,7 @@
      * <ul>
      *     <li> {@code spec.span = [start, start + size]} </li>
      *     <li> {@code spec.alignment = alignment} </li>
+     *     <li> {@code spec.weight = weight} </li>
      * </ul>
      * <p>
      * To leave the start index undefined, use the value {@link #UNDEFINED}.
@@ -2485,9 +2582,55 @@
      * @param start     the start
      * @param size      the size
      * @param alignment the alignment
+     * @param weight    the weight
+     */
+    public static Spec spec(int start, int size, Alignment alignment, float weight) {
+        return new Spec(start != UNDEFINED, start, size, alignment, weight);
+    }
+
+    /**
+     * Equivalent to: {@code spec(start, 1, alignment, weight)}.
+     *
+     * @param start     the start
+     * @param alignment the alignment
+     * @param weight    the weight
+     */
+    public static Spec spec(int start, Alignment alignment, float weight) {
+        return spec(start, 1, alignment, weight);
+    }
+
+    /**
+     * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
+     * where {@code default_alignment} is specified in
+     * {@link android.widget.GridLayout.LayoutParams}.
+     *
+     * @param start  the start
+     * @param size   the size
+     * @param weight the weight
+     */
+    public static Spec spec(int start, int size, float weight) {
+        return spec(start, size, UNDEFINED_ALIGNMENT, weight);
+    }
+
+    /**
+     * Equivalent to: {@code spec(start, 1, weight)}.
+     *
+     * @param start  the start
+     * @param weight the weight
+     */
+    public static Spec spec(int start, float weight) {
+        return spec(start, 1, weight);
+    }
+
+    /**
+     * Equivalent to: {@code spec(start, size, alignment, 0f)}.
+     *
+     * @param start     the start
+     * @param size      the size
+     * @param alignment the alignment
      */
     public static Spec spec(int start, int size, Alignment alignment) {
-        return new Spec(start != UNDEFINED, start, size, alignment);
+        return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
     }
 
     /**
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 495d5c688..70c77b8 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Slog;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
@@ -211,54 +212,111 @@
         }
     }
 
-    private final InputMethodSettings mSettings;
-    private InputMethodAndSubtypeList mSubtypeList;
+    private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
+        return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
+                subtype.hashCode()) : NOT_A_SUBTYPE_ID;
+    }
 
-    @VisibleForTesting
-    public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList,
-            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
-        if (imi == null) {
-            return null;
+    private static class StaticRotationList {
+        private final List<ImeSubtypeListItem> mImeSubtypeList;
+        public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
+            mImeSubtypeList = imeSubtypeList;
         }
-        if (imList.size() <= 1) {
-            return null;
-        }
-        // Here we have two rotation groups, depending on the returned boolean value of
-        // {@link InputMethodInfo#supportsSwitchingToNextInputMethod()}.
-        final boolean expectedValueOfSupportsSwitchingToNextInputMethod =
-                imi.supportsSwitchingToNextInputMethod();
-        final int N = imList.size();
-        final int currentSubtypeId =
-                subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
-                        subtype.hashCode()) : NOT_A_SUBTYPE_ID;
-        for (int i = 0; i < N; ++i) {
-            final ImeSubtypeListItem isli = imList.get(i);
-            // Skip until the current IME/subtype is found.
-            if (!isli.mImi.equals(imi) || isli.mSubtypeId != currentSubtypeId) {
-                continue;
-            }
-            // Found the current IME/subtype. Start searching the next IME/subtype from here.
-            for (int j = 0; j < N - 1; ++j) {
-                final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
-                // Skip if the candidate doesn't belong to the expected rotation group.
-                if (expectedValueOfSupportsSwitchingToNextInputMethod !=
-                        candidate.mImi.supportsSwitchingToNextInputMethod()) {
-                    continue;
+
+        /**
+         * Returns the index of the specified input method and subtype in the given list.
+         * @param imi The {@link InputMethodInfo} to be searched.
+         * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method
+         * does not have a subtype.
+         * @return The index in the given list. -1 if not found.
+         */
+        private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
+            final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+            final int N = mImeSubtypeList.size();
+            for (int i = 0; i < N; ++i) {
+                final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
+                // Skip until the current IME/subtype is found.
+                if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
+                    return i;
                 }
+            }
+            return -1;
+        }
+
+        public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+                InputMethodInfo imi, InputMethodSubtype subtype) {
+            if (imi == null) {
+                return null;
+            }
+            if (mImeSubtypeList.size() <= 1) {
+                return null;
+            }
+            final int currentIndex = getIndex(imi, subtype);
+            if (currentIndex < 0) {
+                return null;
+            }
+            final int N = mImeSubtypeList.size();
+            for (int offset = 1; offset < N; ++offset) {
+                // Start searching the next IME/subtype from the next of the current index.
+                final int candidateIndex = (currentIndex + offset) % N;
+                final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
                 // Skip if searching inside the current IME only, but the candidate is not
                 // the current IME.
-                if (onlyCurrentIme && !candidate.mImi.equals(imi)) {
+                if (onlyCurrentIme && !imi.equals(candidate.mImi)) {
                     continue;
                 }
                 return candidate;
             }
-            // No appropriate IME/subtype is found in the list. Give up.
             return null;
         }
-        // The current IME/subtype is not found in the list. Give up.
-        return null;
     }
 
+    @VisibleForTesting
+    public static class ControllerImpl {
+        private final StaticRotationList mSwitchingAwareSubtypeList;
+        private final StaticRotationList mSwitchingUnawareSubtypeList;
+
+        public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
+            mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+                    true /* supportsSwitchingToNextInputMethod */));
+            mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+                    false /* supportsSwitchingToNextInputMethod */));
+        }
+
+        public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
+                InputMethodSubtype subtype) {
+            if (imi == null) {
+                return null;
+            }
+            if (imi.supportsSwitchingToNextInputMethod()) {
+                return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+                        subtype);
+            } else {
+                return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi,
+                        subtype);
+            }
+        }
+
+        private static List<ImeSubtypeListItem> filterImeSubtypeList(
+                final List<ImeSubtypeListItem> items,
+                final boolean supportsSwitchingToNextInputMethod) {
+            final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
+            final int ALL_ITEMS_COUNT = items.size();
+            for (int i = 0; i < ALL_ITEMS_COUNT; i++) {
+                final ImeSubtypeListItem item = items.get(i);
+                if (item.mImi.supportsSwitchingToNextInputMethod() ==
+                        supportsSwitchingToNextInputMethod) {
+                    result.add(item);
+                }
+            }
+            return result;
+        }
+    }
+
+    private final InputMethodSettings mSettings;
+    private InputMethodAndSubtypeList mSubtypeList;
+    private ControllerImpl mController;
+
     private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
         mSettings = settings;
         resetCircularListLocked(context);
@@ -276,12 +334,18 @@
 
     public void resetCircularListLocked(Context context) {
         mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+        mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList());
     }
 
-    public ImeSubtypeListItem getNextInputMethodLocked(
-            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
-        return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
-                onlyCurrentIme, imi, subtype);
+    public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
+            InputMethodSubtype subtype) {
+        if (mController == null) {
+            if (DEBUG) {
+                Log.e(TAG, "mController shouldn't be null.");
+            }
+            return null;
+        }
+        return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
     }
 
     public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 835a648..2d72494 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -172,8 +172,10 @@
 	$(call include-path-for, bluedroid) \
 	$(call include-path-for, libhardware)/hardware \
 	$(call include-path-for, libhardware_legacy)/hardware_legacy \
+	$(TOP)/bionic/libc/dns/include \
 	$(TOP)/frameworks/av/include \
 	$(TOP)/system/media/camera/include \
+	$(TOP)/system/netd/include \
 	external/pdfium/core/include/fpdfapi \
 	external/pdfium/core/include/fpdfdoc \
 	external/pdfium/fpdfsdk/include \
@@ -232,6 +234,7 @@
 	libaudioutils \
 	libpdfium \
 	libimg_utils \
+	libnetd_client \
 
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index b389d9e..0cfcaef 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -50,26 +50,16 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle, jlong skiaShaderHandle)
+static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle)
 {
     SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    SkiaShader* skiaShader = reinterpret_cast<SkiaShader*>(skiaShaderHandle);
     SkSafeUnref(shader);
-    // skiaShader == NULL when not !USE_OPENGL_RENDERER, so no need to delete it outside the ifdef
-#ifdef USE_OPENGL_RENDERER
-    if (android::uirenderer::Caches::hasInstance()) {
-        android::uirenderer::Caches::getInstance().resourceCache.destructor(skiaShader);
-    } else {
-        delete skiaShader;
-    }
-#endif
 }
 
 static void Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle,
-        jlong skiaShaderHandle, jlong matrixHandle)
+        jlong matrixHandle)
 {
     SkShader* shader       = reinterpret_cast<SkShader*>(shaderHandle);
-    SkiaShader* skiaShader = reinterpret_cast<SkiaShader*>(skiaShaderHandle);
     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
     if (shader) {
         if (NULL == matrix) {
@@ -78,9 +68,6 @@
         else {
             shader->setLocalMatrix(*matrix);
         }
-#ifdef USE_OPENGL_RENDERER
-        skiaShader->setMatrix(const_cast<SkMatrix*>(matrix));
-#endif
     }
 }
 
@@ -98,20 +85,6 @@
     return reinterpret_cast<jlong>(s);
 }
 
-static jlong BitmapShader_postConstructor(JNIEnv* env, jobject o, jlong shaderHandle,
-        jlong bitmapHandle, jint tileModeX, jint tileModeY) {
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-#ifdef USE_OPENGL_RENDERER
-    SkiaShader* skiaShader = new SkiaBitmapShader(bitmap, shader,
-            static_cast<SkShader::TileMode>(tileModeX), static_cast<SkShader::TileMode>(tileModeY),
-            NULL, !shader->isOpaque());
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong LinearGradient_create1(JNIEnv* env, jobject o,
@@ -141,105 +114,6 @@
     return reinterpret_cast<jlong>(shader);
 }
 
-static jlong LinearGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
-        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jintArray colorArray,
-        jfloatArray posArray, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    size_t count = env->GetArrayLength(colorArray);
-    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
-    jfloat* storedBounds = new jfloat[4];
-    storedBounds[0] = x0; storedBounds[1] = y0;
-    storedBounds[2] = x1; storedBounds[3] = y1;
-    
-    bool missFirst = false;
-    bool missLast = false;
-    size_t stopCount = count;
-
-    jfloat* storedPositions = NULL;
-    if (posArray) {
-        AutoJavaFloatArray autoPos(env, posArray, count);
-        const float* posValues = autoPos.ptr();
-
-        missFirst = posValues[0] != 0.0f;
-        missLast = posValues[count - 1] != 1.0f;
-
-        stopCount += missFirst + missLast;
-        storedPositions = new jfloat[stopCount];
-
-        if (missFirst) {
-            storedPositions[0] = 0.0f;
-        }
-
-        for (size_t i = missFirst; i < count + missFirst; i++) {
-            storedPositions[i] = posValues[i - missFirst];
-        }
-
-        if (missLast) {
-            storedPositions[stopCount - 1] = 1.0f;
-        }
-    } else {
-        storedPositions = new jfloat[count];
-        storedPositions[0] = 0.0f;
-        const jfloat step = 1.0f / (count - 1);
-        for (size_t i = 1; i < count - 1; i++) {
-            storedPositions[i] = step * i;
-        }
-        storedPositions[count - 1] = 1.0f;
-    }
-
-    uint32_t* storedColors = new uint32_t[stopCount];
-
-    if (missFirst) {
-        storedColors[0] = static_cast<uint32_t>(colorValues[0]);
-    }
-
-    for (size_t i = missFirst; i < count + missFirst; i++) {
-        storedColors[i] = static_cast<uint32_t>(colorValues[i - missFirst]);
-    }
-
-    if (missLast) {
-        storedColors[stopCount - 1] = static_cast<uint32_t>(colorValues[count - 1]);
-    }
-
-    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
-            storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
-            !shader->isOpaque());
-
-    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
-static jlong LinearGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
-        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    float* storedBounds = new float[4];
-    storedBounds[0] = x0; storedBounds[1] = y0;
-    storedBounds[2] = x1; storedBounds[3] = y1;
-
-    float* storedPositions = new float[2];
-    storedPositions[0] = 0.0f;
-    storedPositions[1] = 1.0f;
-
-    uint32_t* storedColors = new uint32_t[2];
-    storedColors[0] = static_cast<uint32_t>(color0);
-    storedColors[1] = static_cast<uint32_t>(color1);
-
-    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
-            storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
-            !shader->isOpaque());
-
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
 static jlong LinearGradient_create2(JNIEnv* env, jobject o,
                                     jfloat x0, jfloat y0, jfloat x1, jfloat y1,
                                     jint color0, jint color1, jint tileMode)
@@ -300,67 +174,6 @@
     return reinterpret_cast<jlong>(s);
 }
 
-static jlong RadialGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
-        jfloat x, jfloat y, jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    size_t count = env->GetArrayLength(colorArray);
-    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
-    jfloat* storedPositions = new jfloat[count];
-    uint32_t* storedColors = new uint32_t[count];
-    for (size_t i = 0; i < count; i++) {
-        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
-    }
-
-    if (posArray) {
-        AutoJavaFloatArray autoPos(env, posArray, count);
-        const float* posValues = autoPos.ptr();
-        for (size_t i = 0; i < count; i++) {
-            storedPositions[i] = posValues[i];
-        }
-    } else {
-        storedPositions[0] = 0.0f;
-        const jfloat step = 1.0f / (count - 1);
-        for (size_t i = 1; i < count - 1; i++) {
-            storedPositions[i] = step * i;
-        }
-        storedPositions[count - 1] = 1.0f;
-    }
-
-    SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
-            storedPositions, count, shader, (SkShader::TileMode) tileMode, NULL,
-            !shader->isOpaque());
-
-    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
-static jlong RadialGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
-        jfloat x, jfloat y, jfloat radius, jint color0, jint color1, jint tileMode) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    float* storedPositions = new float[2];
-    storedPositions[0] = 0.0f;
-    storedPositions[1] = 1.0f;
-
-    uint32_t* storedColors = new uint32_t[2];
-    storedColors[0] = static_cast<uint32_t>(color0);
-    storedColors[1] = static_cast<uint32_t>(color1);
-
-    SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
-            storedPositions, 2, shader, (SkShader::TileMode) tileMode, NULL,
-            !shader->isOpaque());
-
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static jlong SweepGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y,
@@ -393,65 +206,6 @@
     return reinterpret_cast<jlong>(s);
 }
 
-static jlong SweepGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
-        jfloat x, jfloat y, jintArray colorArray, jfloatArray posArray) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    size_t count = env->GetArrayLength(colorArray);
-    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
-    jfloat* storedPositions = new jfloat[count];
-    uint32_t* storedColors = new uint32_t[count];
-    for (size_t i = 0; i < count; i++) {
-        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
-    }
-
-    if (posArray) {
-        AutoJavaFloatArray autoPos(env, posArray, count);
-        const float* posValues = autoPos.ptr();
-        for (size_t i = 0; i < count; i++) {
-            storedPositions[i] = posValues[i];
-        }
-    } else {
-        storedPositions[0] = 0.0f;
-        const jfloat step = 1.0f / (count - 1);
-        for (size_t i = 1; i < count - 1; i++) {
-            storedPositions[i] = step * i;
-        }
-        storedPositions[count - 1] = 1.0f;
-    }
-
-    SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, count,
-            shader, NULL, !shader->isOpaque());
-
-    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
-static jlong SweepGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
-        jfloat x, jfloat y, jint color0, jint color1) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-    float* storedPositions = new float[2];
-    storedPositions[0] = 0.0f;
-    storedPositions[1] = 1.0f;
-
-    uint32_t* storedColors = new uint32_t[2];
-    storedColors[0] = static_cast<uint32_t>(color0);
-    storedColors[1] = static_cast<uint32_t>(color1);
-
-    SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, 2,
-            shader, NULL, !shader->isOpaque());
-
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong ComposeShader_create1(JNIEnv* env, jobject o,
@@ -476,40 +230,6 @@
     return reinterpret_cast<jlong>(shader);
 }
 
-static jlong ComposeShader_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
-        jlong shaderAHandle, jlong shaderBHandle, jint porterDuffModeHandle) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader *>(shaderHandle);
-    SkiaShader* shaderA = reinterpret_cast<SkiaShader *>(shaderAHandle);
-    SkiaShader* shaderB = reinterpret_cast<SkiaShader *>(shaderBHandle);
-    SkPorterDuff::Mode porterDuffMode = static_cast<SkPorterDuff::Mode>(porterDuffModeHandle);
-    SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
-    SkiaShader* skiaShader = new SkiaComposeShader(shaderA, shaderB, mode, shader);
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
-static jlong ComposeShader_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
-        jlong shaderAHandle, jlong shaderBHandle, jlong modeHandle) {
-#ifdef USE_OPENGL_RENDERER
-    SkShader* shader = reinterpret_cast<SkShader *>(shaderHandle);
-    SkiaShader* shaderA = reinterpret_cast<SkiaShader *>(shaderAHandle);
-    SkiaShader* shaderB = reinterpret_cast<SkiaShader *>(shaderBHandle);
-    SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
-    SkXfermode::Mode skiaMode;
-    if (!SkXfermode::AsMode(mode, &skiaMode)) {
-        // TODO: Support other modes
-        skiaMode = SkXfermode::kSrcOver_Mode;
-    }
-    SkiaShader* skiaShader = new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
-    return reinterpret_cast<jlong>(skiaShader);
-#else
-    return NULL;
-#endif
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gColorMethods[] = {
@@ -518,41 +238,32 @@
 };
 
 static JNINativeMethod gShaderMethods[] = {
-    { "nativeDestructor",        "(JJ)V",    (void*)Shader_destructor        },
-    { "nativeSetLocalMatrix",    "(JJJ)V",   (void*)Shader_setLocalMatrix    }
+    { "nativeDestructor",        "(J)V",    (void*)Shader_destructor        },
+    { "nativeSetLocalMatrix",    "(JJ)V",   (void*)Shader_setLocalMatrix    }
 };
 
 static JNINativeMethod gBitmapShaderMethods[] = {
     { "nativeCreate",     "(JII)J",  (void*)BitmapShader_constructor },
-    { "nativePostCreate", "(JJII)J", (void*)BitmapShader_postConstructor }
 };
 
 static JNINativeMethod gLinearGradientMethods[] = {
     { "nativeCreate1",     "(FFFF[I[FI)J",  (void*)LinearGradient_create1     },
     { "nativeCreate2",     "(FFFFIII)J",    (void*)LinearGradient_create2     },
-    { "nativePostCreate1", "(JFFFF[I[FI)J", (void*)LinearGradient_postCreate1 },
-    { "nativePostCreate2", "(JFFFFIII)J",   (void*)LinearGradient_postCreate2 }
 };
 
 static JNINativeMethod gRadialGradientMethods[] = {
     { "nativeCreate1",     "(FFF[I[FI)J",  (void*)RadialGradient_create1     },
     { "nativeCreate2",     "(FFFIII)J",    (void*)RadialGradient_create2     },
-    { "nativePostCreate1", "(JFFF[I[FI)J", (void*)RadialGradient_postCreate1 },
-    { "nativePostCreate2", "(JFFFIII)J",   (void*)RadialGradient_postCreate2 }
 };
 
 static JNINativeMethod gSweepGradientMethods[] = {
     { "nativeCreate1",     "(FF[I[F)J",  (void*)SweepGradient_create1     },
     { "nativeCreate2",     "(FFII)J",    (void*)SweepGradient_create2     },
-    { "nativePostCreate1", "(JFF[I[F)J", (void*)SweepGradient_postCreate1 },
-    { "nativePostCreate2", "(JFFII)J",   (void*)SweepGradient_postCreate2 }
 };
 
 static JNINativeMethod gComposeShaderMethods[] = {
     { "nativeCreate1",      "(JJJ)J",   (void*)ComposeShader_create1     },
     { "nativeCreate2",      "(JJI)J",   (void*)ComposeShader_create2     },
-    { "nativePostCreate1",  "(JJJJ)J",  (void*)ComposeShader_postCreate1 },
-    { "nativePostCreate2",  "(JJJI)J",  (void*)ComposeShader_postCreate2 }
 };
 
 #include <android_runtime/AndroidRuntime.h>
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6d23c32..bc5e1b3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,8 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "NetdClient.h"
+#include "resolv_netid.h"
 #include <utils/misc.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
@@ -250,6 +252,36 @@
     }
 }
 
+static void android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+{
+    setNetworkForProcess(netId);
+}
+
+static void android_net_utils_unbindProcessToNetwork(JNIEnv *env, jobject thiz)
+{
+    setNetworkForProcess(NETID_UNSET);
+}
+
+static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
+{
+    return getNetworkForProcess();
+}
+
+static void android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, jint netId)
+{
+    setNetworkForResolv(netId);
+}
+
+static void android_net_utils_unbindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz)
+{
+    setNetworkForResolv(NETID_UNSET);
+}
+
+static void android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId)
+{
+    setNetworkForSocket(netId, socket);
+}
+
 // ----------------------------------------------------------------------------
 
 /*
@@ -267,6 +299,12 @@
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
     { "markSocket", "(II)V", (void*) android_net_utils_markSocket },
+    { "bindProcessToNetwork", "(I)V", (void*) android_net_utils_bindProcessToNetwork },
+    { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
+    { "unbindProcessToNetwork", "()V", (void*) android_net_utils_unbindProcessToNetwork },
+    { "bindProcessToNetworkForHostResolution", "(I)V", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
+    { "unbindProcessToNetworkForHostResolution", "()V", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
+    { "bindSocketToNetwork", "(II)V", (void*) android_net_utils_bindSocketToNetwork },
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 27d3f39..c5dd06f 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -45,7 +45,6 @@
 #include <DisplayListRenderer.h>
 #include <LayerRenderer.h>
 #include <OpenGLRenderer.h>
-#include <SkiaShader.h>
 #include <Stencil.h>
 #include <Rect.h>
 #include <RenderNode.h>
@@ -85,8 +84,6 @@
     #define RENDERER_LOGD(...)
 #endif
 
-#define MODIFIER_SHADER 2
-
 // ----------------------------------------------------------------------------
 
 static struct {
@@ -616,24 +613,6 @@
 }
 
 // ----------------------------------------------------------------------------
-// Shaders and color filters
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jint modifiers) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    if (modifiers & MODIFIER_SHADER) renderer->resetShader();
-}
-
-static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jlong shaderPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    SkiaShader* shader = reinterpret_cast<SkiaShader*>(shaderPtr);
-    renderer->setupShader(shader);
-}
-
-
-// ----------------------------------------------------------------------------
 // Draw filters
 // ----------------------------------------------------------------------------
 
@@ -1091,9 +1070,6 @@
     { "nDrawPath",          "(JJJ)V",          (void*) android_view_GLES20Canvas_drawPath },
     { "nDrawLines",         "(J[FIIJ)V",       (void*) android_view_GLES20Canvas_drawLines },
 
-    { "nResetModifiers",    "(JI)V",           (void*) android_view_GLES20Canvas_resetModifiers },
-    { "nSetupShader",       "(JJ)V",           (void*) android_view_GLES20Canvas_setupShader },
-
     { "nSetupPaintFilter",  "(JII)V",          (void*) android_view_GLES20Canvas_setupPaintFilter },
     { "nResetPaintFilter",  "(J)V",            (void*) android_view_GLES20Canvas_resetPaintFilter },
 
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 6ae02e0..a590dbf 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -211,8 +211,9 @@
     outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
             env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
 
-    uint64_t bits = env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits);
-    if (bits) {
+    BitSet64 bits =
+            BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
+    if (!bits.isEmpty()) {
         jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
                 gPointerCoordsClassInfo.mPackedAxisValues));
         if (valuesArray) {
@@ -221,11 +222,9 @@
 
             uint32_t index = 0;
             do {
-                uint32_t axis = __builtin_ctzll(bits);
-                uint64_t axisBit = 1LL << axis;
-                bits &= ~axisBit;
+                uint32_t axis = bits.clearFirstMarkedBit();
                 outRawPointerCoords->setAxisValue(axis, values[index++]);
-            } while (bits);
+            } while (!bits.isEmpty());
 
             env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
             env->DeleteLocalRef(valuesArray);
@@ -275,21 +274,19 @@
     env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
             rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
 
-    const uint64_t unpackedAxisBits = 0
-            | (1LL << AMOTION_EVENT_AXIS_X)
-            | (1LL << AMOTION_EVENT_AXIS_Y)
-            | (1LL << AMOTION_EVENT_AXIS_PRESSURE)
-            | (1LL << AMOTION_EVENT_AXIS_SIZE)
-            | (1LL << AMOTION_EVENT_AXIS_TOUCH_MAJOR)
-            | (1LL << AMOTION_EVENT_AXIS_TOUCH_MINOR)
-            | (1LL << AMOTION_EVENT_AXIS_TOOL_MAJOR)
-            | (1LL << AMOTION_EVENT_AXIS_TOOL_MINOR)
-            | (1LL << AMOTION_EVENT_AXIS_ORIENTATION);
-
     uint64_t outBits = 0;
-    uint64_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits;
-    if (remainingBits) {
-        uint32_t packedAxesCount = __builtin_popcountll(remainingBits);
+    BitSet64 bits = BitSet64(rawPointerCoords->bits);
+    bits.clearBit(AMOTION_EVENT_AXIS_X);
+    bits.clearBit(AMOTION_EVENT_AXIS_Y);
+    bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE);
+    bits.clearBit(AMOTION_EVENT_AXIS_SIZE);
+    bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+    bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+    bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+    bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
+    bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
+    if (!bits.isEmpty()) {
+        uint32_t packedAxesCount = bits.count();
         jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
                 outPointerCoordsObj);
         if (!outValuesArray) {
@@ -302,12 +299,10 @@
         const float* values = rawPointerCoords->values;
         uint32_t index = 0;
         do {
-            uint32_t axis = __builtin_ctzll(remainingBits);
-            uint64_t axisBit = 1LL << axis;
-            remainingBits &= ~axisBit;
-            outBits |= axisBit;
+            uint32_t axis = bits.clearFirstMarkedBit();
+            outBits |= BitSet64::valueForBit(axis);
             outValues[index++] = rawPointerCoords->getAxisValue(axis);
-        } while (remainingBits);
+        } while (!bits.isEmpty());
 
         env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0);
         env->DeleteLocalRef(outValuesArray);
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 8f83ab2..3180e58 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -19,13 +19,9 @@
 -->
 <resources>
     <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_width">200dp</dimen>
+    <dimen name="thumbnail_width">512dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_height">177dp</dimen>
-    <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="recents_thumbnail_width">512dp</dimen>
-    <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="recents_thumbnail_height">512dp</dimen>
+    <dimen name="thumbnail_height">512dp</dimen>
     <!-- The maximum number of action buttons that should be permitted within
          an action bar/action mode. This will be used to determine how many
          showAsAction="ifRoom" items can fit. "always" items can override this. -->
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 040bb5b..21235ec 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -35,13 +35,9 @@
     <item type="dimen" name="dialog_fixed_height_minor">90%</item>
 
     <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_width">230dp</dimen>
+    <dimen name="thumbnail_width">640dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_height">135dp</dimen>
-    <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="recents_thumbnail_width">512dp</dimen>
-    <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="recents_thumbnail_height">512dp</dimen>
+    <dimen name="thumbnail_height">640dp</dimen>
 
     <!-- Preference activity, vertical padding for the header list -->
     <dimen name="preference_screen_header_vertical_padding">32dp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c05dfca..9eb6f74 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4057,6 +4057,9 @@
         The default is one.
         See {@link android.widget.GridLayout.Spec}. -->
         <attr name="layout_rowSpan" format="integer" min="1" />
+        <!-- The relative proportion of horizontal space that should be allocated to this view
+        during excess space distribution. -->
+        <attr name="layout_rowWeight" format="float" />
         <!-- The column boundary delimiting the left of the group of cells
         occupied by this view. -->
         <attr name="layout_column" />
@@ -4065,6 +4068,9 @@
         The default is one.
         See {@link android.widget.GridLayout.Spec}. -->
         <attr name="layout_columnSpan" format="integer" min="1" />
+        <!-- The relative proportion of vertical space that should be allocated to this view
+        during excess space distribution. -->
+        <attr name="layout_columnWeight" format="float" />
         <!-- Gravity specifies how a component should be placed in its group of cells.
         The default is LEFT | BASELINE.
         See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 52b021f..38f00fe 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,13 +19,9 @@
 -->
 <resources>
     <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_width">164dp</dimen>
+    <dimen name="thumbnail_width">256dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="thumbnail_height">145dp</dimen>
-    <!-- The width that is used when creating thumbnails of applications. -->
-    <dimen name="recents_thumbnail_width">256dp</dimen>
-    <!-- The height that is used when creating thumbnails of applications. -->
-    <dimen name="recents_thumbnail_height">256dp</dimen>
+    <dimen name="thumbnail_height">256dp</dimen>
     <!-- The standard size (both width and height) of an application icon that
          will be displayed in the app launcher and elsewhere. -->
     <dimen name="app_icon_size">48dip</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ce0d2d5..42ed318 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2178,12 +2178,11 @@
   <public type="attr" name="contentInsetLeft" />
   <public type="attr" name="contentInsetRight" />
   <public type="attr" name="paddingMode" />
+  <public type="attr" name="layout_rowWeight" />
+  <public type="attr" name="layout_columnWeight" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
-  <public type="dimen" name="recents_thumbnail_height" />
-  <public type="dimen" name="recents_thumbnail_width" />
-
   <public-padding type="id" name="l_resource_pad" end="0x01020040" />
 
   <public type="id" name="mask" />
diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java
new file mode 100644
index 0000000..d4f6b1f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileBridgeTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.os;
+
+import android.os.FileBridge.FileBridgeOutputStream;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import libcore.io.Streams;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+public class FileBridgeTest extends AndroidTestCase {
+
+    private File file;
+    private FileOutputStream fileOs;
+    private FileBridge bridge;
+    private FileBridgeOutputStream client;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        file = getContext().getFileStreamPath("meow.dat");
+        file.delete();
+
+        fileOs = new FileOutputStream(file);
+
+        bridge = new FileBridge();
+        bridge.setTargetFile(fileOs.getFD());
+        bridge.start();
+        client = new FileBridgeOutputStream(bridge.getClientSocket());
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        fileOs.close();
+        file.delete();
+    }
+
+    private void assertOpen() throws Exception {
+        assertFalse("expected open", bridge.isClosed());
+    }
+
+    private void closeAndAssertClosed() throws Exception {
+        client.close();
+
+        // Wait a beat for things to settle down
+        SystemClock.sleep(200);
+        assertTrue("expected closed", bridge.isClosed());
+    }
+
+    private void assertContents(byte[] expected) throws Exception {
+        MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file)));
+    }
+
+    public void testNoWriteNoSync() throws Exception {
+        assertOpen();
+        closeAndAssertClosed();
+    }
+
+    public void testNoWriteSync() throws Exception {
+        assertOpen();
+        client.flush();
+        closeAndAssertClosed();
+    }
+
+    public void testWriteNoSync() throws Exception {
+        assertOpen();
+        client.write("meow".getBytes(StandardCharsets.UTF_8));
+        closeAndAssertClosed();
+        assertContents("meow".getBytes(StandardCharsets.UTF_8));
+    }
+
+    public void testWriteSync() throws Exception {
+        assertOpen();
+        client.write("cake".getBytes(StandardCharsets.UTF_8));
+        client.flush();
+        closeAndAssertClosed();
+        assertContents("cake".getBytes(StandardCharsets.UTF_8));
+    }
+
+    public void testWriteSyncWrite() throws Exception {
+        assertOpen();
+        client.write("meow".getBytes(StandardCharsets.UTF_8));
+        client.flush();
+        client.write("cake".getBytes(StandardCharsets.UTF_8));
+        closeAndAssertClosed();
+        assertContents("meowcake".getBytes(StandardCharsets.UTF_8));
+    }
+
+    public void testEmptyWrite() throws Exception {
+        assertOpen();
+        client.write(new byte[0]);
+        closeAndAssertClosed();
+        assertContents(new byte[0]);
+    }
+
+    public void testWriteAfterClose() throws Exception {
+        assertOpen();
+        client.write("meow".getBytes(StandardCharsets.UTF_8));
+        closeAndAssertClosed();
+        try {
+            client.write("cake".getBytes(StandardCharsets.UTF_8));
+            fail("wrote after close!");
+        } catch (IOException expected) {
+        }
+        assertContents("meow".getBytes(StandardCharsets.UTF_8));
+    }
+
+    public void testRandomWrite() throws Exception {
+        final Random r = new Random();
+        final ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+        for (int i = 0; i < 512; i++) {
+            final byte[] test = new byte[r.nextInt(24169)];
+            r.nextBytes(test);
+            result.write(test);
+            client.write(test);
+            client.flush();
+        }
+
+        closeAndAssertClosed();
+        assertContents(result.toByteArray());
+    }
+
+    public void testGiantWrite() throws Exception {
+        final byte[] test = new byte[263401];
+        new Random().nextBytes(test);
+
+        assertOpen();
+        client.write(test);
+        closeAndAssertClosed();
+        assertContents(test);
+    }
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
index 20fe465..7b83999 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java
@@ -31,7 +31,7 @@
  * Empty service for testing legacy multidex. Access more than 64k methods but some are required at
  * init, some only at verification and others during execution.
  */
-public abstract class AbstractService extends Service {
+public abstract class AbstractService extends Service implements Runnable {
     private final String TAG = "MultidexLegacyTestService" + getId();
 
     private int instanceFieldNotInited;
@@ -47,6 +47,12 @@
     @Override
     public void onCreate() {
         Log.i(TAG, "onCreate");
+        new Thread(this).start();
+
+    }
+
+    @Override
+    public void run() {
         Context applicationContext = getApplicationContext();
         File resultFile = new File(applicationContext.getFilesDir(), getId());
         try {
@@ -84,7 +90,6 @@
         } catch (IOException e) {
             e.printStackTrace();
         }
-
     }
 
     @Override
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
index 0f343b1..459bed5 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -25,8 +25,9 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
-import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -39,6 +40,7 @@
     private static final boolean DUMMY_FORCE_DEFAULT = false;
     private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
     private static final String SYSTEM_LOCALE = "en_US";
+    private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
 
     private static InputMethodSubtype createDummySubtype(final String locale) {
         final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
@@ -64,142 +66,141 @@
         si.exported = true;
         si.nonLocalizedLabel = imeLabel;
         ri.serviceInfo = si;
-        final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
-        for (String subtypeLocale : subtypeLocales) {
-            subtypes.add(createDummySubtype(subtypeLocale));
+        List<InputMethodSubtype> subtypes = null;
+        if (subtypeLocales != null) {
+            subtypes = new ArrayList<InputMethodSubtype>();
+            for (String subtypeLocale : subtypeLocales) {
+                subtypes.add(createDummySubtype(subtypeLocale));
+            }
         }
         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
                 DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
-        for (int i = 0; i < subtypes.size(); ++i) {
-            final String subtypeLocale = subtypeLocales.get(i);
-            items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
-                    SYSTEM_LOCALE));
+        if (subtypes == null) {
+            items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
+                    NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
+        } else {
+            for (int i = 0; i < subtypes.size(); ++i) {
+                final String subtypeLocale = subtypeLocales.get(i);
+                items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
+                        SYSTEM_LOCALE));
+            }
         }
     }
 
-    private static List<ImeSubtypeListItem> createTestData() {
+    private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
         final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
-        addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme",
-                Arrays.asList("en_US", "es_US", "fr"),
+        addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
                 true /* supportsSwitchingToNextInputMethod*/);
-        addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme",
+        addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
                 Arrays.asList("en_UK", "hi"),
                 false /* supportsSwitchingToNextInputMethod*/);
-        addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme",
-                Arrays.asList("ja_JP"),
+        addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
+                false /* supportsSwitchingToNextInputMethod*/);
+        addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
                 true /* supportsSwitchingToNextInputMethod*/);
-        addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme",
-                Arrays.asList("ja_JP"),
+        addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
+                Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
+        return items;
+    }
+
+    private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
+        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+        addDummyImeSubtypeListItems(items,
+                "UnknownIme", "UnknownIme",
+                Arrays.asList("en_US", "hi"),
+                true /* supportsSwitchingToNextInputMethod*/);
+        addDummyImeSubtypeListItems(items,
+                "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
+                Arrays.asList("en_US"),
+                false /* supportsSwitchingToNextInputMethod*/);
+        addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
+                "UnknownSubtypeUnawareIme", null,
                 false /* supportsSwitchingToNextInputMethod*/);
         return items;
     }
 
-    @SmallTest
-    public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception {
-        final List<ImeSubtypeListItem> imList = createTestData();
-
-        final boolean ONLY_CURRENT_IME = false;
-        ImeSubtypeListItem currentIme;
-        ImeSubtypeListItem nextIme;
-
-        // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
-        currentIme = imList.get(0);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(1), nextIme);
-        // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
-        currentIme = imList.get(1);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(2), nextIme);
-        // "switchAwareLatinIme/fr" -> "switchAwareJapaneseIme/ja_JP"
-        currentIme = imList.get(2);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(5), nextIme);
-        // "switchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US"
-        currentIme = imList.get(5);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(0), nextIme);
-
-        // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
-        currentIme = imList.get(3);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(4), nextIme);
-        // "nonSwitchAwareLatinIme/hi" -> "nonSwitchAwareJapaneseIme/ja_JP"
-        currentIme = imList.get(4);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(6), nextIme);
-        // "nonSwitchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareLatinIme/en_UK"
-        currentIme = imList.get(6);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(3), nextIme);
+    private void assertNextInputMethod(final ControllerImpl controller,
+            final boolean onlyCurrentIme,
+            final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
+        InputMethodSubtype subtype = null;
+        if (currentItem.mSubtypeName != null) {
+            subtype = createDummySubtype(currentItem.mSubtypeName.toString());
+        }
+        final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
+                currentItem.mImi, subtype);
+        assertEquals(nextItem, nextIme);
     }
 
     @SmallTest
-    public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception {
-        final List<ImeSubtypeListItem> imList = createTestData();
+    public void testControllerImpl() throws Exception {
+        final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
+        final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
+        final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
+        final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
+        final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);
 
-        final boolean ONLY_CURRENT_IME = true;
-        ImeSubtypeListItem currentIme;
-        ImeSubtypeListItem nextIme;
+        final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
+        final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+        final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
+        final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
+        final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
+        final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
+        final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
+        final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
 
-        // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
-        currentIme = imList.get(0);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(1), nextIme);
-        // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
-        currentIme = imList.get(1);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(2), nextIme);
-        // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US"
-        currentIme = imList.get(2);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(0), nextIme);
+        final ControllerImpl controller = new ControllerImpl(enabledItems);
 
-        // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
-        currentIme = imList.get(3);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(4), nextIme);
-        // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK"
-        currentIme = imList.get(4);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertEquals(imList.get(3), nextIme);
+        // switching-aware loop
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                latinIme_en_US, latinIme_fr);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                latinIme_fr, japaneseIme_ja_JP);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                japaneseIme_ja_JP, latinIme_en_US);
 
-        // "switchAwareJapaneseIme/ja_JP" -> null
-        currentIme = imList.get(5);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertNull(nextIme);
+        // switching-unaware loop
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                switchingUnawarelatinIme_hi, subtypeUnawareIme);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                subtypeUnawareIme, switchUnawareJapaneseIme_ja_JP);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                switchUnawareJapaneseIme_ja_JP, switchingUnawarelatinIme_en_UK);
 
-        // "nonSwitchAwareJapaneseIme/ja_JP" -> null
-        currentIme = imList.get(6);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
-                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
-                        currentIme.mSubtypeName.toString()));
-        assertNull(nextIme);
+        // test onlyCurrentIme == true
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                latinIme_en_US, latinIme_fr);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                latinIme_fr, latinIme_en_US);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                switchingUnawarelatinIme_hi, switchingUnawarelatinIme_en_UK);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                subtypeUnawareIme, null);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                japaneseIme_ja_JP, null);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                switchUnawareJapaneseIme_ja_JP, null);
+
+        // Make sure that disabled IMEs are not accepted.
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                disabledIme_en_US, null);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                disabledIme_hi, null);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                disabledSwitchingUnawareIme, null);
+        assertNextInputMethod(controller, false /* onlyCurrentIme */,
+                disabledSubtypeUnawareIme, null);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                disabledIme_en_US, null);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                disabledIme_hi, null);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                disabledSwitchingUnawareIme, null);
+        assertNextInputMethod(controller, true /* onlyCurrentIme */,
+                disabledSubtypeUnawareIme, null);
     }
  }
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 452c575..c6bccfe 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -105,8 +105,11 @@
 
 ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
 
+$(eval $(call create-font-symlink,Roboto-Black.ttf,Roboto-Bold.ttf))
 $(eval $(call create-font-symlink,Roboto-Light.ttf,Roboto-Regular.ttf))
 $(eval $(call create-font-symlink,Roboto-LightItalic.ttf,Roboto-Italic.ttf))
+$(eval $(call create-font-symlink,Roboto-Medium.ttf,Roboto-Regular.ttf))
+$(eval $(call create-font-symlink,Roboto-MediumItalic.ttf,Roboto-Italic.ttf))
 $(eval $(call create-font-symlink,Roboto-Thin.ttf,Roboto-Regular.ttf))
 $(eval $(call create-font-symlink,Roboto-ThinItalic.ttf,Roboto-Italic.ttf))
 $(eval $(call create-font-symlink,RobotoCondensed-Regular.ttf,Roboto-Regular.ttf))
@@ -116,8 +119,11 @@
 
 else # !MINIMAL_FONT
 font_src_files += \
+    Roboto-Black.ttf \
     Roboto-Light.ttf \
     Roboto-LightItalic.ttf \
+    Roboto-Medium.ttf \
+    Roboto-MediumItalic.ttf \
     Roboto-Thin.ttf \
     Roboto-ThinItalic.ttf \
     RobotoCondensed-Regular.ttf \
diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf
new file mode 100644
index 0000000..18cf2ae
--- /dev/null
+++ b/data/fonts/Roboto-Black.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index c5b9c67..7e43dd2 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index 0320214..b8f3165 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 38ba570..b84e93c 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index 271606b..46c6fbc 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 17ef355..e11331d 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf
new file mode 100644
index 0000000..433bca4
--- /dev/null
+++ b/data/fonts/Roboto-Medium.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
new file mode 100644
index 0000000..7166eb7
--- /dev/null
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index 7469063..7180d51f 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 74efe4d..9792ed1 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index f08ea51..5366fe5 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 1252d00..fe52e7d 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index e914a07..2d29ffb 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index 8a570cf..1d7eb08 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf
index 41d212a..1209024 100644
--- a/data/fonts/RobotoCondensed-Light.ttf
+++ b/data/fonts/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf
old mode 100755
new mode 100644
index dd54971..ed1ccea
--- a/data/fonts/RobotoCondensed-LightItalic.ttf
+++ b/data/fonts/RobotoCondensed-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index a16b9cb..4a9a6f9 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 2312a04..e5573bb 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -24,8 +24,11 @@
     Roboto-Bold.ttf \
     Roboto-Italic.ttf \
     Roboto-BoldItalic.ttf \
+    Roboto-Black.ttf \
     Roboto-Light.ttf \
     Roboto-LightItalic.ttf \
+    Roboto-Medium.ttf \
+    Roboto-MediumItalic.ttf \
     Roboto-Thin.ttf \
     Roboto-ThinItalic.ttf \
     RobotoCondensed-Regular.ttf \
diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml
index 97b7fd8..646b33b 100644
--- a/data/fonts/system_fonts.xml
+++ b/data/fonts/system_fonts.xml
@@ -68,6 +68,25 @@
 
     <family>
         <nameset>
+            <name>sans-serif-medium</name>
+        </nameset>
+        <fileset>
+            <file>Roboto-Medium.ttf</file>
+            <file>Roboto-MediumItalic.ttf</file>
+        </fileset>
+    </family>
+
+    <family>
+        <nameset>
+            <name>sans-serif-black</name>
+        </nameset>
+        <fileset>
+            <file>Roboto-Black.ttf</file>
+        </fileset>
+    </family>
+
+    <family>
+        <nameset>
             <name>sans-serif-condensed-light</name>
         </nameset>
         <fileset>
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index b7673d8..3ab57c1 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -44,7 +44,6 @@
         mTileY = tileY;
         final long b = bitmap.ni();
         native_instance = nativeCreate(b, tileX.nativeInt, tileY.nativeInt);
-        native_shader = nativePostCreate(native_instance, b, tileX.nativeInt, tileY.nativeInt);
     }
 
     /**
@@ -59,6 +58,4 @@
 
     private static native long nativeCreate(long native_bitmap, int shaderTileModeX,
             int shaderTileModeY);
-    private static native long nativePostCreate(long native_shader, long native_bitmap,
-            int shaderTileModeX, int shaderTileModeY);
 }
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 5109ffd..d7b2071 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -55,14 +55,6 @@
         mXferMode = mode;
         native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
                 (mode != null) ? mode.native_instance : 0);
-        if (mode instanceof PorterDuffXfermode) {
-            PorterDuff.Mode pdMode = ((PorterDuffXfermode) mode).mode;
-            native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
-                    shaderB.native_shader, pdMode != null ? pdMode.nativeInt : 0);
-        } else {
-            native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
-                    shaderB.native_shader, mode != null ? mode.native_instance : 0);
-        }
     }
 
     /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -79,8 +71,6 @@
         mPorterDuffMode = mode;
         native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance,
                 mode.nativeInt);
-        native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
-                shaderB.native_shader, mode.nativeInt);
     }
 
     /**
@@ -108,8 +98,4 @@
             long native_mode);
     private static native long nativeCreate2(long native_shaderA, long native_shaderB,
             int porterDuffMode);
-    private static native long nativePostCreate1(long native_shader, long native_skiaShaderA,
-            long native_skiaShaderB, long native_mode);
-    private static native long nativePostCreate2(long native_shader, long native_skiaShaderA,
-            long native_skiaShaderB, int porterDuffMode);
 }
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 0eae67c..90cb217 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -66,8 +66,6 @@
         mPositions = positions;
         mTileMode = tile;
         native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt);
-        native_shader = nativePostCreate1(native_instance, x0, y0, x1, y1, colors, positions,
-                tile.nativeInt);
     }
 
     /** Create a shader that draws a linear gradient along a line.
@@ -90,8 +88,6 @@
         mColor1 = color1;
         mTileMode = tile;
         native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
-        native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1,
-                tile.nativeInt);
     }
 
     /**
@@ -120,8 +116,4 @@
             int colors[], float positions[], int tileMode);
     private native long nativeCreate2(float x0, float y0, float x1, float y1,
             int color0, int color1, int tileMode);
-    private native long nativePostCreate1(long native_shader, float x0, float y0, float x1, float y1,
-            int colors[], float positions[], int tileMode);
-    private native long nativePostCreate2(long native_shader, float x0, float y0, float x1, float y1,
-            int color0, int color1, int tileMode);
 }
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index c00c612..75c951a 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -66,8 +66,6 @@
         mPositions = positions;
         mTileMode = tile;
         native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
-        native_shader = nativePostCreate1(native_instance, x, y, radius, colors, positions,
-                tile.nativeInt);
     }
 
     /** Create a shader that draws a radial gradient given the center and radius.
@@ -91,8 +89,6 @@
         mColor1 = color1;
         mTileMode = tile;
         native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
-        native_shader = nativePostCreate2(native_instance, x, y, radius, color0, color1,
-                tile.nativeInt);
     }
 
     /**
@@ -121,10 +117,5 @@
             int colors[], float positions[], int tileMode);
     private static native long nativeCreate2(float x, float y, float radius,
             int color0, int color1, int tileMode);
-
-    private static native long nativePostCreate1(long native_shader, float x, float y, float radius,
-            int colors[], float positions[], int tileMode);
-    private static native long nativePostCreate2(long native_shader, float x, float y, float radius,
-            int color0, int color1, int tileMode);
 }
 
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 94b4c4a..6870ab4 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -29,10 +29,6 @@
      * @hide 
      */
     public long native_instance;
-    /**
-     * @hide
-     */
-    public long native_shader;
 
     private Matrix mLocalMatrix;
 
@@ -78,7 +74,7 @@
      */
     public void setLocalMatrix(Matrix localM) {
         mLocalMatrix = localM;
-        nativeSetLocalMatrix(native_instance, native_shader,
+        nativeSetLocalMatrix(native_instance,
                 localM == null ? 0 : localM.native_instance);
     }
 
@@ -86,7 +82,7 @@
         try {
             super.finalize();
         } finally {
-            nativeDestructor(native_instance, native_shader);
+            nativeDestructor(native_instance);
         }
     }
 
@@ -112,7 +108,7 @@
         }
     }
 
-    private static native void nativeDestructor(long native_shader, long native_skiaShader);
+    private static native void nativeDestructor(long native_shader);
     private static native void nativeSetLocalMatrix(long native_shader,
-            long native_skiaShader, long matrix_instance);
+            long matrix_instance);
 }
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 21239f7..18a748f 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -63,7 +63,6 @@
         mColors = colors;
         mPositions = positions;
         native_instance = nativeCreate1(cx, cy, colors, positions);
-        native_shader = nativePostCreate1(native_instance, cx, cy, colors, positions);
     }
 
     /**
@@ -81,7 +80,6 @@
         mColor0 = color0;
         mColor1 = color1;
         native_instance = nativeCreate2(cx, cy, color0, color1);
-        native_shader = nativePostCreate2(native_instance, cx, cy, color0, color1);
     }
 
     /**
@@ -108,10 +106,5 @@
 
     private static native long nativeCreate1(float x, float y, int colors[], float positions[]);
     private static native long nativeCreate2(float x, float y, int color0, int color1);
-
-    private static native long nativePostCreate1(long native_shader, float cx, float cy,
-            int[] colors, float[] positions);    
-    private static native long nativePostCreate2(long native_shader, float cx, float cy,
-            int color0, int color1);
 }
 
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 2e2ee15..5367663 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -33,6 +33,7 @@
 #include "thread/TaskManager.h"
 
 #include "AssetAtlas.h"
+#include "Extensions.h"
 #include "FontRenderer.h"
 #include "GammaFontRenderer.h"
 #include "TextureCache.h"
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 3016814..937bf8d 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -230,6 +230,11 @@
             return false;
         }
 
+        if (op->mPaint && mOps[0].op->mPaint &&
+            op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) {
+            return false;
+        }
+
         /* Draw Modifiers compatibility check
          *
          * Shadows are ignored, as only text uses them, and in that case they are drawn
@@ -244,7 +249,6 @@
          */
         const DrawModifiers& lhsMod = lhs->mDrawModifiers;
         const DrawModifiers& rhsMod = rhs->mDrawModifiers;
-        if (lhsMod.mShader != rhsMod.mShader) return false;
 
         // Draw filter testing expects bit fields to be clear if filter not set.
         if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index dac86cb..96c6292 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -58,11 +58,6 @@
         caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
     }
 
-    for (size_t i = 0; i < shaders.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(shaders.itemAt(i));
-        caches.resourceCache.destructorLocked(shaders.itemAt(i));
-    }
-
     for (size_t i = 0; i < sourcePaths.size(); i++) {
         caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
     }
@@ -92,7 +87,6 @@
     bitmapResources.clear();
     ownedBitmapResources.clear();
     patchResources.clear();
-    shaders.clear();
     sourcePaths.clear();
     paints.clear();
     regions.clear();
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b2ead5b..11e78b0 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -56,7 +56,6 @@
 class OpenGLRenderer;
 class Rect;
 class Layer;
-class SkiaShader;
 
 class ClipRectOp;
 class SaveLayerOp;
@@ -127,7 +126,6 @@
     SortedVector<const SkPath*> sourcePaths;
     Vector<const SkRegion*> regions;
     Vector<const SkMatrix*> matrices;
-    Vector<SkiaShader*> shaders;
     Vector<Layer*> layers;
     uint32_t functorCount;
     bool hasDrawOps;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index e4867220..ea3e7a8 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -208,9 +208,16 @@
         if (!state.mMatrix.isSimple()) return false;
 
         // check state/paint for transparency
-        if (state.mDrawModifiers.mShader ||
-                state.mAlpha != 1.0f ||
-                (mPaint && mPaint->getAlpha() != 0xFF)) return false;
+        if (mPaint) {
+            if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
+                return false;
+            }
+            if (mPaint->getAlpha() != 0xFF) {
+                return false;
+            }
+        }
+
+        if (state.mAlpha != 1.0f) return false;
 
         SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
         return (mode == SkXfermode::kSrcOver_Mode ||
@@ -592,37 +599,6 @@
     const SkRegion* mRegion;
 };
 
-class ResetShaderOp : public StateOp {
-public:
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
-        renderer.resetShader();
-    }
-
-    virtual void output(int level, uint32_t logFlags) const {
-        OP_LOGS("ResetShader");
-    }
-
-    virtual const char* name() { return "ResetShader"; }
-};
-
-class SetupShaderOp : public StateOp {
-public:
-    SetupShaderOp(SkiaShader* shader)
-            : mShader(shader) {}
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
-        renderer.setupShader(mShader);
-    }
-
-    virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("SetupShader, shader %p", mShader);
-    }
-
-    virtual const char* name() { return "SetupShader"; }
-
-private:
-    SkiaShader* mShader;
-};
-
 class ResetPaintFilterOp : public StateOp {
 public:
     virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 606c67e..229afdf 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -47,7 +47,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 DisplayListData* DisplayListRenderer::finishRecording() {
-    mShaderMap.clear();
     mPaintMap.clear();
     mRegionMap.clear();
     mPathMap.clear();
@@ -394,15 +393,6 @@
     return DrawGlInfo::kStatusDone;
 }
 
-void DisplayListRenderer::resetShader() {
-    addStateOp(new (alloc()) ResetShaderOp());
-}
-
-void DisplayListRenderer::setupShader(SkiaShader* shader) {
-    shader = refShader(shader);
-    addStateOp(new (alloc()) SetupShaderOp(shader));
-}
-
 void DisplayListRenderer::resetPaintFilter() {
     addStateOp(new (alloc()) ResetPaintFilterOp());
 }
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index d814111..f0ae00f 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -95,9 +95,6 @@
     virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
 
     // Misc - should be implemented with SkPaint inspection
-    virtual void resetShader();
-    virtual void setupShader(SkiaShader* shader);
-
     virtual void resetPaintFilter();
     virtual void setupPaintFilter(int clearBits, int setBits);
 
@@ -269,21 +266,6 @@
         return bitmap;
     }
 
-    inline SkiaShader* refShader(SkiaShader* shader) {
-        if (!shader) return NULL;
-
-        SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
-        // TODO: We also need to handle generation ID changes in compose shaders
-        if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) {
-            shaderCopy = shader->copy();
-            // replaceValueFor() performs an add if the entry doesn't exist
-            mShaderMap.replaceValueFor(shader, shaderCopy);
-            mDisplayListData->shaders.add(shaderCopy);
-            mCaches.resourceCache.incrementRefcount(shaderCopy);
-        }
-        return shaderCopy;
-    }
-
     inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
         mDisplayListData->patchResources.add(patch);
         mCaches.resourceCache.incrementRefcount(patch);
@@ -293,7 +275,6 @@
     DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap;
     DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
     DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
-    DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
 
     Caches& mCaches;
     DisplayListData* mDisplayListData;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 647c281..4407ab0 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -73,7 +73,7 @@
         }
     }
     renderer->setupDrawColorFilter(paint->getColorFilter());
-    renderer->setupDrawShader();
+    renderer->setupDrawShader(paint->getShader());
     renderer->setupDrawBlending(paint);
     renderer->setupDrawProgram();
     renderer->setupDrawModelView(kModelViewMode_Translate, false,
@@ -85,7 +85,7 @@
     renderer->setupDrawTexture(0);
     renderer->setupDrawPureColorUniforms();
     renderer->setupDrawColorFilterUniforms(paint->getColorFilter());
-    renderer->setupDrawShaderUniforms(pureTranslate);
+    renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate);
     renderer->setupDrawTextGammaUniforms();
 
     return NO_ERROR;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 691f1c9..71836dd 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <SkCanvas.h>
+#include <SkShader.h>
 #include <SkTypeface.h>
 
 #include <utils/Log.h>
@@ -37,6 +38,7 @@
 #include "PathTessellator.h"
 #include "Properties.h"
 #include "ShadowTessellator.h"
+#include "SkiaShader.h"
 #include "utils/GLUtils.h"
 #include "Vector.h"
 #include "VertexBuffer.h"
@@ -1053,6 +1055,45 @@
 
 #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
 
+// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
+// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
+class LayerShader : public SkShader {
+public:
+    LayerShader(Layer* layer, const SkMatrix* localMatrix)
+    : INHERITED(localMatrix)
+    , mLayer(layer) {
+    }
+
+    virtual bool asACustomShader(void** data) const {
+        if (data) {
+            *data = static_cast<void*>(mLayer);
+        }
+        return true;
+    }
+
+    virtual bool isOpaque() const {
+        return !mLayer->isBlend();
+    }
+
+protected:
+    virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
+        LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
+    }
+
+    virtual void flatten(SkWriteBuffer&) const {
+        LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
+    }
+
+    virtual Factory getFactory() const {
+        LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
+        return NULL;
+    }
+private:
+    // Unowned.
+    Layer* mLayer;
+    typedef SkShader INHERITED;
+};
+
 void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
     if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
 
@@ -1066,21 +1107,19 @@
         paint.setAntiAlias(true);
         paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
 
-        SkiaShader* oldShader = mDrawModifiers.mShader;
-
         // create LayerShader to map SaveLayer content into subsequent draw
         SkMatrix shaderMatrix;
         shaderMatrix.setTranslate(rect.left, rect.bottom);
         shaderMatrix.preScale(1, -1);
-        SkiaLayerShader layerShader(layer, &shaderMatrix);
-        mDrawModifiers.mShader = &layerShader;
+        LayerShader layerShader(layer, &shaderMatrix);
+        paint.setShader(&layerShader);
 
         // Since the drawing primitive is defined in local drawing space,
         // we don't need to modify the draw matrix
         const SkPath* maskPath = layer->getConvexMask();
         DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
 
-        mDrawModifiers.mShader = oldShader;
+        paint.setShader(NULL);
         restore();
 
         return;
@@ -1627,9 +1666,9 @@
     mSetShaderColor = mDescription.setColorModulate(a);
 }
 
-void OpenGLRenderer::setupDrawShader() {
-    if (mDrawModifiers.mShader) {
-        mDrawModifiers.mShader->describe(mDescription, mExtensions);
+void OpenGLRenderer::setupDrawShader(const SkShader* shader) {
+    if (shader != NULL) {
+        SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader);
     }
 }
 
@@ -1655,15 +1694,21 @@
     }
 }
 
+static bool isBlendedColorFilter(const SkColorFilter* filter) {
+    if (filter == NULL) {
+        return false;
+    }
+    return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+}
+
 void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) {
     SkXfermode::Mode mode = layer->getMode();
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
+    // TODO: check shader blending, once we have shader drawing support for layers.
     bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f ||
-            (mColorSet && mColorA < 1.0f) ||
-            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
-            layer->getColorFilter();
+            (mColorSet && mColorA < 1.0f) || isBlendedColorFilter(layer->getColorFilter());
     chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
@@ -1673,8 +1718,8 @@
     // argb=1,0,0,0
     accountForClear(mode);
     blend |= (mColorSet && mColorA < 1.0f) ||
-            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
-            (paint && paint->getColorFilter());
+            (getShader(paint) && !getShader(paint)->isOpaque()) ||
+            isBlendedColorFilter(getColorFilter(paint));
     chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
@@ -1717,8 +1762,8 @@
     }
 }
 
-void OpenGLRenderer::setupDrawColorUniforms() {
-    if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
+void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
+    if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
         mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
@@ -1729,20 +1774,22 @@
     }
 }
 
-void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
-    if (mDrawModifiers.mShader) {
-        if (ignoreTransform) {
-            // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
-            // because it was built into modelView / the geometry, and the SkiaShader needs to
-            // compensate.
-            mat4 modelViewWithoutTransform;
-            modelViewWithoutTransform.loadInverse(*currentTransform());
-            modelViewWithoutTransform.multiply(mModelViewMatrix);
-            mModelViewMatrix.load(modelViewWithoutTransform);
-        }
-        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
-                mModelViewMatrix, *mSnapshot, &mTextureUnit);
+void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform) {
+    if (shader == NULL) {
+        return;
     }
+
+    if (ignoreTransform) {
+        // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
+        // because it was built into modelView / the geometry, and the description needs to
+        // compensate.
+        mat4 modelViewWithoutTransform;
+        modelViewWithoutTransform.loadInverse(*currentTransform());
+        modelViewWithoutTransform.multiply(mModelViewMatrix);
+        mModelViewMatrix.load(modelViewWithoutTransform);
+    }
+
+    SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader);
 }
 
 void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) {
@@ -2201,7 +2248,7 @@
     // Apply a scale transform on the canvas only when a shader is in use
     // Skia handles the ratio between the dst and src rects as a scale factor
     // when a shader is set
-    bool useScaleTransform = mDrawModifiers.mShader && scaled;
+    bool useScaleTransform = getShader(paint) && scaled;
     bool ignoreTransform = false;
 
     if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) {
@@ -2359,13 +2406,13 @@
     if (isAA) setupDrawAA();
     setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, isAA);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
-    setupDrawColorUniforms();
+    setupDrawColorUniforms(getShader(paint));
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms();
+    setupDrawShaderUniforms(getShader(paint));
 
     const void* vertices = vertexBuffer.getBuffer();
     bool force = mCaches.unbindMeshBuffer();
@@ -2670,7 +2717,7 @@
     const float sy = y - shadow->top + textShadow.dy;
 
     const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha;
-    if (mDrawModifiers.mShader) {
+    if (getShader(paint)) {
         textShadow.color = SK_ColorWHITE;
     }
 
@@ -2678,7 +2725,7 @@
     setupDrawWithTexture(true);
     setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha);
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, true);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_TranslateAndScale, false,
@@ -2686,7 +2733,7 @@
     setupDrawTexture(shadow->id);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms();
+    setupDrawShaderUniforms(getShader(paint));
     setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3008,21 +3055,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Shaders
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetShader() {
-    mDrawModifiers.mShader = NULL;
-}
-
-void OpenGLRenderer::setupShader(SkiaShader* shader) {
-    mDrawModifiers.mShader = shader;
-    if (mDrawModifiers.mShader) {
-        mDrawModifiers.mShader->setCaches(mCaches);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // Draw filters
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -3080,7 +3112,7 @@
     setupDrawWithTexture(true);
     setupDrawAlpha8Color(paint->getColor(), alpha);
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, true);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_TranslateAndScale, false,
@@ -3088,7 +3120,7 @@
     setupDrawTexture(texture->id);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms();
+    setupDrawShaderUniforms(getShader(paint));
     setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3254,7 +3286,7 @@
 
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
-    if (mDrawModifiers.mShader) {
+    if (getShader(paint)) {
         color |= 0x00ffffff;
     }
 
@@ -3290,15 +3322,15 @@
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawColorFilter(getColorFilter(paint));
     setupDrawBlending(paint);
     setupDrawProgram();
     setupDrawDirtyRegionsDisabled();
     setupDrawModelView(kModelViewMode_Translate, false,
             0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform);
-    setupDrawColorUniforms();
-    setupDrawShaderUniforms();
+    setupDrawColorUniforms(getShader(paint));
+    setupDrawShaderUniforms(getShader(paint));
     setupDrawColorFilterUniforms(getColorFilter(paint));
 
     if (dirty && hasLayer()) {
@@ -3314,21 +3346,21 @@
         const SkPaint* paint, bool ignoreTransform) {
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
-    if (mDrawModifiers.mShader) {
+    if (getShader(paint)) {
         color |= 0x00ffffff;
     }
 
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawColorFilter(getColorFilter(paint));
     setupDrawBlending(paint);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_TranslateAndScale, false,
             left, top, right, bottom, ignoreTransform);
-    setupDrawColorUniforms();
-    setupDrawShaderUniforms(ignoreTransform);
+    setupDrawColorUniforms(getShader(paint));
+    setupDrawShaderUniforms(getShader(paint), ignoreTransform);
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawSimpleMesh();
 
@@ -3441,7 +3473,7 @@
         setupDrawAlpha8Color(color, alpha);
     }
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, true);
     setupDrawProgram();
     if (!dirty) setupDrawDirtyRegionsDisabled();
@@ -3449,7 +3481,7 @@
     setupDrawTexture(texture);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms(ignoreTransform);
+    setupDrawShaderUniforms(getShader(paint), ignoreTransform);
     setupDrawMesh(vertices, texCoords);
 
     glDrawArrays(drawMode, 0, elementsCount);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index c6d9071..fc27947 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -26,7 +26,6 @@
 #include <SkMatrix.h>
 #include <SkPaint.h>
 #include <SkRegion.h>
-#include <SkShader.h>
 #include <SkXfermode.h>
 
 #include <utils/Blur.h>
@@ -45,13 +44,15 @@
 #include "Program.h"
 #include "Rect.h"
 #include "Renderer.h"
-#include "StatefulBaseRenderer.h"
 #include "Snapshot.h"
+#include "StatefulBaseRenderer.h"
 #include "UvMapper.h"
 #include "Vertex.h"
 #include "Caches.h"
 #include "CanvasProperty.h"
 
+class SkShader;
+
 namespace android {
 namespace uirenderer {
 
@@ -59,7 +60,6 @@
 class RenderNode;
 class TextSetupFunctor;
 class VertexBuffer;
-class SkiaShader;
 
 struct DrawModifiers {
     DrawModifiers() {
@@ -70,7 +70,6 @@
         memset(this, 0, sizeof(DrawModifiers));
     }
 
-    SkiaShader* mShader;
     float mOverrideLayerAlpha;
 
     // Draw filters
@@ -217,9 +216,6 @@
     status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
             float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter);
 
-    virtual void resetShader();
-    virtual void setupShader(SkiaShader* shader);
-
     virtual void resetPaintFilter();
     virtual void setupPaintFilter(int clearBits, int setBits);
 
@@ -467,6 +463,14 @@
     }
 
     /**
+     * Safely retrieves the Shader from the given Paint. If the paint is
+     * null then null is returned.
+     */
+    static inline const SkShader* getShader(const SkPaint* paint) {
+        return paint ? paint->getShader() : NULL;
+    }
+
+    /**
      * Set to true to suppress error checks at the end of a frame.
      */
     virtual bool suppressErrorChecks() const {
@@ -838,7 +842,7 @@
     void setupDrawColor(float r, float g, float b, float a);
     void setupDrawAlpha8Color(int color, int alpha);
     void setupDrawTextGamma(const SkPaint* paint);
-    void setupDrawShader();
+    void setupDrawShader(const SkShader* shader);
     void setupDrawColorFilter(const SkColorFilter* filter);
     void setupDrawBlending(const Layer* layer, bool swapSrcDst = false);
     void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false);
@@ -862,9 +866,17 @@
      */
     void setupDrawModelView(ModelViewMode mode, bool offset,
             float left, float top, float right, float bottom, bool ignoreTransform = false);
-    void setupDrawColorUniforms();
+    void setupDrawColorUniforms(bool hasShader);
     void setupDrawPureColorUniforms();
-    void setupDrawShaderUniforms(bool ignoreTransform = false);
+
+    /**
+     * Setup uniforms for the current shader.
+     *
+     * @param shader SkShader on the current paint.
+     *
+     * @param ignoreTransform Set to true to ignore the transform in shader.
+     */
+    void setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform = false);
     void setupDrawColorFilterUniforms(const SkColorFilter* paint);
     void setupDrawSimpleMesh();
     void setupDrawTexture(GLuint texture);
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index e191a26..23cab0e 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -35,7 +35,6 @@
 class Layer;
 class Matrix4;
 class SkiaColorFilter;
-class SkiaShader;
 class Patch;
 
 enum DrawOpMode {
@@ -183,9 +182,6 @@
     virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
 
     // Misc - should be implemented with SkPaint inspection
-    virtual void resetShader() = 0;
-    virtual void setupShader(SkiaShader* shader) = 0;
-
     virtual void resetPaintFilter() = 0;
     virtual void setupPaintFilter(int clearBits, int setBits) = 0;
 
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 13a3e8e..8b553d1 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -71,11 +71,6 @@
     incrementRefcount((void*) pathResource, kPath);
 }
 
-void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
-    SkSafeRef(shaderResource->getSkShader());
-    incrementRefcount((void*) shaderResource, kShader);
-}
-
 void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
     incrementRefcount((void*) patchResource, kNinePatch);
 }
@@ -104,11 +99,6 @@
     incrementRefcountLocked((void*) pathResource, kPath);
 }
 
-void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) {
-    SkSafeRef(shaderResource->getSkShader());
-    incrementRefcountLocked((void*) shaderResource, kShader);
-}
-
 void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) {
     incrementRefcountLocked((void*) patchResource, kNinePatch);
 }
@@ -132,11 +122,6 @@
     decrementRefcount((void*) pathResource);
 }
 
-void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
-    SkSafeUnref(shaderResource->getSkShader());
-    decrementRefcount((void*) shaderResource);
-}
-
 void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
     decrementRefcount((void*) patchResource);
 }
@@ -168,11 +153,6 @@
     decrementRefcountLocked((void*) pathResource);
 }
 
-void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) {
-    SkSafeUnref(shaderResource->getSkShader());
-    decrementRefcountLocked((void*) shaderResource);
-}
-
 void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
     decrementRefcountLocked((void*) patchResource);
 }
@@ -227,25 +207,6 @@
     }
 }
 
-void ResourceCache::destructor(SkiaShader* resource) {
-    Mutex::Autolock _l(mLock);
-    destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(SkiaShader* resource) {
-    ssize_t index = mCache->indexOfKey(resource);
-    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
-    if (ref == NULL) {
-        // If we're not tracking this resource, just delete it
-        delete resource;
-        return;
-    }
-    ref->destroyed = true;
-    if (ref->refCount == 0) {
-        deleteResourceReferenceLocked(resource, ref);
-    }
-}
-
 void ResourceCache::destructor(Res_png_9patch* resource) {
     Mutex::Autolock _l(mLock);
     destructorLocked(resource);
@@ -333,11 +294,6 @@
                 }
             }
             break;
-            case kShader: {
-                SkiaShader* shader = (SkiaShader*) resource;
-                delete shader;
-            }
-            break;
             case kNinePatch: {
                 if (Caches::hasInstance()) {
                     Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 4097ba4..3864d4b 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -20,7 +20,6 @@
 #include <cutils/compiler.h>
 
 #include <SkBitmap.h>
-#include <SkiaShader.h>
 
 #include <utils/KeyedVector.h>
 
@@ -36,7 +35,6 @@
  */
 enum ResourceType {
     kBitmap,
-    kShader,
     kNinePatch,
     kPath,
     kLayer
@@ -70,36 +68,30 @@
 
     void incrementRefcount(const SkPath* resource);
     void incrementRefcount(const SkBitmap* resource);
-    void incrementRefcount(SkiaShader* resource);
     void incrementRefcount(const Res_png_9patch* resource);
     void incrementRefcount(Layer* resource);
 
     void incrementRefcountLocked(const SkPath* resource);
     void incrementRefcountLocked(const SkBitmap* resource);
-    void incrementRefcountLocked(SkiaShader* resource);
     void incrementRefcountLocked(const Res_png_9patch* resource);
     void incrementRefcountLocked(Layer* resource);
 
     void decrementRefcount(const SkBitmap* resource);
     void decrementRefcount(const SkPath* resource);
-    void decrementRefcount(SkiaShader* resource);
     void decrementRefcount(const Res_png_9patch* resource);
     void decrementRefcount(Layer* resource);
 
     void decrementRefcountLocked(const SkBitmap* resource);
     void decrementRefcountLocked(const SkPath* resource);
-    void decrementRefcountLocked(SkiaShader* resource);
     void decrementRefcountLocked(const Res_png_9patch* resource);
     void decrementRefcountLocked(Layer* resource);
 
     void destructor(SkPath* resource);
     void destructor(const SkBitmap* resource);
-    void destructor(SkiaShader* resource);
     void destructor(Res_png_9patch* resource);
 
     void destructorLocked(SkPath* resource);
     void destructorLocked(const SkBitmap* resource);
-    void destructorLocked(SkiaShader* resource);
     void destructorLocked(Res_png_9patch* resource);
 
     bool recycle(SkBitmap* resource);
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6a4a0c8..c672bc4 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -21,9 +21,10 @@
 #include <SkMatrix.h>
 
 #include "Caches.h"
+#include "Layer.h"
+#include "Matrix.h"
 #include "SkiaShader.h"
 #include "Texture.h"
-#include "Matrix.h"
 
 namespace android {
 namespace uirenderer {
@@ -54,89 +55,142 @@
             a);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
-
-void SkiaShader::copyFrom(const SkiaShader& shader) {
-    mType = shader.mType;
-    mKey = shader.mKey;
-    mTileX = shader.mTileX;
-    mTileY = shader.mTileY;
-    mBlend = shader.mBlend;
-    mUnitMatrix = shader.mUnitMatrix;
-    mShaderMatrix = shader.mShaderMatrix;
-    mGenerationId = shader.mGenerationId;
-}
-
-SkiaShader::SkiaShader(): mCaches(NULL) {
-}
-
-SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
-        SkShader::TileMode tileY, const SkMatrix* matrix, bool blend):
-        mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
-        mCaches(NULL) {
-    setMatrix(matrix);
-    mGenerationId = 0;
-}
-
-SkiaShader::~SkiaShader() {
-}
-
-void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
-}
-
-void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-        GLuint* textureUnit) {
-}
-
-void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
-    mCaches->bindTexture(texture->id);
+static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
+    caches->bindTexture(texture->id);
     texture->setWrapST(wrapS, wrapT);
 }
 
-void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
-    screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
-    screenSpace.multiply(modelView);
+/**
+ * Compute the matrix to transform to screen space.
+ * @param screenSpace Output param for the computed matrix.
+ * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
+ *      or identity.
+ * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
+ * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
+ */
+static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
+        const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
+    mat4 shaderMatrix;
+    // uses implicit construction
+    shaderMatrix.loadInverse(localMatrix);
+    // again, uses implicit construction
+    screenSpace.loadMultiply(unitMatrix, shaderMatrix);
+    screenSpace.multiply(modelViewMatrix);
+}
+
+// Returns true if one is a bitmap and the other is a gradient
+static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
+    return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
+            || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
+}
+
+SkiaShaderType SkiaShader::getType(const SkShader& shader) {
+    // First check for a gradient shader.
+    switch (shader.asAGradient(NULL)) {
+        case SkShader::kNone_GradientType:
+            // Not a gradient shader. Fall through to check for other types.
+            break;
+        case SkShader::kLinear_GradientType:
+        case SkShader::kRadial_GradientType:
+        case SkShader::kSweep_GradientType:
+            return kGradient_SkiaShaderType;
+        default:
+            // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+            return kNone_SkiaShaderType;
+    }
+
+    // The shader is not a gradient. Check for a bitmap shader.
+    if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
+        return kBitmap_SkiaShaderType;
+    }
+
+    // Check for a ComposeShader.
+    SkShader::ComposeRec rec;
+    if (shader.asACompose(&rec)) {
+        const SkiaShaderType shaderAType = getType(*rec.fShaderA);
+        const SkiaShaderType shaderBType = getType(*rec.fShaderB);
+
+        // Compose is only supported if one is a bitmap and the other is a
+        // gradient. Otherwise, return None to skip.
+        if (!bitmapAndGradient(shaderAType, shaderBType)) {
+            return kNone_SkiaShaderType;
+        }
+        return kCompose_SkiaShaderType;
+    }
+
+    if (shader.asACustomShader(NULL)) {
+        return kLayer_SkiaShaderType;
+    }
+
+    return kNone_SkiaShaderType;
+}
+
+typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader);
+
+describeProc gDescribeProc[] = {
+    InvalidSkiaShader::describe,
+    SkiaBitmapShader::describe,
+    SkiaGradientShader::describe,
+    SkiaComposeShader::describe,
+    SkiaLayerShader::describe,
+};
+
+typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+
+setupProgramProc gSetupProgramProc[] = {
+    InvalidSkiaShader::setupProgram,
+    SkiaBitmapShader::setupProgram,
+    SkiaGradientShader::setupProgram,
+    SkiaComposeShader::setupProgram,
+    SkiaLayerShader::setupProgram,
+};
+
+void SkiaShader::describe(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    gDescribeProc[getType(shader)](caches, description, extensions, shader);
+}
+
+void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+
+    gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Layer shader
 ///////////////////////////////////////////////////////////////////////////////
 
-SkiaLayerShader::SkiaLayerShader(Layer* layer, const SkMatrix* matrix):
-        SkiaShader(kBitmap, NULL, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
-                matrix, layer->isBlend()), mLayer(layer) {
-    updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaLayerShader::copy() {
-    SkiaLayerShader* copy = new SkiaLayerShader();
-    copy->copyFrom(*this);
-    copy->mLayer = mLayer;
-    return copy;
-}
-
-void SkiaLayerShader::describe(ProgramDescription& description, const Extensions& extensions) {
+void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
+        const Extensions&, const SkShader& shader) {
     description.hasBitmap = true;
 }
 
-void SkiaLayerShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot& snapshot, GLuint* textureUnit) {
-    GLuint textureSlot = (*textureUnit)++;
-    Caches::getInstance().activeTexture(textureSlot);
+void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+    Layer* layer;
+    if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+        LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
+    }
 
-    const float width = mLayer->getWidth();
-    const float height = mLayer->getHeight();
+    GLuint textureSlot = (*textureUnit)++;
+    caches->activeTexture(textureSlot);
+
+    const float width = layer->getWidth();
+    const float height = layer->getHeight();
 
     mat4 textureTransform;
-    computeScreenSpaceMatrix(textureTransform, modelView);
+    computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
+
 
     // Uniforms
-    mLayer->bindTexture();
-    mLayer->setWrap(GL_CLAMP_TO_EDGE);
-    mLayer->setFilter(GL_LINEAR);
+    layer->bindTexture();
+    layer->setWrap(GL_CLAMP_TO_EDGE);
+    layer->setFilter(GL_LINEAR);
 
+    Program* program = caches->currentProgram;
     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
@@ -147,67 +201,99 @@
 // Bitmap shader
 ///////////////////////////////////////////////////////////////////////////////
 
-SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
-        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
-        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
-    updateLocalMatrix(matrix);
-}
+struct BitmapShaderInfo {
+    float width;
+    float height;
+    GLenum wrapS;
+    GLenum wrapT;
+    Texture* texture;
+};
 
-SkiaShader* SkiaBitmapShader::copy() {
-    SkiaBitmapShader* copy = new SkiaBitmapShader();
-    copy->copyFrom(*this);
-    copy->mBitmap = mBitmap;
-    return copy;
-}
-
-void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
-    Texture* texture = mCaches->textureCache.get(mBitmap);
-    if (!texture) return;
-    mTexture = texture;
+static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
+        BitmapShaderInfo* shaderInfo,
+        const Extensions& extensions,
+        const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
+    Texture* texture = caches->textureCache.get(&bitmap);
+    if (!texture) return false;
 
     const float width = texture->width;
     const float height = texture->height;
+    GLenum wrapS, wrapT;
 
-    description.hasBitmap = true;
+    if (description) {
+        description->hasBitmap = true;
+    }
     // The driver does not support non-power of two mirrored/repeated
     // textures, so do it ourselves
     if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
-            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
-        description.isBitmapNpot = true;
-        description.bitmapWrapS = gTileModes[mTileX];
-        description.bitmapWrapT = gTileModes[mTileY];
-        mWrapS = GL_CLAMP_TO_EDGE;
-        mWrapT = GL_CLAMP_TO_EDGE;
+            (tileModes[0] != SkShader::kClamp_TileMode ||
+             tileModes[1] != SkShader::kClamp_TileMode)) {
+        if (description) {
+            description->isBitmapNpot = true;
+            description->bitmapWrapS = gTileModes[tileModes[0]];
+            description->bitmapWrapT = gTileModes[tileModes[1]];
+        }
+        wrapS = GL_CLAMP_TO_EDGE;
+        wrapT = GL_CLAMP_TO_EDGE;
     } else {
-        mWrapS = gTileModes[mTileX];
-        mWrapT = gTileModes[mTileY];
+        wrapS = gTileModes[tileModes[0]];
+        wrapT = gTileModes[tileModes[1]];
     }
+
+    if (shaderInfo) {
+        shaderInfo->width = width;
+        shaderInfo->height = height;
+        shaderInfo->wrapS = wrapS;
+        shaderInfo->wrapT = wrapT;
+        shaderInfo->texture = texture;
+    }
+    return true;
 }
 
-void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot&, GLuint* textureUnit) {
+void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+        LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
+    }
+    bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
+}
+
+void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+        LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
+    }
+
     GLuint textureSlot = (*textureUnit)++;
     Caches::getInstance().activeTexture(textureSlot);
 
-    Texture* texture = mTexture;
-    mTexture = NULL;
-    if (!texture) return;
+    BitmapShaderInfo shaderInfo;
+    if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
+        return;
+    }
+
+    Program* program = caches->currentProgram;
+    Texture* texture = shaderInfo.texture;
+
     const AutoTexture autoCleanup(texture);
 
-    const float width = texture->width;
-    const float height = texture->height;
-
     mat4 textureTransform;
-    computeScreenSpaceMatrix(textureTransform, modelView);
+    computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
 
     // Uniforms
-    bindTexture(texture, mWrapS, mWrapT);
+    bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
     texture->setFilter(GL_LINEAR);
 
     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
-    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+    glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
+            1.0f / shaderInfo.height);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -225,74 +311,6 @@
     matrix->postScale(inv, inv);
 }
 
-SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
-        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
-        SkMatrix* matrix, bool blend):
-        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
-        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
-    SkPoint points[2];
-    points[0].set(bounds[0], bounds[1]);
-    points[1].set(bounds[2], bounds[3]);
-
-    SkMatrix unitMatrix;
-    toUnitMatrix(points, &unitMatrix);
-    mUnitMatrix.load(unitMatrix);
-
-    updateLocalMatrix(matrix);
-
-    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaLinearGradientShader::~SkiaLinearGradientShader() {
-    delete[] mBounds;
-    delete[] mColors;
-    delete[] mPositions;
-}
-
-SkiaShader* SkiaLinearGradientShader::copy() {
-    SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
-    copy->copyFrom(*this);
-    copy->mBounds = new float[4];
-    memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
-    copy->mColors = new uint32_t[mCount];
-    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
-    copy->mPositions = new float[mCount];
-    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
-    copy->mCount = mCount;
-    copy->mIsSimple = mIsSimple;
-    return copy;
-}
-
-void SkiaLinearGradientShader::describe(ProgramDescription& description,
-        const Extensions& extensions) {
-    description.hasGradient = true;
-    description.gradientType = ProgramDescription::kGradientLinear;
-    description.isSimpleGradient = mIsSimple;
-}
-
-void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot&, GLuint* textureUnit) {
-    if (CC_UNLIKELY(!mIsSimple)) {
-        GLuint textureSlot = (*textureUnit)++;
-        Caches::getInstance().activeTexture(textureSlot);
-
-        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
-
-        // Uniforms
-        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
-        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
-    } else {
-        bindUniformColor(program->getUniform("startColor"), mColors[0]);
-        bindUniformColor(program->getUniform("endColor"), mColors[1]);
-    }
-
-    Caches::getInstance().dither.setupProgram(program, textureUnit);
-
-    mat4 screenSpace;
-    computeScreenSpaceMatrix(screenSpace, modelView);
-    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Circular gradient shader
 ///////////////////////////////////////////////////////////////////////////////
@@ -304,37 +322,6 @@
     matrix->postScale(inv, inv);
 }
 
-SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
-        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
-        SkMatrix* matrix, bool blend):
-        SkiaSweepGradientShader(kCircularGradient, colors, positions, count, key,
-                tileMode, matrix, blend) {
-    SkMatrix unitMatrix;
-    toCircularUnitMatrix(x, y, radius, &unitMatrix);
-    mUnitMatrix.load(unitMatrix);
-
-    updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaCircularGradientShader::copy() {
-    SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
-    copy->copyFrom(*this);
-    copy->mColors = new uint32_t[mCount];
-    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
-    copy->mPositions = new float[mCount];
-    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
-    copy->mCount = mCount;
-    copy->mIsSimple = mIsSimple;
-    return copy;
-}
-
-void SkiaCircularGradientShader::describe(ProgramDescription& description,
-        const Extensions& extensions) {
-    description.hasGradient = true;
-    description.gradientType = ProgramDescription::kGradientCircular;
-    description.isSimpleGradient = mIsSimple;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Sweep gradient shader
 ///////////////////////////////////////////////////////////////////////////////
@@ -343,74 +330,103 @@
     matrix->setTranslate(-x, -y);
 }
 
-SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
-        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
-        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
-                SkShader::kClamp_TileMode, matrix, blend),
-        mColors(colors), mPositions(positions), mCount(count) {
-    SkMatrix unitMatrix;
-    toSweepUnitMatrix(x, y, &unitMatrix);
-    mUnitMatrix.load(unitMatrix);
+///////////////////////////////////////////////////////////////////////////////
+// Common gradient code
+///////////////////////////////////////////////////////////////////////////////
 
-    updateLocalMatrix(matrix);
-
-    mIsSimple = count == 2;
+static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
+    return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
 }
 
-SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, uint32_t* colors,
-        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
-        SkMatrix* matrix, bool blend):
-        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
-        mColors(colors), mPositions(positions), mCount(count) {
-    // protected method, that doesn't setup mUnitMatrix - should be handled by subclass
+void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    SkShader::GradientInfo gradInfo;
+    gradInfo.fColorCount = 0;
+    gradInfo.fColors = NULL;
+    gradInfo.fColorOffsets = NULL;
 
-    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaSweepGradientShader::~SkiaSweepGradientShader() {
-    delete[] mColors;
-    delete[] mPositions;
-}
-
-SkiaShader* SkiaSweepGradientShader::copy() {
-    SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
-    copy->copyFrom(*this);
-    copy->mColors = new uint32_t[mCount];
-    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
-    copy->mPositions = new float[mCount];
-    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
-    copy->mCount = mCount;
-    copy->mIsSimple = mIsSimple;
-    return copy;
-}
-
-void SkiaSweepGradientShader::describe(ProgramDescription& description,
-        const Extensions& extensions) {
+    switch (shader.asAGradient(&gradInfo)) {
+        case SkShader::kLinear_GradientType:
+            description.gradientType = ProgramDescription::kGradientLinear;
+            break;
+        case SkShader::kRadial_GradientType:
+            description.gradientType = ProgramDescription::kGradientCircular;
+            break;
+        case SkShader::kSweep_GradientType:
+            description.gradientType = ProgramDescription::kGradientSweep;
+            break;
+        default:
+            // Do nothing. This shader is unsupported.
+            return;
+    }
     description.hasGradient = true;
-    description.gradientType = ProgramDescription::kGradientSweep;
-    description.isSimpleGradient = mIsSimple;
+    description.isSimpleGradient = isSimpleGradient(gradInfo);
 }
 
-void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot& snapshot, GLuint* textureUnit) {
-    if (CC_UNLIKELY(!mIsSimple)) {
-        GLuint textureSlot = (*textureUnit)++;
-        Caches::getInstance().activeTexture(textureSlot);
+void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+    // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
+    // how much space has been allocated for fColors and fColorOffsets.  10 was chosen
+    // arbitrarily, but should be >= 2.
+    // As output, it tells the number of actual colors/offsets in the gradient.
+    const int COLOR_COUNT = 10;
+    SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
+    SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
 
-        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
+    SkShader::GradientInfo gradInfo;
+    gradInfo.fColorCount = COLOR_COUNT;
+    gradInfo.fColors = colorStorage.get();
+    gradInfo.fColorOffsets = positionStorage.get();
+
+    SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
+
+    Program* program = caches->currentProgram;
+    if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+        if (gradInfo.fColorCount > COLOR_COUNT) {
+            // There was not enough room in our arrays for all the colors and offsets. Try again,
+            // now that we know the true number of colors.
+            gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
+            gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
+
+            shader.asAGradient(&gradInfo);
+        }
+        GLuint textureSlot = (*textureUnit)++;
+        caches->activeTexture(textureSlot);
+
+#ifndef SK_SCALAR_IS_FLOAT
+    #error Need to convert gradInfo.fColorOffsets to float!
+#endif
+        Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
+                gradInfo.fColorCount);
 
         // Uniforms
-        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
+        bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
         glUniform1i(program->getUniform("gradientSampler"), textureSlot);
     } else {
-       bindUniformColor(program->getUniform("startColor"), mColors[0]);
-       bindUniformColor(program->getUniform("endColor"), mColors[1]);
+        bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
+        bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
     }
 
-    mCaches->dither.setupProgram(program, textureUnit);
+    caches->dither.setupProgram(program, textureUnit);
+
+    SkMatrix unitMatrix;
+    switch (gradType) {
+        case SkShader::kLinear_GradientType:
+            toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+            break;
+        case SkShader::kRadial_GradientType:
+            toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+                    gradInfo.fRadius[0], &unitMatrix);
+            break;
+        case SkShader::kSweep_GradientType:
+            toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
+    }
 
     mat4 screenSpace;
-    computeScreenSpaceMatrix(screenSpace, modelView);
+    computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
     glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
 }
 
@@ -418,49 +434,39 @@
 // Compose shader
 ///////////////////////////////////////////////////////////////////////////////
 
-SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
-        SkXfermode::Mode mode, SkShader* key):
-        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
-        NULL, first->blend() || second->blend()),
-        mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
-}
-
-SkiaComposeShader::~SkiaComposeShader() {
-    if (mCleanup) {
-        delete mFirst;
-        delete mSecond;
+void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    SkShader::ComposeRec rec;
+    if (!shader.asACompose(&rec)) {
+        LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
     }
-}
-
-SkiaShader* SkiaComposeShader::copy() {
-    SkiaComposeShader* copy = new SkiaComposeShader();
-    copy->copyFrom(*this);
-    copy->mFirst = mFirst->copy();
-    copy->mSecond = mSecond->copy();
-    copy->mMode = mMode;
-    copy->cleanup();
-    return copy;
-}
-
-void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
-    mFirst->describe(description, extensions);
-    mSecond->describe(description, extensions);
-    if (mFirst->type() == kBitmap) {
+    SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
+    SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
+    if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
         description.isBitmapFirst = true;
     }
-    description.shadersMode = mMode;
+    if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
+        // TODO: Support other modes.
+        description.shadersMode = SkXfermode::kSrcOver_Mode;
+    }
 }
 
-void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot& snapshot, GLuint* textureUnit) {
+void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+    SkShader::ComposeRec rec;
+    if (!shader.asACompose(&rec)) {
+        LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
+    }
+
     // Apply this compose shader's local transform and pass it down to
     // the child shaders. They will in turn apply their local transform
     // to this matrix.
     mat4 transform;
-    computeScreenSpaceMatrix(transform, modelView);
+    computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
 
-    mFirst->setupProgram(program, transform, snapshot, textureUnit);
-    mSecond->setupProgram(program, transform, snapshot, textureUnit);
+    SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
+    SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 9f30257..034c3f6 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -28,249 +28,90 @@
 #include "ProgramCache.h"
 #include "TextureCache.h"
 #include "GradientCache.h"
-#include "Snapshot.h"
 
 namespace android {
 namespace uirenderer {
 
 class Caches;
-
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
+class Layer;
 
 /**
- * Represents a Skia shader. A shader will modify the GL context and active
- * program to recreate the original effect.
+ * Type of Skia shader in use.
  */
+enum SkiaShaderType {
+    kNone_SkiaShaderType,
+    kBitmap_SkiaShaderType,
+    kGradient_SkiaShaderType,
+    kCompose_SkiaShaderType,
+    kLayer_SkiaShaderType
+};
+
 class SkiaShader {
 public:
-    /**
-     * Type of Skia shader in use.
-     */
-    enum Type {
-        kNone,
-        kBitmap,
-        kLinearGradient,
-        kCircularGradient,
-        kSweepGradient,
-        kCompose
-    };
+    static SkiaShaderType getType(const SkShader& shader);
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
 
-    ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
-            SkShader::TileMode tileY, const SkMatrix* matrix, bool blend);
-    virtual ~SkiaShader();
-
-    virtual SkiaShader* copy() = 0;
-    void copyFrom(const SkiaShader& shader);
-
-    virtual void describe(ProgramDescription& description, const Extensions& extensions);
-    virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-    inline SkShader* getSkShader() {
-        return mKey;
+class InvalidSkiaShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader) {
+        // This shader is unsupported. Skip it.
+    }
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+        // This shader is unsupported. Skip it.
     }
 
-    inline bool blend() const {
-        return mBlend;
-    }
-
-    Type type() const {
-        return mType;
-    }
-
-    virtual void setCaches(Caches& caches) {
-        mCaches = &caches;
-    }
-
-    uint32_t getGenerationId() {
-        return mGenerationId;
-    }
-
-    void setMatrix(const SkMatrix* matrix) {
-        updateLocalMatrix(matrix);
-        mGenerationId++;
-    }
-
-    void updateLocalMatrix(const SkMatrix* matrix) {
-        if (matrix) {
-            mat4 localMatrix(*matrix);
-            mShaderMatrix.loadInverse(localMatrix);
-        } else {
-            mShaderMatrix.loadIdentity();
-        }
-    }
-
-    void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
-
-protected:
-    SkiaShader();
-
-    /**
-     * The appropriate texture unit must have been activated prior to invoking
-     * this method.
-     */
-    inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT);
-
-    Type mType;
-    SkShader* mKey;
-    SkShader::TileMode mTileX;
-    SkShader::TileMode mTileY;
-    bool mBlend;
-
-    Caches* mCaches;
-
-    mat4 mUnitMatrix;
-    mat4 mShaderMatrix;
-
-private:
-    uint32_t mGenerationId;
-}; // struct SkiaShader
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementations
-///////////////////////////////////////////////////////////////////////////////
-
+};
 /**
  * A shader that draws a layer.
  */
-struct SkiaLayerShader: public SkiaShader {
-    SkiaLayerShader(Layer* layer, const SkMatrix* matrix);
-    SkiaShader* copy();
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-private:
-    SkiaLayerShader() {
-    }
-
-    Layer* mLayer;
-}; // struct SkiaLayerShader
+class SkiaLayerShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaLayerShader
 
 /**
  * A shader that draws a bitmap.
  */
-struct SkiaBitmapShader: public SkiaShader {
-    ANDROID_API SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
-            SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
-    SkiaShader* copy();
+class SkiaBitmapShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
 
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
 
-private:
-    SkiaBitmapShader() : mBitmap(NULL), mTexture(NULL) {
-    }
-
-    SkBitmap* mBitmap;
-    Texture* mTexture;
-    GLenum mWrapS;
-    GLenum mWrapT;
-}; // struct SkiaBitmapShader
+}; // class SkiaBitmapShader
 
 /**
- * A shader that draws a linear gradient.
+ * A shader that draws one of three types of gradient, depending on shader param.
  */
-struct SkiaLinearGradientShader: public SkiaShader {
-    ANDROID_API SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions,
-            int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
-    ~SkiaLinearGradientShader();
-    SkiaShader* copy();
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-private:
-    SkiaLinearGradientShader() {
-    }
-
-    bool mIsSimple;
-    float* mBounds;
-    uint32_t* mColors;
-    float* mPositions;
-    int mCount;
-}; // struct SkiaLinearGradientShader
-
-/**
- * A shader that draws a sweep gradient.
- */
-struct SkiaSweepGradientShader: public SkiaShader {
-    ANDROID_API SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions,
-            int count, SkShader* key, SkMatrix* matrix, bool blend);
-    ~SkiaSweepGradientShader();
-    SkiaShader* copy();
-
-    virtual void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-protected:
-    SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions,
-            int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
-    SkiaSweepGradientShader() {
-    }
-
-    bool mIsSimple;
-    uint32_t* mColors;
-    float* mPositions;
-    int mCount;
-}; // struct SkiaSweepGradientShader
-
-/**
- * A shader that draws a circular gradient.
- */
-struct SkiaCircularGradientShader: public SkiaSweepGradientShader {
-    ANDROID_API SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors,
-            float* positions, int count, SkShader* key,SkShader::TileMode tileMode,
-            SkMatrix* matrix, bool blend);
-    SkiaShader* copy();
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-
-private:
-    SkiaCircularGradientShader() {
-    }
-}; // struct SkiaCircularGradientShader
+class SkiaGradientShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
 
 /**
  * A shader that draws two shaders, composited with an xfermode.
  */
-struct SkiaComposeShader: public SkiaShader {
-    ANDROID_API SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode,
-            SkShader* key);
-    ~SkiaComposeShader();
-    SkiaShader* copy();
-
-    void setCaches(Caches& caches) {
-        SkiaShader::setCaches(caches);
-        mFirst->setCaches(caches);
-        mSecond->setCaches(caches);
-    }
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-private:
-    SkiaComposeShader(): mCleanup(false) {
-    }
-
-    void cleanup() {
-        mCleanup = true;
-    }
-
-    SkiaShader* mFirst;
-    SkiaShader* mSecond;
-    SkXfermode::Mode mMode;
-
-    bool mCleanup;
-}; // struct SkiaComposeShader
+class SkiaComposeShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaComposeShader
 
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 34e2265..60b4b96 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -34,7 +34,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 TextureCache::TextureCache():
-        mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
         mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
     char property[PROPERTY_VALUE_MAX];
@@ -58,7 +58,7 @@
 }
 
 TextureCache::TextureCache(uint32_t maxByteSize):
-        mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     init();
 }
@@ -103,7 +103,7 @@
 // Callbacks
 ///////////////////////////////////////////////////////////////////////////////
 
-void TextureCache::operator()(const SkBitmap*&, Texture*& texture) {
+void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) {
     // This will be called already locked
     if (texture) {
         mSize -= texture->bitmapSize;
@@ -122,7 +122,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void TextureCache::resetMarkInUse() {
-    LruCache<const SkBitmap*, Texture*>::Iterator iter(mCache);
+    LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache);
     while (iter.next()) {
         iter.value()->isInUse = false;
     }
@@ -140,7 +140,7 @@
 // Returns a prepared Texture* that either is already in the cache or can fit
 // in the cache (and is thus added to the cache)
 Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
-    Texture* texture = mCache.get(bitmap);
+    Texture* texture = mCache.get(bitmap->pixelRef());
 
     if (!texture) {
         if (!canMakeTextureFromBitmap(bitmap)) {
@@ -170,7 +170,7 @@
             if (mDebugEnabled) {
                 ALOGD("Texture created, size = %d", size);
             }
-            mCache.put(bitmap, texture);
+            mCache.put(bitmap->pixelRef(), texture);
         }
     } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
         // Texture was in the cache but is dirty, re-upload
@@ -218,7 +218,7 @@
 }
 
 void TextureCache::remove(const SkBitmap* bitmap) {
-    mCache.remove(bitmap);
+    mCache.remove(bitmap->pixelRef());
 }
 
 void TextureCache::removeDeferred(const SkBitmap* bitmap) {
@@ -231,7 +231,7 @@
     size_t count = mGarbage.size();
     for (size_t i = 0; i < count; i++) {
         const SkBitmap* bitmap = mGarbage.itemAt(i);
-        mCache.remove(bitmap);
+        mCache.remove(bitmap->pixelRef());
         delete bitmap;
     }
     mGarbage.clear();
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 48a10c2..e5b5c1a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -49,7 +49,7 @@
  * Any texture added to the cache causing the cache to grow beyond the maximum
  * allowed size will also cause the oldest texture to be kicked out.
  */
-class TextureCache: public OnEntryRemoved<const SkBitmap*, Texture*> {
+class TextureCache: public OnEntryRemoved<const SkPixelRef*, Texture*> {
 public:
     TextureCache();
     TextureCache(uint32_t maxByteSize);
@@ -59,7 +59,7 @@
      * Used as a callback when an entry is removed from the cache.
      * Do not invoke directly.
      */
-    void operator()(const SkBitmap*& bitmap, Texture*& texture);
+    void operator()(const SkPixelRef*& pixelRef, Texture*& texture);
 
     /**
      * Resets all Textures to not be marked as in use
@@ -147,7 +147,7 @@
 
     void init();
 
-    LruCache<const SkBitmap*, Texture*> mCache;
+    LruCache<const SkPixelRef*, Texture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 2685447..d2bf30c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -112,7 +112,9 @@
     }
 
     public interface OnDismissAction {
-        /* returns true if the dismiss should be deferred */
+        /**
+         * @return true if the dismiss should be deferred
+         */
         boolean onDismiss();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index ef7fb89..a1e70b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -83,7 +83,7 @@
 
         boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null);
         boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null);
-        state.enabled = wifiConnected;
+        state.enabled = cb.enabled;
         state.connected = wifiConnected;
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index b244ae1..24fe3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -84,6 +84,8 @@
 import java.util.Arrays;
 import java.util.Locale;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
     public static final String TAG = "StatusBar";
@@ -178,6 +180,7 @@
      */
     protected int mState;
     protected boolean mBouncerShowing;
+    protected boolean mShowLockscreenNotifications;
 
     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
 
@@ -197,6 +200,9 @@
             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
             setZenMode(mode);
+            final boolean show = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1) != 0;
+            setShowLockscreenNotifications(show);
         }
     };
 
@@ -213,33 +219,47 @@
 
     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
         @Override
-        public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+        public boolean onClickHandler(
+                final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
             if (DEBUG) {
                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
             }
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
-                try {
-                    // The intent we are sending is for the application, which
-                    // won't have permission to immediately start an activity after
-                    // the user switches to home.  We know it is safe to do at this
-                    // point, so make sure new activity switches are now allowed.
-                    ActivityManagerNative.getDefault().resumeAppSwitches();
-                    // Also, notifications can be launched from the lock screen,
-                    // so dismiss the lock screen when the activity starts.
-                    ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-                } catch (RemoteException e) {
-                }
-            }
+                startNotificationActivity(new OnDismissAction() {
+                    @Override
+                    public boolean onDismiss() {
+                        try {
+                            // The intent we are sending is for the application, which
+                            // won't have permission to immediately start an activity after
+                            // the user switches to home.  We know it is safe to do at this
+                            // point, so make sure new activity switches are now allowed.
+                            ActivityManagerNative.getDefault().resumeAppSwitches();
+                            // Also, notifications can be launched from the lock screen,
+                            // so dismiss the lock screen when the activity starts.
+                            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                        } catch (RemoteException e) {
+                        }
 
-            boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
+                        boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
 
-            if (isActivity && handled) {
-                // close the shade if it was open
-                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                visibilityChanged(false);
+                        // close the shade if it was open
+                        if (handled) {
+                            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                            visibilityChanged(false);
+                        }
+                        return handled; // Wait for activity start.
+                    }
+                });
+                return true;
+            } else {
+                return super.onClickHandler(view, pendingIntent, fillInIntent);
             }
-            return handled;
+        }
+
+        private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
+                Intent fillInIntent) {
+            return super.onClickHandler(view, pendingIntent, fillInIntent);
         }
     };
 
@@ -328,6 +348,9 @@
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
                 mSettingsObserver);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+                mSettingsObserver);
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -434,6 +457,14 @@
         }
     }
 
+    /**
+     * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
+     * @param action A dismiss action that is called if it's safe to start the activity.
+     */
+    protected void startNotificationActivity(OnDismissAction action) {
+        action.onDismiss();
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         final Locale locale = mContext.getResources().getConfiguration().locale;
@@ -999,47 +1030,55 @@
             mIsHeadsUp = forHun;
         }
 
-        public void onClick(View v) {
-            try {
-                // The intent we are sending is for the application, which
-                // won't have permission to immediately start an activity after
-                // the user switches to home.  We know it is safe to do at this
-                // point, so make sure new activity switches are now allowed.
-                ActivityManagerNative.getDefault().resumeAppSwitches();
-                // Also, notifications can be launched from the lock screen,
-                // so dismiss the lock screen when the activity starts.
-                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-            } catch (RemoteException e) {
-            }
+        public void onClick(final View v) {
+            startNotificationActivity(new OnDismissAction() {
+                public boolean onDismiss() {
+                    try {
+                        // The intent we are sending is for the application, which
+                        // won't have permission to immediately start an activity after
+                        // the user switches to home.  We know it is safe to do at this
+                        // point, so make sure new activity switches are now allowed.
+                        ActivityManagerNative.getDefault().resumeAppSwitches();
+                        // Also, notifications can be launched from the lock screen,
+                        // so dismiss the lock screen when the activity starts.
+                        ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                    } catch (RemoteException e) {
+                    }
 
-            if (mIntent != null) {
-                int[] pos = new int[2];
-                v.getLocationOnScreen(pos);
-                Intent overlay = new Intent();
-                overlay.setSourceBounds(
-                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
-                try {
-                    mIntent.send(mContext, 0, overlay);
-                } catch (PendingIntent.CanceledException e) {
-                    // the stack trace isn't very helpful here.  Just log the exception message.
-                    Log.w(TAG, "Sending contentIntent failed: " + e);
+                    boolean sent = false;
+                    if (mIntent != null) {
+                        int[] pos = new int[2];
+                        v.getLocationOnScreen(pos);
+                        Intent overlay = new Intent();
+                        overlay.setSourceBounds(new Rect(pos[0], pos[1],
+                                pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                        try {
+                            mIntent.send(mContext, 0, overlay);
+                            sent = true;
+                        } catch (PendingIntent.CanceledException e) {
+                            // the stack trace isn't very helpful here.
+                            // Just log the exception message.
+                            Log.w(TAG, "Sending contentIntent failed: " + e);
+                        }
+                    }
+
+                    try {
+                        if (mIsHeadsUp) {
+                            mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+                        }
+                        mBarService.onNotificationClick(mNotificationKey);
+                    } catch (RemoteException ex) {
+                        // system process is dead if we're here.
+                    }
+
+                    // close the shade if it was open
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                    visibilityChanged(false);
+
+                    boolean waitForActivityLaunch = sent && mIntent.isActivity();
+                    return waitForActivityLaunch;
                 }
-
-                KeyguardTouchDelegate.getInstance(mContext).dismiss();
-            }
-
-            try {
-                if (mIsHeadsUp) {
-                    mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
-                }
-                mBarService.onNotificationClick(mNotificationKey);
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-
-            // close the shade if it was open
-            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-            visibilityChanged(false);
+            });
         }
     }
 
@@ -1209,6 +1248,10 @@
         updateNotificationIcons();
     }
 
+    protected void setShowLockscreenNotifications(boolean show) {
+        mShowLockscreenNotifications = show;
+    }
+
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
     protected abstract void updateNotificationIcons();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d8e1766..9138867 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -28,6 +28,7 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.keyguard.KeyguardSecurityModel.*;
 
 /**
@@ -69,6 +70,12 @@
         }
     }
 
+    public void showWithDismissAction(OnDismissAction r) {
+        ensureView();
+        mKeyguardView.setOnDismissAction(r);
+        show();
+    }
+
     public void hide() {
         if (mKeyguardView != null) {
             mKeyguardView.cleanUp();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 35ffb9c..22d1bba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
@@ -499,6 +500,12 @@
     }
 
     @Override
+    protected void setShowLockscreenNotifications(boolean show) {
+        super.setShowLockscreenNotifications(show);
+        updateStackScrollerState();
+    }
+
+    @Override
     public void start() {
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
@@ -2346,6 +2353,15 @@
         }
     };
 
+    @Override
+    protected void startNotificationActivity(OnDismissAction action) {
+        if (mStatusBarKeyguardViewManager.isShowing()) {
+            mStatusBarKeyguardViewManager.dismissWithAction(action);
+        } else {
+            action.onDismiss();
+        }
+    }
+
     // SystemUIService notifies SystemBars of configuration changes, which then calls down here
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
@@ -2774,7 +2790,10 @@
     }
 
     public void updateStackScrollerState() {
+        if (mStackScroller == null) return;
         mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
+        mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD
+                ? View.INVISIBLE : View.VISIBLE);
     }
 
     public void userActivity() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1040c15..3849d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -29,6 +29,8 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -108,6 +110,13 @@
         updateStates();
     }
 
+    public void dismissWithAction(OnDismissAction r) {
+        if (!mOccluded) {
+            mBouncer.showWithDismissAction(r);
+        }
+        updateStates();
+    }
+
     /**
      * Reset the state of the view.
      */
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 966c0b0..56402a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -339,7 +339,7 @@
         boolean wifiOut = wifiEnabled && mWifiSsid != null
                 && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
                 || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT);
-        cb.onWifiSignalChanged(wifiEnabled, mQSWifiIconId, wifiIn, wifiOut,
+        cb.onWifiSignalChanged(mWifiEnabled, mQSWifiIconId, wifiIn, wifiOut,
                 mContentDescriptionWifi, wifiDesc);
 
         boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9977193..d909568 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -66,6 +66,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.telephony.TelephonyManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -1921,9 +1922,8 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
     }
 
-    static ITelephony getTelephonyService() {
-        return ITelephony.Stub.asInterface(
-                ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+    TelephonyManager getTelephonyService() {
+        return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
     }
 
     static IAudioService getAudioService() {
@@ -2006,14 +2006,10 @@
                 // If an incoming call is ringing, HOME is totally disabled.
                 // (The user is already on the InCallScreen at this point,
                 // and his ONLY options are to answer or reject the call.)
-                try {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null && telephonyService.isRinging()) {
-                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
-                        return -1;
-                    }
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+                TelephonyManager telephonyManager = getTelephonyService();
+                if (telephonyManager != null && telephonyManager.isRinging()) {
+                    Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+                    return -1;
                 }
 
                 // Delay handling home if a double-tap is possible.
@@ -3957,37 +3953,33 @@
                     }
                 }
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null) {
-                        try {
-                            if (telephonyService.isRinging()) {
-                                // If an incoming call is ringing, either VOLUME key means
-                                // "silence ringer".  We handle these keys here, rather than
-                                // in the InCallScreen, to make sure we'll respond to them
-                                // even if the InCallScreen hasn't come to the foreground yet.
-                                // Look for the DOWN event here, to agree with the "fallback"
-                                // behavior in the InCallScreen.
-                                Log.i(TAG, "interceptKeyBeforeQueueing:"
-                                      + " VOLUME key-down while ringing: Silence ringer!");
+                    TelephonyManager telephonyManager = getTelephonyService();
+                    if (telephonyManager != null) {
+                        if (telephonyManager.isRinging()) {
+                            // If an incoming call is ringing, either VOLUME key means
+                            // "silence ringer".  We handle these keys here, rather than
+                            // in the InCallScreen, to make sure we'll respond to them
+                            // even if the InCallScreen hasn't come to the foreground yet.
+                            // Look for the DOWN event here, to agree with the "fallback"
+                            // behavior in the InCallScreen.
+                            Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                  + " VOLUME key-down while ringing: Silence ringer!");
 
-                                // Silence the ringer.  (It's safe to call this
-                                // even if the ringer has already been silenced.)
-                                telephonyService.silenceRinger();
+                            // Silence the ringer.  (It's safe to call this
+                            // even if the ringer has already been silenced.)
+                            telephonyManager.silenceRinger();
 
-                                // And *don't* pass this key thru to the current activity
-                                // (which is probably the InCallScreen.)
-                                result &= ~ACTION_PASS_TO_USER;
-                                break;
-                            }
-                            if (telephonyService.isOffhook()
-                                    && (result & ACTION_PASS_TO_USER) == 0) {
-                                // If we are in call but we decided not to pass the key to
-                                // the application, handle the volume change here.
-                                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
-                                break;
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                            // And *don't* pass this key thru to the current activity
+                            // (which is probably the InCallScreen.)
+                            result &= ~ACTION_PASS_TO_USER;
+                            break;
+                        }
+                        if (telephonyManager.isOffhook()
+                                && (result & ACTION_PASS_TO_USER) == 0) {
+                            // If we are in call but we decided not to pass the key to
+                            // the application, handle the volume change here.
+                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+                            break;
                         }
                     }
 
@@ -4004,14 +3996,10 @@
             case KeyEvent.KEYCODE_ENDCALL: {
                 result &= ~ACTION_PASS_TO_USER;
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
+                    TelephonyManager telephonyManager = getTelephonyService();
                     boolean hungUp = false;
-                    if (telephonyService != null) {
-                        try {
-                            hungUp = telephonyService.endCall();
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
-                        }
+                    if (telephonyManager != null) {
+                        hungUp = telephonyManager.endCall();
                     }
                     interceptPowerKeyDown(!interactive || hungUp);
                 } else {
@@ -4047,23 +4035,19 @@
                         interceptScreenshotChord();
                     }
 
-                    ITelephony telephonyService = getTelephonyService();
+                    TelephonyManager telephonyManager = getTelephonyService();
                     boolean hungUp = false;
-                    if (telephonyService != null) {
-                        try {
-                            if (telephonyService.isRinging()) {
-                                // Pressing Power while there's a ringing incoming
-                                // call should silence the ringer.
-                                telephonyService.silenceRinger();
-                            } else if ((mIncallPowerBehavior
-                                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
-                                    && telephonyService.isOffhook() && interactive) {
-                                // Otherwise, if "Power button ends call" is enabled,
-                                // the Power button will hang up any current active call.
-                                hungUp = telephonyService.endCall();
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                    if (telephonyManager != null) {
+                        if (telephonyManager.isRinging()) {
+                            // Pressing Power while there's a ringing incoming
+                            // call should silence the ringer.
+                            telephonyManager.silenceRinger();
+                        } else if ((mIncallPowerBehavior
+                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+                                && telephonyManager.isOffhook() && interactive) {
+                            // Otherwise, if "Power button ends call" is enabled,
+                            // the Power button will hang up any current active call.
+                            hungUp = telephonyManager.endCall();
                         }
                     }
                     interceptPowerKeyDown(!interactive || hungUp
@@ -4096,16 +4080,12 @@
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null) {
-                        try {
-                            if (!telephonyService.isIdle()) {
-                                // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
-                                // to avoid music playback.
-                                break;
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                    TelephonyManager telephonyManager = getTelephonyService();
+                    if (telephonyManager != null) {
+                        if (!telephonyManager.isIdle()) {
+                            // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+                            // to avoid music playback.
+                            break;
                         }
                     }
                 }
@@ -4135,20 +4115,16 @@
 
             case KeyEvent.KEYCODE_CALL: {
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null) {
-                        try {
-                            if (telephonyService.isRinging()) {
-                                Log.i(TAG, "interceptKeyBeforeQueueing:"
-                                      + " CALL key-down while ringing: Answer the call!");
-                                telephonyService.answerRingingCall();
+                    TelephonyManager telephonyManager = getTelephonyService();
+                    if (telephonyManager != null) {
+                        if (telephonyManager.isRinging()) {
+                            Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                  + " CALL key-down while ringing: Answer the call!");
+                            telephonyManager.answerRingingCall();
 
-                                // And *don't* pass this key thru to the current activity
-                                // (which is presumably the InCallScreen.)
-                                result &= ~ACTION_PASS_TO_USER;
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                            // And *don't* pass this key thru to the current activity
+                            // (which is presumably the InCallScreen.)
+                            result &= ~ACTION_PASS_TO_USER;
                         }
                     }
                 }
@@ -5565,8 +5541,13 @@
         pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
                 pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
         pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
+
         mStatusBarController.dump(pw, prefix);
         mNavigationBarController.dump(pw, prefix);
         PolicyControl.dump(prefix, pw);
+
+        if (mOrientationListener != null) {
+            mOrientationListener.dump(pw, prefix);
+        }
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
index 0c77556..2cc33b5f 100644
--- a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
+++ b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java
@@ -26,6 +26,9 @@
 import android.util.FloatMath;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
 
 /**
  * A special helper class used by the WindowManager
@@ -181,6 +184,19 @@
      */
     public abstract void onProposedRotationChanged(int rotation);
 
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mLock) {
+            pw.println(prefix + TAG);
+            prefix += "  ";
+            pw.println(prefix + "mEnabled=" + mEnabled);
+            pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
+            pw.println(prefix + "mSensor=" + mSensor);
+            pw.println(prefix + "mRate=" + mRate);
+
+            mSensorEventListener.dumpLocked(pw, prefix);
+        }
+    }
+
     /**
      * This class filters the raw accelerometer data and tries to detect actual changes in
      * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
@@ -342,6 +358,14 @@
             /* ROTATION_270 */ { -25, 65 }
         };
 
+        // The tilt angle below which we conclude that the user is holding the device
+        // overhead reading in bed and lock into that state.
+        private final int TILT_OVERHEAD_ENTER = -40;
+
+        // The tilt angle above which we conclude that the user would like a rotation
+        // change to occur and unlock from the overhead state.
+        private final int TILT_OVERHEAD_EXIT = -15;
+
         // The gap angle in degrees between adjacent orientation angles for hysteresis.
         // This creates a "dead zone" between the current orientation and a proposed
         // adjacent orientation.  No orientation proposal is made when the orientation
@@ -364,12 +388,18 @@
 
         // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
         private long mFlatTimestampNanos;
+        private boolean mFlat;
 
         // Timestamp when the device last appeared to be swinging.
         private long mSwingTimestampNanos;
+        private boolean mSwinging;
 
         // Timestamp when the device last appeared to be undergoing external acceleration.
         private long mAccelerationTimestampNanos;
+        private boolean mAccelerating;
+
+        // Whether we are locked into an overhead usage mode.
+        private boolean mOverhead;
 
         // History of observed tilt angles.
         private static final int TILT_HISTORY_SIZE = 40;
@@ -381,6 +411,19 @@
             return mProposedRotation;
         }
 
+        public void dumpLocked(PrintWriter pw, String prefix) {
+            pw.println(prefix + "mProposedRotation=" + mProposedRotation);
+            pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
+            pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
+            pw.println(prefix + "mLastFilteredY=" + mLastFilteredY);
+            pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ);
+            pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}");
+            pw.println(prefix + "mFlat=" + mFlat);
+            pw.println(prefix + "mSwinging=" + mSwinging);
+            pw.println(prefix + "mAccelerating=" + mAccelerating);
+            pw.println(prefix + "mOverhead=" + mOverhead);
+        }
+
         @Override
         public void onAccuracyChanged(Sensor sensor, int accuracy) {
         }
@@ -478,7 +521,18 @@
 
                         // If the tilt angle is too close to horizontal then we cannot determine
                         // the orientation angle of the screen.
-                        if (Math.abs(tiltAngle) > MAX_TILT) {
+                        if (tiltAngle <= TILT_OVERHEAD_ENTER) {
+                            mOverhead = true;
+                        } else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
+                            mOverhead = false;
+                        }
+                        if (mOverhead) {
+                            if (LOG) {
+                                Slog.v(TAG, "Ignoring sensor data, device is overhead: "
+                                        + "tiltAngle=" + tiltAngle);
+                            }
+                            clearPredictedRotationLocked();
+                        } else if (Math.abs(tiltAngle) > MAX_TILT) {
                             if (LOG) {
                                 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
                                         + "tiltAngle=" + tiltAngle);
@@ -526,6 +580,9 @@
                         }
                     }
                 }
+                mFlat = isFlat;
+                mSwinging = isSwinging;
+                mAccelerating = isAccelerating;
 
                 // Determine new proposed rotation.
                 oldProposedRotation = mProposedRotation;
@@ -543,6 +600,7 @@
                             + ", isAccelerating=" + isAccelerating
                             + ", isFlat=" + isFlat
                             + ", isSwinging=" + isSwinging
+                            + ", isOverhead=" + mOverhead
                             + ", timeUntilSettledMS=" + remainingMS(now,
                                     mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
                             + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
@@ -660,8 +718,12 @@
             mLastFilteredTimestampNanos = Long.MIN_VALUE;
             mProposedRotation = -1;
             mFlatTimestampNanos = Long.MIN_VALUE;
+            mFlat = false;
             mSwingTimestampNanos = Long.MIN_VALUE;
+            mSwinging = false;
             mAccelerationTimestampNanos = Long.MIN_VALUE;
+            mAccelerating = false;
+            mOverhead = false;
             clearPredictedRotationLocked();
             clearTiltHistoryLocked();
         }
@@ -726,6 +788,11 @@
             return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
         }
 
+        private float getLastTiltLocked() {
+            int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex);
+            return index >= 0 ? mTiltHistory[index] : Float.NaN;
+        }
+
         private float remainingMS(long now, long until) {
             return now >= until ? 0 : (until - now) * 0.000001f;
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7ecf248..1e21e1c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -343,12 +343,6 @@
     private static final int EVENT_INET_CONDITION_HOLD_END = 5;
 
     /**
-     * used internally to set enable/disable cellular data
-     * arg1 = ENBALED or DISABLED
-     */
-    private static final int EVENT_SET_MOBILE_DATA = 7;
-
-    /**
      * used internally to clear a wakelock when transitioning
      * from one net to another
      */
@@ -1822,20 +1816,6 @@
         return true;
     }
 
-    /**
-     * @see ConnectivityManager#getMobileDataEnabled()
-     */
-    public boolean getMobileDataEnabled() {
-        // TODO: This detail should probably be in DataConnectionTracker's
-        //       which is where we store the value and maybe make this
-        //       asynchronous.
-        enforceAccessPermission();
-        boolean retVal = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.MOBILE_DATA, 1) == 1;
-        if (VDBG) log("getMobileDataEnabled returning " + retVal);
-        return retVal;
-    }
-
     public void setDataDependency(int networkType, boolean met) {
         enforceConnectivityInternalPermission();
 
@@ -1908,22 +1888,6 @@
         }
     };
 
-    /**
-     * @see ConnectivityManager#setMobileDataEnabled(boolean)
-     */
-    public void setMobileDataEnabled(boolean enabled) {
-        enforceChangePermission();
-        if (DBG) log("setMobileDataEnabled(" + enabled + ")");
-
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
-                (enabled ? ENABLED : DISABLED), 0));
-    }
-
-    private void handleSetMobileData(boolean enabled) {
-    // TODO - handle this - probably generalize passing in a transport type and send to the
-    // factories?
-    }
-
     @Override
     public void setPolicyDataEnable(int networkType, boolean enabled) {
         // only someone like NPMS should only be calling us
@@ -3315,11 +3279,6 @@
                     handleInetConditionHoldEnd(netType, sequence);
                     break;
                 }
-                case EVENT_SET_MOBILE_DATA: {
-                    boolean enabled = (msg.arg1 == ENABLED);
-                    handleSetMobileData(enabled);
-                    break;
-                }
                 case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
                     handleDeprecatedGlobalHttpProxy();
                     break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac30319..248b44d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6010,6 +6010,9 @@
             IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG,
                 "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
+        if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
+            return false;
+        }
 
         if (pi.applicationInfo.uid == uid) {
             return true;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 33e59a7..df80f02 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -33,6 +33,7 @@
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
@@ -342,6 +343,10 @@
         mWindowManager = mService.mWindowManager;
         mStackId = activityContainer.mStackId;
         mCurrentUser = mService.mCurrentUserId;
+        // Get the activity screenshot thumbnail dimensions
+        Resources res = mService.mContext.getResources();
+        mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+        mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
     }
 
     /**
@@ -725,42 +730,54 @@
         }
     }
 
+    /**
+     * This resets the saved state from the last screenshot, forcing a new screenshot to be taken
+     * again when requested.
+     */
+    private void invalidateLastScreenshot() {
+        mLastScreenshotActivity = null;
+        if (mLastScreenshotBitmap != null) {
+            mLastScreenshotBitmap.recycle();
+        }
+        mLastScreenshotBitmap = null;
+    }
+
     public final Bitmap screenshotActivities(ActivityRecord who) {
+        if (DEBUG_SCREENSHOTS) Slog.d(TAG, "screenshotActivities: " + who);
         if (who.noDisplay) {
+            if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tNo display");
             return null;
         }
 
         TaskRecord tr = who.task;
-        if (mService.getMostRecentTask() != tr && tr.intent != null &&
-                (tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
-            // If this task is being excluded from recents, we don't want to take
-            // the expense of capturing a thumbnail, since we will never show it.
+        if (mService.getMostRecentTask() != tr || isHomeStack()) {
+            // This is an optimization -- since we never show Home or Recents within Recents itself,
+            // we can just go ahead and skip taking the screenshot if this is the home stack.  In
+            // the case where the most recent task is not the task that was supplied, then the stack
+            // has changed, so invalidate the last screenshot().
+            invalidateLastScreenshot();
+            if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tIs Home stack? " + isHomeStack());
             return null;
         }
 
-        Resources res = mService.mContext.getResources();
         int w = mThumbnailWidth;
         int h = mThumbnailHeight;
-        if (w < 0) {
-            mThumbnailWidth = w =
-               res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_width);
-            mThumbnailHeight = h =
-               res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_height);
-        }
-
         if (w > 0) {
             if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
                     || mLastScreenshotActivity.state == ActivityState.RESUMED
                     || mLastScreenshotBitmap.getWidth() != w
                     || mLastScreenshotBitmap.getHeight() != h) {
+                if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tUpdating screenshot");
                 mLastScreenshotActivity = who;
                 mLastScreenshotBitmap = mWindowManager.screenshotApplications(
                         who.appToken, Display.DEFAULT_DISPLAY, w, h, SCREENSHOT_FORCE_565);
             }
             if (mLastScreenshotBitmap != null) {
+                if (DEBUG_SCREENSHOTS) Slog.d(TAG, "\tReusing last screenshot");
                 return mLastScreenshotBitmap.copy(mLastScreenshotBitmap.getConfig(), true);
             }
         }
+        Slog.e(TAG, "Invalid thumbnail dimensions: " + w + "x" + h);
         return null;
     }
 
@@ -1032,6 +1049,12 @@
         } else {
             next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
         }
+
+        // If we are resuming the activity that we had last screenshotted, then we know it will be
+        // updated, so invalidate the last screenshot to ensure we take a fresh one when requested
+        if (next == mLastScreenshotActivity) {
+            invalidateLastScreenshot();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 252c0bb..adc7aff 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -106,6 +106,7 @@
     static final boolean DEBUG_SAVED_STATE = DEBUG || false;
     static final boolean DEBUG_STATES = DEBUG || false;
     static final boolean DEBUG_IDLE = DEBUG || false;
+    static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
 
     public static final int HOME_STACK_ID = 0;
 
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index 1bc278d..f8e9b7b 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -164,8 +164,13 @@
         mActionTimer.sendTimerMessage(state, delayMillis);
     }
 
-    protected final boolean sendCommand(HdmiCecMessage cmd) {
-        return mService.sendCecCommand(cmd);
+    protected final void sendCommand(HdmiCecMessage cmd) {
+        mService.sendCecCommand(cmd);
+    }
+
+    protected final void sendCommand(HdmiCecMessage cmd,
+            HdmiControlService.SendMessageCallback callback) {
+        mService.sendCecCommand(cmd, callback);
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index f99a01d..662159d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -20,9 +20,6 @@
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
@@ -176,13 +173,59 @@
      *                         Otherwise, scan address will start from {@code preferredAddress}
      * @param callback callback interface to report allocated logical address to caller
      */
-    void allocateLogicalAddress(int deviceType, int preferredAddress,
-            AllocateLogicalAddressCallback callback) {
-        Message msg = mIoHandler.obtainMessage(MSG_ALLOCATE_LOGICAL_ADDRESS);
-        msg.arg1 = deviceType;
-        msg.arg2 = preferredAddress;
-        msg.obj = callback;
-        mIoHandler.sendMessage(msg);
+    void allocateLogicalAddress(final int deviceType, final int preferredAddress,
+            final AllocateLogicalAddressCallback callback) {
+        runOnIoThread(new Runnable() {
+            @Override
+            public void run() {
+                handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
+            }
+        });
+    }
+
+    private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
+            final AllocateLogicalAddressCallback callback) {
+        int startAddress = preferredAddress;
+        // If preferred address is "unregistered", start address will be the smallest
+        // address matched with the given device type.
+        if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
+            for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+                if (deviceType == HdmiCec.getTypeFromAddress(i)) {
+                    startAddress = i;
+                    break;
+                }
+            }
+        }
+
+        int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
+        // Iterates all possible addresses which has the same device type.
+        for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
+            int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
+            if (curAddress != HdmiCec.ADDR_UNREGISTERED
+                    && deviceType == HdmiCec.getTypeFromAddress(i)) {
+                // <Polling Message> is a message which has empty body and
+                // uses same address for both source and destination address.
+                // If sending <Polling Message> failed (NAK), it becomes
+                // new logical address for the device because no device uses
+                // it as logical address of the device.
+                int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress,
+                        EMPTY_BODY);
+                if (error != ERROR_SUCCESS) {
+                    logicalAddress = curAddress;
+                    break;
+                }
+            }
+        }
+
+        final int assignedAddress = logicalAddress;
+        if (callback != null) {
+            runOnServiceThread(new Runnable() {
+                    @Override
+                public void run() {
+                    callback.onAllocated(deviceType, assignedAddress);
+                }
+            });
+        }
     }
 
     private static byte[] buildBody(int opcode, byte[] params) {
@@ -192,101 +235,6 @@
         return body;
     }
 
-    private final class IoHandler extends Handler {
-        private IoHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SEND_CEC_COMMAND:
-                    HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj;
-                    byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
-                    nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
-                            cecMessage.getDestination(), body);
-                    break;
-                case MSG_ALLOCATE_LOGICAL_ADDRESS:
-                    int deviceType = msg.arg1;
-                    int preferredAddress = msg.arg2;
-                    AllocateLogicalAddressCallback callback =
-                            (AllocateLogicalAddressCallback) msg.obj;
-                    handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
-                    break;
-                default:
-                    Slog.w(TAG, "Unsupported CEC Io request:" + msg.what);
-                    break;
-            }
-        }
-
-        private void handleAllocateLogicalAddress(int deviceType, int preferredAddress,
-                AllocateLogicalAddressCallback callback) {
-            int startAddress = preferredAddress;
-            // If preferred address is "unregistered", start_index will be the smallest
-            // address matched with the given device type.
-            if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
-                for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
-                    if (deviceType == HdmiCec.getTypeFromAddress(i)) {
-                        startAddress = i;
-                        break;
-                    }
-                }
-            }
-
-            int logcialAddress = HdmiCec.ADDR_UNREGISTERED;
-            // Iterates all possible addresses which has the same device type.
-            for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
-                int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
-                if (curAddress != HdmiCec.ADDR_UNREGISTERED
-                        && deviceType == HdmiCec.getTypeFromAddress(i)) {
-                    // <Polling Message> is a message which has empty body and
-                    // uses same address for both source and destination address.
-                    // If sending <Polling Message> failed (NAK), it becomes
-                    // new logical address for the device because no device uses
-                    // it as logical address of the device.
-                    int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress,
-                            EMPTY_BODY);
-                    if (error != ERROR_SUCCESS) {
-                        logcialAddress = curAddress;
-                        break;
-                    }
-                }
-            }
-
-            Message msg = mControlHandler.obtainMessage(MSG_REPORT_LOGICAL_ADDRESS);
-            msg.arg1 = deviceType;
-            msg.arg2 = logcialAddress;
-            msg.obj = callback;
-            mControlHandler.sendMessage(msg);
-        }
-    }
-
-    private final class ControlHandler extends Handler {
-        private ControlHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_RECEIVE_CEC_COMMAND:
-                    // TODO: delegate it to HdmiControl service.
-                    onReceiveCommand((HdmiCecMessage) msg.obj);
-                    break;
-                case MSG_REPORT_LOGICAL_ADDRESS:
-                    int deviceType = msg.arg1;
-                    int logicalAddress = msg.arg2;
-                    AllocateLogicalAddressCallback callback =
-                            (AllocateLogicalAddressCallback) msg.obj;
-                    callback.onAllocated(deviceType, logicalAddress);
-                    break;
-                default:
-                    Slog.i(TAG, "Unsupported message type:" + msg.what);
-                    break;
-            }
-        }
-    }
-
     /**
      * Add a new {@link HdmiCecDeviceInfo}. It returns old device info which has the same
      * logical address as new device info's.
@@ -411,10 +359,18 @@
         return nativeGetVendorId(mNativePtr);
     }
 
+    private void runOnIoThread(Runnable runnable) {
+        mIoHandler.post(runnable);
+    }
+
+    private void runOnServiceThread(Runnable runnable) {
+        mControlHandler.post(runnable);
+    }
+
     private void init(HdmiControlService service, long nativePtr) {
         mService = service;
-        mIoHandler = new IoHandler(service.getServiceLooper());
-        mControlHandler = new ControlHandler(service.getServiceLooper());
+        mIoHandler = new Handler(service.getServiceLooper());
+        mControlHandler = new Handler(service.getServiceLooper());
         mNativePtr = nativePtr;
     }
 
@@ -438,13 +394,27 @@
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
                 (sourceAddress, message.getSource(), message.getOpcode(),
                         HdmiCecMessageBuilder.ABORT_REFUSED);
-        sendCommand(cecMessage);
-
+        sendCommand(cecMessage, null);
     }
 
-    boolean sendCommand(HdmiCecMessage cecMessage) {
-        Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
-        return mIoHandler.sendMessage(message);
+    void sendCommand(final HdmiCecMessage cecMessage,
+            final HdmiControlService.SendMessageCallback callback) {
+        runOnIoThread(new Runnable() {
+            @Override
+            public void run() {
+                byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
+                final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
+                        cecMessage.getDestination(), body);
+                if (callback != null) {
+                    runOnServiceThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onSendCompleted(error);
+                        }
+                    });
+                }
+            }
+        });
     }
 
     /**
@@ -453,12 +423,15 @@
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
         byte opcode = body[0];
         byte params[] = Arrays.copyOfRange(body, 1, body.length);
-        HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+        final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
 
         // Delegate message to main handler so that it handles in main thread.
-        Message message = mControlHandler.obtainMessage(
-                MSG_RECEIVE_CEC_COMMAND, cecMessage);
-        mControlHandler.sendMessage(message);
+        runOnServiceThread(new Runnable() {
+            @Override
+            public void run() {
+                onReceiveCommand(cecMessage);
+            }
+        });
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 09153b9..d7a2c1c6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,30 +18,25 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.IHdmiControlService;
-import android.hardware.hdmi.IHdmiHotplugEventListener;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecDeviceInfo;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.IHdmiControlService;
+import android.hardware.hdmi.IHdmiHotplugEventListener;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.util.SparseArray;
-
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -54,6 +49,20 @@
     // TODO: Rename the permission to HDMI_CONTROL.
     private static final String PERMISSION = "android.permission.HDMI_CEC";
 
+    /**
+     * Interface to report send result.
+     */
+    interface SendMessageCallback {
+        /**
+         * Called when {@link HdmiControlService#sendCecCommand} is completed.
+         *
+         * @param error result of send request. 0 if succeed. Otherwise it will be
+         *        negative value
+         */
+        // TODO: define error code as constants and update javadoc.
+        void onSendCompleted(int error);
+    }
+
     // A thread to handle synchronous IO of CEC and MHL control service.
     // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
     // and sparse call it shares a thread to handle IO operations.
@@ -205,10 +214,14 @@
      * Transmit a CEC command to CEC bus.
      *
      * @param command CEC command to send out
-     * @return {@code true} if succeeds to send command
+     * @param callback interface used to the result of send command
      */
-    boolean sendCecCommand(HdmiCecMessage command) {
-        return mCecController.sendCommand(command);
+    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+        mCecController.sendCommand(command, callback);
+    }
+
+    void sendCecCommand(HdmiCecMessage command) {
+        mCecController.sendCommand(command, null);
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index 0a701f9..db9d28d 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -16,6 +16,7 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiCecMessage;
 import android.util.Slog;
 
 /**
@@ -37,17 +38,24 @@
 
     @Override
     boolean start() {
-        if (sendCommand(
-                HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress, mAvrAddress))) {
-            mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
-            addTimer(mState, TIMEOUT_MS);
-        } else {
-            Slog.w(TAG, "Failed to send <Request ARC Initiation>");
-            // If failed to send <Request ARC Initiation>, start "Disabled" ARC transmission
-            // action.
-            disableArcTransmission();
-            finish();
-        }
+        HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
+                mAvrAddress);
+        sendCommand(command, new HdmiControlService.SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                // success.
+                if (error == 0) {
+                    mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+                    addTimer(mState, TIMEOUT_MS);
+                } else {
+                    Slog.w(TAG, "Failed to send <Request ARC Initiation>");
+                    // If failed to send <Request ARC Initiation>, start "Disabled"
+                    // ARC transmission action.
+                    disableArcTransmission();
+                    finish();
+                }
+            }
+        });
         return true;
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index db1b992..7669f87 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -16,6 +16,7 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiCecMessage;
 import android.util.Slog;
 
 /**
@@ -37,17 +38,23 @@
 
     @Override
     boolean start() {
-        if (sendCommand(
-                HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress))) {
-            mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
-            addTimer(mState, TIMEOUT_MS);
-        } else {
-            Slog.w(TAG, "Failed to send <Request ARC Initiation>");
-            // If failed to send <Request ARC Termination>, start "Disabled" ARC transmission
-            // action.
-            disableArcTransmission();
-            finish();
-        }
+        HdmiCecMessage command =
+                HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
+        sendCommand(command, new HdmiControlService.SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error == 0) {
+                    mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+                    addTimer(mState, TIMEOUT_MS);
+                } else {
+                    Slog.w(TAG, "Failed to send <Request ARC Initiation>");
+                    // If failed to send <Request ARC Termination>, start "Disabled" ARC
+                    // transmission action.
+                    disableArcTransmission();
+                    finish();
+                }
+            }
+        });
         return true;
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 6bf149b..94776a2 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -64,26 +64,7 @@
     @Override
     boolean start() {
         if (mEnabled) {
-            if (sendCommand(
-                    HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress))) {
-                // Enable ARC status immediately after sending <Report Arc Initiated>.
-                // If AVR responds with <Feature Abort>, disable ARC status again.
-                // This is different from spec that says that turns ARC status to "Enabled"
-                // if <Report ARC Initiated> is acknowledged and no <Feature Abort> is received.
-                // But implemented this way to save the time having to wait for <Feature Abort>.
-                setArcStatus(true);
-                // If succeeds to send <Report ARC Initiated>, wait general timeout
-                // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
-                mState = STATE_WAITING_TIMEOUT;
-                addTimer(mState, TIMEOUT_MS);
-            } else {
-                // If fails to send <Report ARC Initiated>, disable ARC and
-                // send <Report ARC Terminated> directly.
-                Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:" + mSourceAddress
-                        + ", avr Address:" + mAvrAddress + "]");
-                setArcStatus(false);
-                finish();
-            }
+            sendReportArcInitiated();
         } else {
             setArcStatus(false);
             finish();
@@ -91,6 +72,38 @@
         return true;
     }
 
+    private void sendReportArcInitiated() {
+        HdmiCecMessage command =
+                HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress);
+        sendCommand(command, new HdmiControlService.SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error == 0) {
+                    // Enable ARC status immediately after sending <Report Arc Initiated>.
+                    // If AVR responds with <Feature Abort>, disable ARC status again.
+                    // This is different from spec that says that turns ARC status to
+                    // "Enabled" if <Report ARC Initiated> is acknowledged and no
+                    // <Feature Abort> is received.
+                    // But implemented this way to save the time having to wait for
+                    // <Feature Abort>.
+                    setArcStatus(true);
+                    // If succeeds to send <Report ARC Initiated>, wait general timeout
+                    // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
+                    mState = STATE_WAITING_TIMEOUT;
+                    addTimer(mState, TIMEOUT_MS);
+                } else {
+                    // If fails to send <Report ARC Initiated>, disable ARC and
+                    // send <Report ARC Terminated> directly.
+                    Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:"
+                            + mSourceAddress
+                            + ", avr Address:" + mAvrAddress + "]");
+                    setArcStatus(false);
+                    finish();
+                }
+            }
+        });
+    }
+
     private void setArcStatus(boolean enabled) {
         boolean wasEnabled = mService.setArcStatus(enabled);
         Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f90d7ab..3ed73f7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -32,6 +32,7 @@
 import android.content.pm.Signature;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.FileBridge;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -114,7 +115,7 @@
     private boolean mPermissionsConfirmed;
     private boolean mInvalid;
 
-    private ArrayList<WritePipe> mPipes = new ArrayList<>();
+    private ArrayList<FileBridge> mBridges = new ArrayList<>();
 
     private IPackageInstallObserver2 mRemoteObserver;
 
@@ -159,14 +160,14 @@
         // Quick sanity check of state, and allocate a pipe for ourselves. We
         // then do heavy disk allocation outside the lock, but this open pipe
         // will block any attempted install transitions.
-        final WritePipe pipe;
+        final FileBridge bridge;
         synchronized (mLock) {
             if (!mMutationsAllowed) {
                 throw new IllegalStateException("Mutations not allowed");
             }
 
-            pipe = new WritePipe();
-            mPipes.add(pipe);
+            bridge = new FileBridge();
+            mBridges.add(bridge);
         }
 
         try {
@@ -194,9 +195,9 @@
                 Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
             }
 
-            pipe.setTargetFd(targetFd);
-            pipe.start();
-            return pipe.getWriteFd();
+            bridge.setTargetFile(targetFd);
+            bridge.start();
+            return new ParcelFileDescriptor(bridge.getClientSocket());
 
         } catch (ErrnoException e) {
             throw new IllegalStateException("Failed to write", e);
@@ -218,8 +219,8 @@
 
         // Verify that all writers are hands-off
         if (mMutationsAllowed) {
-            for (WritePipe pipe : mPipes) {
-                if (!pipe.isClosed()) {
+            for (FileBridge bridge : mBridges) {
+                if (!bridge.isClosed()) {
                     throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED,
                             "Files still open");
                 }
@@ -482,52 +483,6 @@
         }
     }
 
-    private static class WritePipe extends Thread {
-        private final ParcelFileDescriptor[] mPipe;
-
-        private FileDescriptor mTargetFd;
-
-        private volatile boolean mClosed;
-
-        public WritePipe() {
-            try {
-                mPipe = ParcelFileDescriptor.createPipe();
-            } catch (IOException e) {
-                throw new IllegalStateException("Failed to create pipe");
-            }
-        }
-
-        public boolean isClosed() {
-            return mClosed;
-        }
-
-        public void setTargetFd(FileDescriptor targetFd) {
-            mTargetFd = targetFd;
-        }
-
-        public ParcelFileDescriptor getWriteFd() {
-            return mPipe[1];
-        }
-
-        @Override
-        public void run() {
-            FileInputStream in = null;
-            FileOutputStream out = null;
-            try {
-                // TODO: look at switching to sendfile(2) to speed up
-                in = new FileInputStream(mPipe[0].getFileDescriptor());
-                out = new FileOutputStream(mTargetFd);
-                Streams.copy(in, out);
-            } catch (IOException e) {
-                Slog.w(TAG, "Failed to stream data: " + e);
-            } finally {
-                IoUtils.closeQuietly(mPipe[0]);
-                IoUtils.closeQuietly(mTargetFd);
-                mClosed = true;
-            }
-        }
-    }
-
     private class InstallFailedException extends Exception {
         private final int error;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0fa0b14..b06b090 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6140,7 +6140,12 @@
         }
         final String nativeLibraryPath = (new File(libDir, apkName)).getPath();
         pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
-        pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+        // pkgSetting might be null during rescan following uninstall of updates
+        // to a bundled app, so accommodate that possibility.  The settings in
+        // that case will be established later from the parsed package.
+        if (pkgSetting != null) {
+            pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+        }
     }
 
     // Deduces the required ABI of an upgraded system app.
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 986cdc1..1629a614 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -40,6 +40,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -326,8 +327,12 @@
         for (int i = 0; i < mTrustListeners.size(); i++) {
             try {
                 mTrustListeners.get(i).onTrustChanged(enabled, userId);
+            } catch (DeadObjectException e) {
+                if (DEBUG) Slog.d(TAG, "Removing dead TrustListener.");
+                mTrustListeners.remove(i);
+                i--;
             } catch (RemoteException e) {
-                Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
+                Slog.e(TAG, "Exception while notifying TrustListener.", e);
             }
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4e4d127..5395f60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3351,6 +3351,44 @@
     }
 
     @Override
+    public UserHandle createUser(ComponentName who, String name) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+            long id = Binder.clearCallingIdentity();
+            try {
+                UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
+                if (userInfo != null) {
+                    return userInfo.getUserHandle();
+                }
+                return null;
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    @Override
+    public boolean removeUser(ComponentName who, UserHandle userHandle) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+            long id = Binder.clearCallingIdentity();
+            try {
+                return mUserManager.removeUser(userHandle.getIdentifier());
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    @Override
     public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
 
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
new file mode 100644
index 0000000..c439211
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.internal.telecomm;
+
+/**
+ * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
+ * commands that were previously handled by ITelephony.
+ * {@hide}
+ */
+oneway interface ITelecommService {
+
+    /**
+     * Silence the ringer if an incoming call is currently ringing.
+     * (If vibrating, stop the vibrator also.)
+     *
+     * It's safe to call this if the ringer has already been silenced, or
+     * even if there's no incoming call.  (If so, this method will do nothing.)
+     */
+    void silenceRinger();
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5d485c5..4aed1fe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -30,6 +30,7 @@
 import android.telephony.Rlog;
 import android.util.Log;
 
+import com.android.internal.telecomm.ITelecommService;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
@@ -65,6 +66,8 @@
 public class TelephonyManager {
     private static final String TAG = "TelephonyManager";
 
+    private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
     private static ITelephonyRegistry sRegistry;
 
     /**
@@ -1536,6 +1539,10 @@
         return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
     }
 
+    private ITelecommService getTelecommService() {
+        return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+    }
+
     //
     //
     // PhoneStateListener
@@ -2016,9 +2023,9 @@
     @PrivateApi
     public void silenceRinger() {
         try {
-            getITelephony().silenceRinger();
+            getTelecommService().silenceRinger();
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#silenceRinger", e);
+            Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
         }
     }
 
@@ -2249,4 +2256,25 @@
         }
         return false;
     }
+
+    /** @hide */
+    @PrivateApi
+    public void setDataEnabled(boolean enable) {
+        try {
+            getITelephony().setDataEnabled(enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
+        }
+    }
+
+    /** @hide */
+    @PrivateApi
+    public boolean getDataEnabled() {
+        try {
+            return getITelephony().getDataEnabled();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index baacb74..6d7f158 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -436,4 +436,18 @@
      * @return true on success; false on any failure.
      */
     boolean setPreferredNetworkType(int networkType);
+
+    /**
+     * User enable/disable Mobile Data.
+     *
+     * @param enable true to turn on, else false
+     */
+    void setDataEnabled(boolean enable);
+
+    /**
+     * Get the user enabled state of Mobile Data.
+     *
+     * @return true on enabled
+     */
+    boolean getDataEnabled();
 }
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
index 6ad01a0..0f4e122 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
@@ -302,6 +302,36 @@
                             paint.setShader(ResourceModifiers.instance().mVertGradient);
                         }
                     });
+                    put("radGradient", new DisplayModifier() {
+                        @Override
+                        public void modifyDrawing(Paint paint, Canvas canvas) {
+                            paint.setShader(ResourceModifiers.instance().mRadGradient);
+                        }
+                    });
+                    put("sweepGradient", new DisplayModifier() {
+                        @Override
+                        public void modifyDrawing(Paint paint, Canvas canvas) {
+                            paint.setShader(ResourceModifiers.instance().mSweepGradient);
+                        }
+                    });
+                    put("composeShader", new DisplayModifier() {
+                        @Override
+                        public void modifyDrawing(Paint paint, Canvas canvas) {
+                            paint.setShader(ResourceModifiers.instance().mComposeShader);
+                        }
+                    });
+                    put("bad composeShader", new DisplayModifier() {
+                        @Override
+                        public void modifyDrawing(Paint paint, Canvas canvas) {
+                            paint.setShader(ResourceModifiers.instance().mBadComposeShader);
+                        }
+                    });
+                    put("bad composeShader 2", new DisplayModifier() {
+                        @Override
+                        public void modifyDrawing(Paint paint, Canvas canvas) {
+                            paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader);
+                        }
+                    });
                 }
             });
 
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
index c705443..d522481 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
@@ -23,7 +23,11 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapShader;
 import android.graphics.Color;
+import android.graphics.ComposeShader;
 import android.graphics.LinearGradient;
+import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
+import android.graphics.SweepGradient;
 import android.graphics.Matrix;
 import android.graphics.Shader;
 
@@ -38,6 +42,11 @@
         public final LinearGradient mHorGradient;
         public final LinearGradient mDiagGradient;
         public final LinearGradient mVertGradient;
+        public final RadialGradient mRadGradient;
+        public final SweepGradient mSweepGradient;
+        public final ComposeShader mComposeShader;
+        public final ComposeShader mBadComposeShader;
+        public final ComposeShader mAnotherBadComposeShader;
         public final Bitmap mBitmap;
         private final Matrix mMtx1;
         private final Matrix mMtx2;
@@ -90,6 +99,12 @@
             mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
                     Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
 
+            mSweepGradient = new SweepGradient(mDrawWidth / 2.0f, mDrawHeight / 2.0f,
+                    Color.YELLOW, Color.MAGENTA);
+
+            mComposeShader = new ComposeShader(mRepeatShader, mHorGradient,
+                    PorterDuff.Mode.MULTIPLY);
+
             final float width = mBitmap.getWidth() / 8.0f;
             final float height = mBitmap.getHeight() / 8.0f;
 
@@ -106,6 +121,16 @@
                 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00,
                 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000,
             };
+
+            // Use a repeating gradient with many colors to test the non simple case.
+            mRadGradient = new RadialGradient(mDrawWidth / 4.0f, mDrawHeight / 4.0f, 4.0f,
+                    mBitmapColors, null, Shader.TileMode.REPEAT);
+
+            mBadComposeShader = new ComposeShader(mRadGradient, mComposeShader,
+                    PorterDuff.Mode.MULTIPLY);
+
+            mAnotherBadComposeShader = new ComposeShader(mRadGradient, mVertGradient,
+                    PorterDuff.Mode.MULTIPLY);
         }
 
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index bafc71e..1157de7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -509,8 +509,6 @@
      * @hide
      */
     public boolean isValid() {
-        if (SSID == null)
-            return false;
 
         if (allowedKeyManagement == null)
             return false;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 1484d49..7debb93 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -224,9 +224,9 @@
         public static final int TTLS    = 2;
         /** EAP-Password */
         public static final int PWD     = 3;
-        /** EAP-Subscriber Identity Module */
+        /** EAP-Subscriber Identity Module {@hide} */
         public static final int SIM     = 4;
-        /** EAP-Authentication and Key Agreement */
+        /** EAP-Authentication and Key Agreement {@hide} */
         public static final int AKA     = 5;
         /** @hide */
         public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" };
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 08b430f..9a633b4 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -25,9 +25,13 @@
 import java.util.Iterator;
 import java.util.Map;
 
+/**
+ * A class representing a Wi-Fi Passpoint credential.
+ */
 public class WifiPasspointCredential implements Parcelable {
 
     private final static String TAG = "PasspointCredential";
+    private String mWifiTreePath;
     private String mWifiSPFQDN;
     private String mCredentialName;
     private String mUpdateIdentifier;
@@ -88,10 +92,11 @@
     /**
      * Constructor
      * @param realm Realm of the passpoint credential
-     * @param config Credential information, must be either EAP-TLS or EAP-TTLS.
+     * @param fqdn Fully qualified domain name (FQDN) of the credential
+     * @param config Credential information, must be either EAP-TLS or EAP-TTLS
      * @see WifiEnterpriseConfig
      */
-    public WifiPasspointCredential(String realm, WifiEnterpriseConfig config) {
+    public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) {
         mRealm = realm;
         switch (config.getEapMethod()) {
             case WifiEnterpriseConfig.Eap.TLS:
@@ -325,10 +330,7 @@
         return mPasswd;
     }
 
-    /**
-     * Get the IMSI of this Passpoint credential, for EAP-SIM / EAP-AKA only.
-     * @return IMSI
-     */
+    /** @hide */
     public String getImsi() {
         return mImsi;
     }
@@ -343,8 +345,11 @@
         return mMnc;
     }
 
-    /** @hide */
-    public String getCaRootCert() {
+    /**
+     * Get the CA root certificate path of this Passpoint credential.
+     * @return CA root certificate path
+     */
+    public String getCaRootCertPath() {
         return mCaRootCert;
     }
 
@@ -357,13 +362,29 @@
     }
 
     /**
-     * Get the realm of this Passpoint credential, for all EAP methods.
+     * Set credential information of this Passpoint credential.
+     * @param config Credential information, must be either EAP-TLS or EAP-TTLS
+     */
+    public void setCredential(WifiEnterpriseConfig config) {
+        // TODO
+    }
+
+    /**
+     * Get the realm of this Passpoint credential.
      * @return Realm
      */
     public String getRealm() {
         return mRealm;
     }
 
+    /**
+     * Set the ream of this Passpoint credential.
+     * @param realm Realm
+     */
+    public void setRealm(String realm) {
+        mRealm = realm;
+    }
+
     /** @hide */
     public int getPriority() {
         if (mUserPreferred) {
@@ -374,14 +395,22 @@
     }
 
     /**
-     * Get the fully qualified domain name (FQDN) of this Passpoint credential,
-     * for all EAP methods.
+     * Get the fully qualified domain name (FQDN) of this Passpoint credential.
      * @return FQDN
      */
     public String getFqdn() {
         return mHomeSpFqdn;
     }
 
+    /**
+     * Set the fully qualified domain name (FQDN) of this Passpoint credential.
+     * @param fqdn FQDN
+     */
+    public void setFqdn(String fqdn) {
+        mHomeSpFqdn = fqdn;
+    }
+
+
     /** @hide */
     public String getOtherhomepartners() {
         return mOtherhomepartnerFqdn;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ee4dc5a..ebaa8c9 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -443,10 +443,9 @@
         return null;
     }
 
-    /* TODO: add credential APIs */
-
     /**
-     * Give a list of all saved Passpoint credentials.
+     * Get a list of saved Passpoint credentials. Only those credentials owned
+     * by the caller will be returned.
      *
      * @return The list of credentials
      */