Merge "Add SendMessageCallback to Hdmi control service."
diff --git a/Android.mk b/Android.mk
index d165595..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 \
@@ -377,7 +378,54 @@
 LOCAL_DX_FLAGS := --core-library
 
 # Packages to include, use \* wildcard to include descendants.
-LOCAL_JAR_PACKAGES := android\*
+LOCAL_JAR_PACKAGES := \
+	android \
+	android.accessibilityservice\* \
+	android.accounts\* \
+	android.alsa\* \
+	android.animation\* \
+	android.annotation\* \
+	android.app\* \
+	android.appwidget\* \
+	android.bluetooth\* \
+	android.content\* \
+	android.content\* \
+	android.database\* \
+	android.ddm\* \
+	android.drm\* \
+	android.emoji\* \
+	android.filterfw\* \
+	android.filterpacks\* \
+	android.gesture\* \
+	android.graphics\* \
+	android.inputmethodservice\* \
+	android.location\* \
+	android.media\* \
+	android.mtp\* \
+	android.net\* \
+	android.nfc\* \
+	android.opengl\* \
+	android.os\* \
+	android.preference\* \
+	android.print\* \
+	android.printservice\* \
+	android.provider\* \
+	android.renderscript\* \
+	android.sax\* \
+	android.security\* \
+	android.service\* \
+	android.speech\* \
+	android.system\* \
+	android.telecomm\* \
+	android.telephony\* \
+	android.test\* \
+	android.text\* \
+	android.transition\* \
+	android.tv\* \
+	android.util\* \
+	android.view\* \
+	android.webkit\* \
+	android.widget\*
 
 # List of classes and interfaces which should be loaded by the Zygote.
 LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
@@ -396,7 +444,15 @@
 LOCAL_DX_FLAGS := --core-library
 
 # Packages to include, use \* wildcard to include descendants.
-LOCAL_JAR_PACKAGES := com\* javax\*
+# 'android' is required to be able to include 'android.hardware',
+# however, it causes classes in 'android' to be part of framework
+# and framework2.
+# TODO: Finer grained matching.
+LOCAL_JAR_PACKAGES := \
+	android \
+	android.hardware\* \
+	com\* \
+	javax\*
 
 include $(BUILD_JAVA_LIBRARY)
 framework2_module := $(LOCAL_INSTALLED_MODULE)
diff --git a/api/current.txt b/api/current.txt
index c809379..9de395b 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 {
@@ -11203,7 +11185,6 @@
     method public final android.graphics.Paint getPaint();
     method public android.graphics.Shader.TileMode getTileModeX();
     method public android.graphics.Shader.TileMode getTileModeY();
-    method public android.content.res.ColorStateList getTint();
     method public boolean hasAntiAlias();
     method public boolean hasMipMap();
     method public final boolean isAutoMirrored();
@@ -11218,8 +11199,6 @@
     method public void setTileModeX(android.graphics.Shader.TileMode);
     method public void setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
     method public final void setTileModeY(android.graphics.Shader.TileMode);
-    method public void setTint(android.content.res.ColorStateList);
-    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class ClipDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -11272,6 +11251,7 @@
     method public android.graphics.ColorFilter getColorFilter();
     method public android.graphics.drawable.Drawable.ConstantState getConstantState();
     method public android.graphics.drawable.Drawable getCurrent();
+    method public android.graphics.Rect getDirtyBounds();
     method public int getIntrinsicHeight();
     method public int getIntrinsicWidth();
     method public final int getLevel();
@@ -11309,6 +11289,7 @@
     method public void setHotspotBounds(int, int, int, int);
     method public final boolean setLevel(int);
     method public boolean setState(int[]);
+    method public void setTint(android.content.res.ColorStateList, android.graphics.PorterDuff.Mode);
     method public boolean setVisible(boolean, boolean);
     method public void unscheduleSelf(java.lang.Runnable);
   }
@@ -11464,14 +11445,11 @@
     method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public android.graphics.Paint getPaint();
-    method public android.content.res.ColorStateList getTint();
     method public void setAlpha(int);
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void setTargetDensity(android.graphics.Canvas);
     method public void setTargetDensity(android.util.DisplayMetrics);
     method public void setTargetDensity(int);
-    method public void setTint(android.content.res.ColorStateList);
-    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PaintDrawable extends android.graphics.drawable.ShapeDrawable {
@@ -11492,10 +11470,6 @@
   }
 
   public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
-    method public android.graphics.Rect getDirtyBounds();
-    method public android.content.res.ColorStateList getTint();
-    method public void setTint(android.content.res.ColorStateList);
-    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -11960,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();
@@ -12142,6 +12115,35 @@
     field public static final int CAMERA_ERROR = 3; // 0x3
   }
 
+  public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
+    ctor public CameraCaptureSession();
+    method public abstract void abortCaptures() throws android.hardware.camera2.CameraAccessException;
+    method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void close();
+    method public abstract android.hardware.camera2.CameraDevice getDevice();
+    method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+  }
+
+  public static abstract class CameraCaptureSession.CaptureListener {
+    ctor public CameraCaptureSession.CaptureListener();
+    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
+    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, int);
+    method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
+  }
+
+  public static abstract class CameraCaptureSession.StateListener {
+    ctor public CameraCaptureSession.StateListener();
+    method public void onActive(android.hardware.camera2.CameraCaptureSession);
+    method public void onClosed(android.hardware.camera2.CameraCaptureSession);
+    method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
+    method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
+    method public void onReady(android.hardware.camera2.CameraCaptureSession);
+  }
+
   public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata {
     method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
     method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureRequestKeys();
@@ -12209,16 +12211,17 @@
   }
 
   public abstract interface CameraDevice implements java.lang.AutoCloseable {
-    method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
-    method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated void flush() throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
-    method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated void stopRepeating() throws android.hardware.camera2.CameraAccessException;
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
     field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -12227,7 +12230,7 @@
     field public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5; // 0x5
   }
 
-  public static abstract class CameraDevice.CaptureListener {
+  public static abstract deprecated class CameraDevice.CaptureListener {
     ctor public CameraDevice.CaptureListener();
     method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
     method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
@@ -12237,14 +12240,14 @@
 
   public static abstract class CameraDevice.StateListener {
     ctor public CameraDevice.StateListener();
-    method public void onActive(android.hardware.camera2.CameraDevice);
-    method public void onBusy(android.hardware.camera2.CameraDevice);
+    method public deprecated void onActive(android.hardware.camera2.CameraDevice);
+    method public deprecated void onBusy(android.hardware.camera2.CameraDevice);
     method public void onClosed(android.hardware.camera2.CameraDevice);
     method public abstract void onDisconnected(android.hardware.camera2.CameraDevice);
     method public abstract void onError(android.hardware.camera2.CameraDevice, int);
-    method public void onIdle(android.hardware.camera2.CameraDevice);
+    method public deprecated void onIdle(android.hardware.camera2.CameraDevice);
     method public abstract void onOpened(android.hardware.camera2.CameraDevice);
-    method public void onUnconfigured(android.hardware.camera2.CameraDevice);
+    method public deprecated void onUnconfigured(android.hardware.camera2.CameraDevice);
     field public static final int ERROR_CAMERA_DEVICE = 4; // 0x4
     field public static final int ERROR_CAMERA_DISABLED = 3; // 0x3
     field public static final int ERROR_CAMERA_IN_USE = 1; // 0x1
@@ -25034,7 +25037,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";
@@ -25072,10 +25077,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";
@@ -32877,6 +32884,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>);
@@ -36570,6 +36578,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/Camera.java b/core/java/android/hardware/Camera.java
index 35c86e7..0705e0c 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -169,6 +169,10 @@
     private boolean mFaceDetectionRunning = false;
     private Object mAutoFocusCallbackLock = new Object();
 
+    private static final int NO_ERROR = 0;
+    private static final int EACCESS = -13;
+    private static final int ENODEV = -19;
+
     /**
      * Broadcast Action:  A new picture is taken by the camera, and the entry of
      * the picture has been added to the media store.
@@ -328,6 +332,24 @@
     }
 
     Camera(int cameraId) {
+        int err = cameraInit(cameraId);
+        if (checkInitErrors(err)) {
+            switch(err) {
+                case EACCESS:
+                    throw new RuntimeException("Fail to connect to camera service");
+                case ENODEV:
+                    throw new RuntimeException("Camera initialization failed");
+                default:
+                    // Should never hit this.
+                    throw new RuntimeException("Unknown camera error");
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public int cameraInit(int cameraId) {
         mShutterCallback = null;
         mRawImageCallback = null;
         mJpegCallback = null;
@@ -347,7 +369,21 @@
 
         String packageName = ActivityThread.currentPackageName();
 
-        native_setup(new WeakReference<Camera>(this), cameraId, packageName);
+        return native_setup(new WeakReference<Camera>(this), cameraId, packageName);
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean checkInitErrors(int err) {
+        return err != NO_ERROR;
+    }
+
+    /**
+     * @hide
+     */
+    public static Camera openUninitialized() {
+        return new Camera();
     }
 
     /**
@@ -360,7 +396,7 @@
         release();
     }
 
-    private native final void native_setup(Object camera_this, int cameraId,
+    private native final int native_setup(Object camera_this, int cameraId,
                                            String packageName);
 
     private native final void native_release();
@@ -458,13 +494,16 @@
      */
     public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
         if (holder != null) {
-            setPreviewDisplay(holder.getSurface());
+            setPreviewSurface(holder.getSurface());
         } else {
-            setPreviewDisplay((Surface)null);
+            setPreviewSurface((Surface)null);
         }
     }
 
-    private native final void setPreviewDisplay(Surface surface) throws IOException;
+    /**
+     * @hide
+     */
+    public native final void setPreviewSurface(Surface surface) throws IOException;
 
     /**
      * Sets the {@link SurfaceTexture} to be used for live preview.
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/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
new file mode 100644
index 0000000..8391209
--- /dev/null
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -0,0 +1,588 @@
+/*
+ * 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.hardware.camera2;
+
+import android.os.Handler;
+import java.util.List;
+
+/**
+ * A configured capture session for a {@link CameraDevice}, used for capturing
+ * images from the camera.
+ *
+ * <p>A CameraCaptureSession is created by providing a set of target output surfaces to
+ * {@link CameraDevice#createCaptureSession createCaptureSession}. Once created, the session is
+ * active until a new session is created by the camera device, or the camera device is closed.</p>
+ *
+ * <p>Creating a session is an expensive operation and can take several hundred milliseconds, since
+ * it requires configuring the camera device's internal pipelines and allocating memory buffers for
+ * sending images to the desired targets. While
+ * {@link CameraDevice#createCaptureSession createCaptureSession} will provide a
+ * CameraCaptureSession object immediately, configuration won't be complete until the
+ * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback is called for the
+ * first time. If configuration cannot be completed, then the
+ * {@link CameraCaptureSession.StateListener#onConfigureFailed onConfigureFailed} is called, and the
+ * session will not become active.</p>
+ *
+ * <p>Any capture requests (repeating or non-repeating) submitted before the session is ready will
+ * be queued up and will begin capture once the session becomes ready. In case the session cannot be
+ * configured and {@link StateListener#onConfigureFailed onConfigureFailed} is called, all queued
+ * capture requests are discarded.</p>
+ *
+ * <p>If a new session is created by the camera device, then the previous session is closed, and its
+ * associated {@link StateListener#onClosed onClosed} callback will be invoked.  All
+ * of the session methods will throw an IllegalStateException if called once the session is
+ * closed.</p>
+ *
+ * <p>A closed session clears any repeating requests (as if {@link #stopRepeating} had been called),
+ * but will still complete all of its in-progress capture requests as normal, before a newly
+ * created session takes over and reconfigures the camera device.</p>
+ */
+public abstract class CameraCaptureSession implements AutoCloseable {
+
+    /**
+     * Get the camera device that this session is created for
+     */
+    public abstract CameraDevice getDevice();
+
+    /**
+     * <p>Submit a request for an image to be captured by the camera device.</p>
+     *
+     * <p>The request defines all the parameters for capturing the single image,
+     * including sensor, lens, flash, and post-processing settings.</p>
+     *
+     * <p>Each request will produce one {@link CaptureResult} and produce new frames for one or more
+     * target Surfaces, set with the CaptureRequest builder's
+     * {@link CaptureRequest.Builder#addTarget} method. The target surfaces (set with
+     * {@link CaptureRequest.Builder#addTarget}) must be a subset of the surfaces provided when this
+     * capture session was created.</p>
+     *
+     * <p>Multiple requests can be in progress at once. They are processed in
+     * first-in, first-out order, with minimal delays between each
+     * capture. Requests submitted through this method have higher priority than
+     * those submitted through {@link #setRepeatingRequest} or
+     * {@link #setRepeatingBurst}, and will be processed as soon as the current
+     * repeat/repeatBurst processing completes.</p>
+     *
+     * @param request the settings for this capture
+     * @param listener The callback object to notify once this request has been
+     * processed. If null, no metadata will be produced for this capture,
+     * although image data will still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException if the request targets Surfaces that are not configured as
+     *                                  outputs for this session. Or if the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper.
+     *
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     */
+    public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * Submit a list of requests to be captured in sequence as a burst. The
+     * burst will be captured in the minimum amount of time possible, and will
+     * not be interleaved with requests submitted by other capture or repeat
+     * calls.
+     *
+     * <p>The requests will be captured in order, each capture producing one {@link CaptureResult}
+     * and image buffers for one or more target {@link android.view.Surface surfaces}. The target
+     * surfaces (set with {@link CaptureRequest.Builder#addTarget}) must be a subset of the surfaces
+     * provided when this capture session was created.</p>
+     *
+     * <p>The main difference between this method and simply calling
+     * {@link #capture} repeatedly is that this method guarantees that no
+     * other requests will be interspersed with the burst.</p>
+     *
+     * @param requests the list of settings for this burst capture
+     * @param listener The callback object to notify each time one of the
+     * requests in the burst has been processed. If null, no metadata will be
+     * produced for any requests in this burst, although image data will still
+     * be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests target Surfaces not currently configured as
+     *                                  outputs. Or if the handler is null, the listener is not
+     *                                  null, and the calling thread has no looper.
+     *
+     * @see #capture
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     */
+    public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
+
+    /**
+     * Request endlessly repeating capture of images by this capture session.
+     *
+     * <p>With this method, the camera device will continually capture images
+     * using the settings in the provided {@link CaptureRequest}, at the maximum
+     * rate possible.</p>
+     *
+     * <p>Repeating requests are a simple way for an application to maintain a
+     * preview or other continuous stream of frames, without having to
+     * continually submit identical requests through {@link #capture}.</p>
+     *
+     * <p>Repeat requests have lower priority than those submitted
+     * through {@link #capture} or {@link #captureBurst}, so if
+     * {@link #capture} is called when a repeating request is active, the
+     * capture request will be processed before any further repeating
+     * requests are processed.<p>
+     *
+     * <p>Repeating requests are a simple way for an application to maintain a
+     * preview or other continuous stream of frames, without having to submit
+     * requests through {@link #capture} at video rates.</p>
+     *
+     * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
+     * {@link #abortCaptures} will also clear the request.</p>
+     *
+     * <p>Calling this method will replace any earlier repeating request or
+     * burst set up by this method or {@link #setRepeatingBurst}, although any
+     * in-progress burst will be completed before the new repeat request will be
+     * used.</p>
+     *
+     * @param request the request to repeat indefinitely
+     * @param listener The callback object to notify every time the
+     * request finishes processing. If null, no metadata will be
+     * produced for this stream of requests, although image data will
+     * still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests reference Surfaces that are not currently
+     *                                  configured as outputs. Or if the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingBurst
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
+
+    /**
+     * <p>Request endlessly repeating capture of a sequence of images by this
+     * capture session.</p>
+     *
+     * <p>With this method, the camera device will continually capture images,
+     * cycling through the settings in the provided list of
+     * {@link CaptureRequest CaptureRequests}, at the maximum rate possible.</p>
+     *
+     * <p>If a request is submitted through {@link #capture} or
+     * {@link #captureBurst}, the current repetition of the request list will be
+     * completed before the higher-priority request is handled. This guarantees
+     * that the application always receives a complete repeat burst captured in
+     * minimal time, instead of bursts interleaved with higher-priority
+     * captures, or incomplete captures.</p>
+     *
+     * <p>Repeating burst requests are a simple way for an application to
+     * maintain a preview or other continuous stream of frames where each
+     * request is different in a predicatable way, without having to continually
+     * submit requests through {@link #captureBurst}.</p>
+     *
+     * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
+     * ongoing burst will still be completed, however. Calling
+     * {@link #abortCaptures} will also clear the request.</p>
+     *
+     * <p>Calling this method will replace a previously-set repeating request or
+     * burst set up by this method or {@link #setRepeatingRequest}, although any
+     * in-progress burst will be completed before the new repeat burst will be
+     * used.</p>
+     *
+     * @param requests the list of requests to cycle through indefinitely
+     * @param listener The callback object to notify each time one of the
+     * requests in the repeating bursts has finished processing. If null, no
+     * metadata will be produced for this stream of requests, although image
+     * data will still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests reference Surfaces not currently configured
+     *                                  as outputs. Or if the handler is null, the listener is not
+     *                                  null, and the calling thread has no looper.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
+
+    /**
+     * <p>Cancel any ongoing repeating capture set by either
+     * {@link #setRepeatingRequest setRepeatingRequest} or
+     * {@link #setRepeatingBurst}. Has no effect on requests submitted through
+     * {@link #capture capture} or {@link #captureBurst captureBurst}.</p>
+     *
+     * <p>Any currently in-flight captures will still complete, as will any burst that is
+     * mid-capture. To ensure that the device has finished processing all of its capture requests
+     * and is in ready state, wait for the {@link StateListener#onReady} callback after
+     * calling this method.</p>
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     *
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see StateListener#onIdle
+     */
+    public abstract void stopRepeating() throws CameraAccessException;
+
+    /**
+     * Discard all captures currently pending and in-progress as fast as possible.
+     *
+     * <p>The camera device will discard all of its current work as fast as possible. Some in-flight
+     * captures may complete successfully and call {@link CaptureListener#onCaptureCompleted}, while
+     * others will trigger their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating
+     * request or a repeating burst is set, it will be cleared.</p>
+     *
+     * <p>This method is the fastest way to switch the camera device to a new session with
+     * {@link CameraDevice#createCaptureSession}, at the cost of discarding in-progress work. It
+     * must be called before the new session is created. Once all pending requests are either
+     * completed or thrown away, the {@link StateListener#onReady} callback will be called,
+     * if the session has not been closed. Otherwise, the {@link StateListener#onClosed}
+     * callback will be fired when a new session is created by the camera device.</p>
+     *
+     * <p>Cancelling will introduce at least a brief pause in the stream of data from the camera
+     * device, since once the camera device is emptied, the first new request has to make it through
+     * the entire camera pipeline before new output buffers are produced.</p>
+     *
+     * <p>This means that using {@code abortCaptures()} to simply remove pending requests is not
+     * recommended; it's best used for quickly switching output configurations, or for cancelling
+     * long in-progress requests (such as a multi-second capture).</p>
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     *
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see #configureOutputs
+     */
+    public abstract void abortCaptures() throws CameraAccessException;
+
+    /**
+     * Close this capture session asynchronously.
+     *
+     * <p>Closing a session frees up the target output Surfaces of the session for reuse with either a
+     * new session, or to other APIs that can draw to Surfaces.</p>
+     *
+     * <p>Note that creating a new capture session with {@link CameraDevice#createCaptureSession}
+     * will close any existing capture session automatically, and call the older session listener's
+     * {@link StateListener#onClosed} callback. Using {@link CameraDevice#createCaptureSession}
+     * directly without closing is the recommended approach for quickly switching to a new session,
+     * since unchanged target outputs can be reused more efficiently.</p>
+     *
+     * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and any
+     * repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
+     * However, any in-progress capture requests submitted to the session will be completed as
+     * normal; once all captures have completed and the session has been torn down,
+     * {@link StateListener#onClosed} will be called.</p>
+     */
+    @Override
+    public abstract void close();
+
+    /**
+     * A listener for tracking the state of a camera capture session.
+     *
+     */
+    public static abstract class StateListener {
+
+        /**
+         * This method is called when the camera device has finished configuring itself, and the
+         * session can start processing capture requests.
+         *
+         * <p>If there are capture requests already queued with the session, they will start
+         * processing once this callback is invoked, and the session will call {@link #onActive}
+         * right after this callback is invoked.</p>
+         *
+         * <p>If no capture requests have been submitted, then the session will invoke
+         * {@link #onReady} right after this callback.</p>
+         *
+         * <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
+         * be invoked instead of this callback.</p>
+         *
+         */
+        public abstract void onConfigured(CameraCaptureSession session);
+
+        /**
+         * This method is called if the session cannot be configured as requested.
+         *
+         * <p>This can happen if the set of requested outputs contains unsupported sizes,
+         * or too many outputs are requested at once.</p>
+         *
+         * <p>The session is considered to be closed, and all methods called on it after this
+         * callback is invoked will throw an IllegalStateException. Any capture requests submitted
+         * to the session prior to this callback will be discarded and will not produce any
+         * callbacks on their listeners.</p>
+         */
+        public abstract void onConfigureFailed(CameraCaptureSession session);
+
+        /**
+         * This method is called every time the session has no more capture requests to process.
+         *
+         * <p>During the creation of a new session, this callback is invoked right after
+         * {@link #onConfigured} if no capture requests were submitted to the session prior to it
+         * completing configuration.</p>
+         *
+         * <p>Otherwise, this callback will be invoked any time the session finishes processing
+         * all of its active capture requests, and no repeating request or burst is set up.</p>
+         *
+         */
+        public void onReady(CameraCaptureSession session) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when the session starts actively processing capture requests.
+         *
+         * <p>If capture requests are submitted prior to {@link #onConfigured} being called,
+         * then the session will start processing those requests immediately after the callback,
+         * and this method will be immediately called after {@link #onConfigured}.
+         *
+         * <p>If the session runs out of capture requests to process and calls {@link #onReady},
+         * then this callback will be invoked again once new requests are submitted for capture.</p>
+         */
+        public void onActive(CameraCaptureSession session) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when the session is closed.
+         *
+         * <p>A session is closed when a new session is created by the parent camera device,
+         * or when the parent camera device is closed (either by the user closing the device,
+         * or due to a camera device disconnection or fatal error).</p>
+         *
+         * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and
+         * any repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
+         * However, any in-progress capture requests submitted to the session will be completed
+         * as normal.</p>
+         */
+        public void onClosed(CameraCaptureSession session) {
+            // default empty implementation
+        }
+    }
+
+    /**
+     * <p>A listener for tracking the progress of a {@link CaptureRequest}
+     * submitted to the camera device.</p>
+     *
+     * <p>This listener is called when a request triggers a capture to start,
+     * and when the capture is complete. In case on an error capturing an image,
+     * the error method is triggered instead of the completion method.</p>
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     */
+    public static abstract class CaptureListener {
+
+        /**
+         * This constant is used to indicate that no images were captured for
+         * the request.
+         *
+         * @hide
+         */
+        public static final int NO_FRAMES_CAPTURED = -1;
+
+        /**
+         * This method is called when the camera device has started capturing
+         * the output image for the request, at the beginning of image exposure.
+         *
+         * <p>This callback is invoked right as the capture of a frame begins,
+         * so it is the most appropriate time for playing a shutter sound,
+         * or triggering UI indicators of capture.</p>
+         *
+         * <p>The request that is being used for this capture is provided, along
+         * with the actual timestamp for the start of exposure. This timestamp
+         * matches the timestamp that will be included in
+         * {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field},
+         * and in the buffers sent to each output Surface. These buffer
+         * timestamps are accessible through, for example,
+         * {@link android.media.Image#getTimestamp() Image.getTimestamp()} or
+         * {@link android.graphics.SurfaceTexture#getTimestamp()}.</p>
+         *
+         * <p>For the simplest way to play a shutter sound camera shutter or a
+         * video recording start/stop sound, see the
+         * {@link android.media.MediaActionSound} class.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera the CameraDevice sending the callback
+         * @param request the request for the capture that just begun
+         * @param timestamp the timestamp at start of capture, in nanoseconds.
+         *
+         * @see android.media.MediaActionSound
+         */
+        public void onCaptureStarted(CameraDevice camera,
+                CaptureRequest request, long timestamp) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when some results from an image capture are
+         * available.
+         *
+         * <p>The result provided here will contain some subset of the fields of
+         * a full result. Multiple onCapturePartial calls may happen per
+         * capture; a given result field will only be present in one partial
+         * capture at most. The final onCaptureCompleted call will always
+         * contain all the fields, whether onCapturePartial was called or
+         * not.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera The CameraDevice sending the callback.
+         * @param request The request that was given to the CameraDevice
+         * @param result The partial output metadata from the capture, which
+         * includes a subset of the CaptureResult fields.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         *
+         * @hide
+         */
+        public void onCapturePartial(CameraDevice camera,
+                CaptureRequest request, CaptureResult result) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has completed and the
+         * result metadata is available.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera The CameraDevice sending the callback.
+         * @param request The request that was given to the CameraDevice
+         * @param result The output metadata from the capture, including the
+         * final capture parameters and the state of the camera system during
+         * capture.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureCompleted(CameraDevice camera,
+                CaptureRequest request, CaptureResult result) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called instead of {@link #onCaptureCompleted} when the
+         * camera device failed to produce a {@link CaptureResult} for the
+         * request.
+         *
+         * <p>Other requests are unaffected, and some or all image buffers from
+         * the capture may have been pushed to their respective output
+         * streams.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param request
+         *            The request that was given to the CameraDevice
+         * @param failure
+         *            The output failure from the capture, including the failure reason
+         *            and the frame number.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureFailed(CameraDevice camera,
+                CaptureRequest request, CaptureFailure failure) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called independently of the others in CaptureListener,
+         * when a capture sequence finishes and all {@link CaptureResult}
+         * or {@link CaptureFailure} for it have been returned via this listener.
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param sequenceId
+         *            A sequence ID returned by the {@link #capture} family of functions.
+         * @param lastFrameNumber
+         *            The last frame number (returned by {@link CaptureResult#getFrameNumber}
+         *            or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
+         *            The last frame number may be equal to NO_FRAMES_CAPTURED if no images
+         *            were captured for this sequence. This can happen, for example, when a
+         *            repeating request or burst is cleared right after being set.
+         *
+         * @see CaptureResult#getFrameNumber()
+         * @see CaptureFailure#getFrameNumber()
+         * @see CaptureResult#getSequenceId()
+         * @see CaptureFailure#getSequenceId()
+         */
+        public void onCaptureSequenceCompleted(CameraDevice camera,
+                int sequenceId, int lastFrameNumber) {
+            // default empty implementation
+        }
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ca03dae..77640d1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -242,10 +242,126 @@
      * @see StreamConfigurationMap#getOutputFormats()
      * @see StreamConfigurationMap#getOutputSizes(int)
      * @see StreamConfigurationMap#getOutputSizes(Class)
+     * @deprecated Use {@link #createCaptureSession} instead
      */
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
 
     /**
+     * <p>Create a new camera capture session by providing the target output set of Surfaces to the
+     * camera device.</p>
+     *
+     * <p>The active capture session determines the set of potential output Surfaces for
+     * the camera device for each capture request. A given request may use all
+     * or a only some of the outputs. Once the CameraCaptureSession is created, requests can be
+     * can be submitted with {@link CameraCaptureSession#capture capture},
+     * {@link CameraCaptureSession#captureBurst captureBurst},
+     * {@link CameraCaptureSession#setRepeatingRequest setRepeatingRequest}, or
+     * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</p>
+     *
+     * <p>Surfaces suitable for inclusion as a camera output can be created for
+     * various use cases and targets:</p>
+     *
+     * <ul>
+     *
+     * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Set the size of the
+     *   Surface with {@link android.view.SurfaceHolder#setFixedSize} to be one of the sizes
+     *   returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceView.class)}
+     *   and then obtain the Surface by calling {@link android.view.SurfaceHolder#getSurface}.</li>
+     *
+     * <li>For accessing through an OpenGL texture via a
+     *   {@link android.graphics.SurfaceTexture SurfaceTexture}: Set the size of
+     *   the SurfaceTexture with
+     *   {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one
+     *   of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceTexture.class)}
+     *   before creating a Surface from the SurfaceTexture with
+     *   {@link Surface#Surface}.</li>
+     *
+     * <li>For recording with {@link android.media.MediaCodec}: Call
+     *   {@link android.media.MediaCodec#createInputSurface} after configuring
+     *   the media codec to use one of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(MediaCodec.class)}
+     *   </li>
+     *
+     * <li>For recording with {@link android.media.MediaRecorder}: Call
+     *   {@link android.media.MediaRecorder#getSurface} after configuring the media recorder to use
+     *   one of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(MediaRecorder.class)},
+     *   or configuring it to use one of the supported
+     *   {@link android.media.CamcorderProfile CamcorderProfiles}.</li>
+     *
+     * <li>For efficient YUV processing with {@link android.renderscript}:
+     *   Create a RenderScript
+     *   {@link android.renderscript.Allocation Allocation} with a supported YUV
+     *   type, the IO_INPUT flag, and one of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(Allocation.class)},
+     *   Then obtain the Surface with
+     *   {@link android.renderscript.Allocation#getSurface}.</li>
+     *
+     * <li>For access to raw, uncompressed or JPEG data in the application: Create a
+     *   {@link android.media.ImageReader} object with the one of the supported
+     *   {@link StreamConfigurationMap#getOutputFormats() output image formats}, and a
+     *   size from the supported
+     *   {@link StreamConfigurationMap#getOutputSizes(int) sizes for that format}. Then obtain
+     *   a Surface from it with {@link android.media.ImageReader#getSurface}.</li>
+     *
+     * </ul>
+     *
+     * </p>
+     *
+     * <p>The camera device will query each Surface's size and formats upon this
+     * call, so they must be set to a valid setting at this time (in particular:
+     * if the format is user-visible, it must be one of
+     * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of
+     * {@link StreamConfigurationMap#getOutputSizes(int)}).</p>
+     *
+     * <p>It can take several hundred milliseconds for the session's configuration to complete,
+     * since camera hardware may need to be powered on or reconfigured. Once the configuration is
+     * complete and the session is ready to actually capture data, the provided
+     * {@link CameraCaptureSession.StateListener}'s
+     * {@link CameraCaptureSession.StateListener#onConfigured} callback will be called.</p>
+     *
+     * <p>If a prior CameraCaptureSession already exists when a new one is created, the previous
+     * session is closed. Any in-progress capture requests made on the prior session will be
+     * completed before the new session is configured and is able to start capturing its own
+     * requests. To minimize the transition time, the {@link CameraCaptureSession#abortCaptures}
+     * call can be used to discard the remaining requests for the prior capture session before a new
+     * one is created. Note that once the new session is created, the old one can no longer have its
+     * captures aborted.</p>
+     *
+     * <p>Using larger resolution outputs, or more outputs, can result in slower
+     * output rate from the device.</p>
+     *
+     * <p>Configuring a session with an empty or null list will close the current session, if
+     * any. This can be used to release the current session's target surfaces for another use.</p>
+     *
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured image data.
+     * @param listener The listener to notify about the status of the new capture session.
+     * @param handler The handler on which the listener should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     * <!--
+     * @return A new camera capture session to use, or null if an empty/null set of Surfaces is
+     *         provided.
+     * -->
+     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+     *                                  the listener is null, or the handler is null but the current
+     *                                  thread has no looper.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see CameraCaptureSession
+     * @see StreamConfigurationMap#getOutputFormats()
+     * @see StreamConfigurationMap#getOutputSizes(int)
+     * @see StreamConfigurationMap#getOutputSizes(Class)
+     */
+    public void createCaptureSession(List<Surface> outputs,
+            CameraCaptureSession.StateListener listener, Handler handler)
+            throws CameraAccessException;
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
      * initialized with template for a target use case. The settings are chosen
      * to be the best options for the specific camera device, so it is not
@@ -314,6 +430,7 @@
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
             throws CameraAccessException;
@@ -358,6 +475,7 @@
      * @see #capture
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
             Handler handler) throws CameraAccessException;
@@ -416,6 +534,7 @@
      * @see #setRepeatingBurst
      * @see #stopRepeating
      * @see #flush
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
             Handler handler) throws CameraAccessException;
@@ -474,6 +593,7 @@
      * @see #setRepeatingRequest
      * @see #stopRepeating
      * @see #flush
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
             Handler handler) throws CameraAccessException;
@@ -498,6 +618,7 @@
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see StateListener#onIdle
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public void stopRepeating() throws CameraAccessException;
 
@@ -534,25 +655,24 @@
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #configureOutputs
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public void flush() throws CameraAccessException;
 
     /**
-     * Close the connection to this camera device.
+     * Close the connection to this camera device as quickly as possible.
      *
-     * <p>After this call, all calls to
-     * the camera device interface will throw a {@link IllegalStateException},
-     * except for calls to close(). Once the device has fully shut down, the
-     * {@link StateListener#onClosed} callback will be called, and the camera is
-     * free to be re-opened.</p>
+     * <p>Immediately after this call, all calls to the camera device or active session interface
+     * will throw a {@link IllegalStateException}, except for calls to close(). Once the device has
+     * fully shut down, the {@link StateListener#onClosed} callback will be called, and the camera
+     * is free to be re-opened.</p>
      *
-     * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the
-     * device's {@link StateListener} will occur, and any remaining submitted capture requests will
-     * not fire their {@link CaptureListener} callbacks.</p>
+     * <p>Immediately after this call, besides the final {@link StateListener#onClosed} calls, no
+     * further callbacks from the device or the active session will occur, and any remaining
+     * submitted capture requests will be discarded, as if
+     * {@link CameraCaptureSession#abortCaptures} had been called, except that no success or failure
+     * callbacks will be invoked.</p>
      *
-     * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close}
-     * once the flush completes. This will discard some capture requests, but results in faster
-     * shutdown.</p>
      */
     @Override
     public void close();
@@ -569,6 +689,7 @@
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public static abstract class CaptureListener {
 
@@ -834,6 +955,7 @@
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera the camera device has that become unconfigured
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onUnconfigured(CameraDevice camera) {
             // Default empty implementation
@@ -863,6 +985,7 @@
          * @see CameraDevice#captureBurst
          * @see CameraDevice#setRepeatingBurst
          * @see CameraDevice#setRepeatingRequest
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onActive(CameraDevice camera) {
             // Default empty implementation
@@ -896,6 +1019,7 @@
          *
          * @see CameraDevice#configureOutputs
          * @see CameraDevice#flush
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onBusy(CameraDevice camera) {
             // Default empty implementation
@@ -943,6 +1067,7 @@
          * @see CameraDevice#configureOutputs
          * @see CameraDevice#stopRepeating
          * @see CameraDevice#flush
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onIdle(CameraDevice camera) {
             // Default empty implementation
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 0fcd598..03b342c 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -20,6 +20,7 @@
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.BinderHolder;
@@ -194,16 +195,11 @@
             // impossible
             return null;
         }
-
         return new CameraCharacteristics(info);
     }
 
     /**
-     * Open a connection to a camera with the given ID. Use
-     * {@link #getCameraIdList} to get the list of available camera
-     * devices. Note that even if an id is listed, open may fail if the device
-     * is disconnected between the calls to {@link #getCameraIdList} and
-     * {@link #openCamera}.
+     * Helper for openning a connection to a camera with the given ID.
      *
      * @param cameraId The unique identifier of the camera device to open
      * @param listener The listener for the camera. Must not be null.
@@ -216,35 +212,51 @@
      * @throws SecurityException if the application does not have permission to
      * access the camera
      * @throws IllegalArgumentException if listener or handler is null.
+     * @return A handle to the newly-created camera device.
      *
      * @see #getCameraIdList
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      */
-    private void openCameraDeviceUserAsync(String cameraId,
+    private CameraDevice openCameraDeviceUserAsync(String cameraId,
             CameraDevice.StateListener listener, Handler handler)
             throws CameraAccessException {
+        CameraDevice device = null;
         try {
 
             synchronized (mLock) {
 
                 ICameraDeviceUser cameraUser;
 
-                android.hardware.camera2.impl.CameraDevice device =
+                android.hardware.camera2.impl.CameraDevice deviceImpl =
                         new android.hardware.camera2.impl.CameraDevice(
                                 cameraId,
                                 listener,
                                 handler);
 
                 BinderHolder holder = new BinderHolder();
-                mCameraService.connectDevice(device.getCallbacks(),
-                        Integer.parseInt(cameraId),
-                        mContext.getPackageName(), USE_CALLING_UID, holder);
-                cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
+
+                ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
+                int id = Integer.parseInt(cameraId);
+                try {
+                    mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
+                            USE_CALLING_UID, holder);
+                    cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
+                } catch (CameraRuntimeException e) {
+                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+                        // Use legacy camera implementation for HAL1 devices
+                        Log.i(TAG, "Using legacy camera HAL.");
+                        cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+                    } else {
+                        // Rethrow otherwise
+                        throw e;
+                    }
+                }
 
                 // TODO: factor out listener to be non-nested, then move setter to constructor
                 // For now, calling setRemoteDevice will fire initial
                 // onOpened/onUnconfigured callbacks.
-                device.setRemoteDevice(cameraUser);
+                deviceImpl.setRemoteDevice(cameraUser);
+                device = deviceImpl;
             }
 
         } catch (NumberFormatException e) {
@@ -255,6 +267,7 @@
         } catch (RemoteException e) {
             // impossible
         }
+        return device;
     }
 
     /**
@@ -265,20 +278,26 @@
      * is disconnected between the calls to {@link #getCameraIdList} and
      * {@link #openCamera}.</p>
      *
-     * <p>If the camera successfully opens after this function call returns,
-     * {@link CameraDevice.StateListener#onOpened} will be invoked with the
-     * newly opened {@link CameraDevice} in the unconfigured state.</p>
+     * <p>Once the camera is successfully opened, {@link CameraDevice.StateListener#onOpened} will
+     * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
+     * for operation by calling {@link CameraDevice#createCaptureSession} and
+     * {@link CameraDevice#createCaptureRequest}</p>
      *
+     * <!--
+     * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
+     * on the returned CameraDevice instance will be queued up until the device startup has
+     * completed and the listener's {@link CameraDevice.StateListener#onOpened onOpened} method is
+     * called. The pending operations are then processed in order.</p>
+     * -->
      * <p>If the camera becomes disconnected during initialization
      * after this function call returns,
      * {@link CameraDevice.StateListener#onDisconnected} with a
      * {@link CameraDevice} in the disconnected state (and
      * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
      *
-     * <p>If the camera fails to initialize after this function call returns,
-     * {@link CameraDevice.StateListener#onError} will be invoked with a
-     * {@link CameraDevice} in the error state (and
-     * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
+     * <p>If opening the camera device fails, then the device listener's
+     * {@link CameraDevice.StateListener#onError onError} method will be called, and subsequent
+     * calls on the camera device will throw an {@link IllegalStateException}.</p>
      *
      * @param cameraId
      *             The unique identifier of the camera device to open
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index a70aa3b..b44b808 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -22,6 +22,8 @@
 import android.util.Rational;
 import android.view.Surface;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Objects;
 
@@ -199,6 +201,20 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean containsTarget(Surface surface) {
+        return mSurfaceSet.contains(surface);
+    }
+
+    /**
+     * @hide
+     */
+    public Collection<Surface> getTargets() {
+        return Collections.unmodifiableCollection(mSurfaceSet);
+    }
+
+    /**
      * A builder for capture requests.
      *
      * <p>To obtain a builder instance, use the
@@ -1110,8 +1126,11 @@
      * output, cropping to a smaller region if necessary to
      * maintain the stream's aspect ratio.</p>
      * <p>HAL2.x uses only (x, y, width)</p>
-     * <p>Any additional per-stream cropping must be done to
-     * maximize the final pixel area of the stream.</p>
+     * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+     * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
+     * it is not croppable. The crop region will be ignored by raw streams.</p>
+     * <p>For non-raw streams, any additional per-stream cropping will
+     * be done to maximize the final pixel area of the stream.</p>
      * <p>For example, if the crop region is set to a 4:3 aspect
      * ratio, then 4:3 streams should use the exact crop
      * region. 16:9 streams should further crop vertically
@@ -1292,8 +1311,16 @@
      * camera device. Applications can request lens shading map data by setting
      * {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
      * lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
-     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+     * applied by the camera device for this capture request.</p>
+     * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+     * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
+     * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
+     * to get best results, it is recommended that the applications wait for the AE and AWB to
+     * be converged before using the returned shading map data.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureRequest#CONTROL_AWB_MODE
      * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
      * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -1442,7 +1469,7 @@
      * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
      * These values are always available, and as close as possible to the
      * actually used nonlinear/nonglobal transforms.</p>
-     * <p>If a request is sent with TRANSFORM_MATRIX with the camera device's
+     * <p>If a request is sent with CONTRAST_CURVE with the camera device's
      * provided curve in FAST or HIGH_QUALITY, the image's tonemap will be
      * roughly the same.</p>
      *
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index f91fcb9..b0cacf1 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1749,8 +1749,11 @@
      * output, cropping to a smaller region if necessary to
      * maintain the stream's aspect ratio.</p>
      * <p>HAL2.x uses only (x, y, width)</p>
-     * <p>Any additional per-stream cropping must be done to
-     * maximize the final pixel area of the stream.</p>
+     * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+     * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
+     * it is not croppable. The crop region will be ignored by raw streams.</p>
+     * <p>For non-raw streams, any additional per-stream cropping will
+     * be done to maximize the final pixel area of the stream.</p>
      * <p>For example, if the crop region is set to a 4:3 aspect
      * ratio, then 4:3 streams should use the exact crop
      * region. 16:9 streams should further crop vertically
@@ -2006,8 +2009,16 @@
      * camera device. Applications can request lens shading map data by setting
      * {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
      * lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
-     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+     * applied by the camera device for this capture request.</p>
+     * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+     * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
+     * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
+     * to get best results, it is recommended that the applications wait for the AE and AWB to
+     * be converged before using the returned shading map data.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureRequest#CONTROL_AWB_MODE
      * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
      * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -2338,7 +2349,7 @@
      * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
      * These values are always available, and as close as possible to the
      * actually used nonlinear/nonglobal transforms.</p>
-     * <p>If a request is sent with TRANSFORM_MATRIX with the camera device's
+     * <p>If a request is sent with CONTRAST_CURVE with the camera device's
      * provided curve in FAST or HIGH_QUALITY, the image's tonemap will be
      * roughly the same.</p>
      *
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 0815170..50a58ed 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -26,7 +26,8 @@
 interface ICameraDeviceUser
 {
     /**
-     * Keep up-to-date with frameworks/av/include/camera/camera2/ICameraDeviceUser.h
+     * Keep up-to-date with frameworks/av/include/camera/camera2/ICameraDeviceUser.h and
+     * frameworks/base/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
      */
     void disconnect();
 
@@ -41,6 +42,27 @@
 
     int cancelRequest(int requestId, out LongParcelable lastFrameNumber);
 
+    /**
+     * Begin the device configuration.
+     *
+     * <p>
+     * beginConfigure must be called before any call to deleteStream, createStream,
+     * or endConfigure.  It is not valid to call this when the device is not idle.
+     * <p>
+     */
+    int beginConfigure();
+
+    /**
+     * End the device configuration.
+     *
+     * <p>
+     * endConfigure must be called after stream configuration is complete (i.e. after
+     * a call to beginConfigure and subsequent createStream/deleteStream calls).  This
+     * must be called before any requests can be submitted.
+     * <p>
+     */
+    int endConfigure();
+
     int deleteStream(int streamId);
 
     // non-negative value is the stream ID. negative value is status_t
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index dba24a1..b082a70 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -19,6 +19,7 @@
 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
 
 import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -216,7 +217,7 @@
             try {
                 waitUntilIdle();
 
-                // TODO: mRemoteDevice.beginConfigure
+                mRemoteDevice.beginConfigure();
                 // Delete all streams first (to free up HW resources)
                 for (Integer streamId : deleteList) {
                     mRemoteDevice.deleteStream(streamId);
@@ -231,7 +232,7 @@
                     mConfiguredOutputs.put(streamId, s);
                 }
 
-                // TODO: mRemoteDevice.endConfigure
+                mRemoteDevice.endConfigure();
             } catch (CameraRuntimeException e) {
                 if (e.getReason() == CAMERA_IN_USE) {
                     throw new IllegalStateException("The camera is currently busy." +
@@ -253,6 +254,13 @@
     }
 
     @Override
+    public void createCaptureSession(List<Surface> outputs,
+            CameraCaptureSession.StateListener listener, Handler handler)
+            throws CameraAccessException {
+        // TODO
+    }
+
+    @Override
     public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException {
         synchronized (mLock) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index db7486d..27cfd38 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -63,7 +63,7 @@
     private static final String TAG = "CameraMetadataJV";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h
-    private static final int NATIVE_JPEG_FORMAT = 0x21;
+    public static final int NATIVE_JPEG_FORMAT = 0x21;
 
     public CameraMetadataNative() {
         super();
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index b3a9559..7544045 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -45,6 +45,15 @@
         readFromParcel(in);
     }
 
+    public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
+                               int precaptureTriggerId, long frameNumber) {
+        this.requestId = requestId;
+        this.subsequenceId = subsequenceId;
+        this.afTriggerId = afTriggerId;
+        this.precaptureTriggerId = precaptureTriggerId;
+        this.frameNumber = frameNumber;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/hardware/camera2/legacy/BurstHolder.java b/core/java/android/hardware/camera2/legacy/BurstHolder.java
new file mode 100644
index 0000000..e35eb50
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/BurstHolder.java
@@ -0,0 +1,82 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.hardware.camera2.CaptureRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Immutable container for a burst of capture results.
+ */
+public class BurstHolder {
+
+    private final ArrayList<CaptureRequest> mRequests;
+    private final boolean mRepeating;
+    private final int mRequestId;
+
+    /**
+     * Immutable container for a burst of capture results.
+     *
+     * @param requestId id of the burst request.
+     * @param repeating true if this burst is repeating.
+     * @param requests a {@link java.util.List} of {@link CaptureRequest}s in this burst.
+     */
+    public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests) {
+        mRequests = new ArrayList<CaptureRequest>(requests);
+        mRepeating = repeating;
+        mRequestId = requestId;
+    }
+
+    /**
+     * Get the id of this request.
+     */
+    public int getRequestId() {
+        return mRequestId;
+    }
+
+    /**
+     * Return true if this repeating.
+     */
+    public boolean isRepeating() {
+        return mRepeating;
+    }
+
+    /**
+     * Return the number of requests in this burst sequence.
+     */
+    public int getNumberOfRequests() {
+        return mRequests.size();
+    }
+
+    /**
+     * Create a list of {@link RequestHolder} objects encapsulating the requests in this burst.
+     *
+     * @param frameNumber the starting framenumber for this burst.
+     * @return the list of {@link RequestHolder} objects.
+     */
+    public List<RequestHolder> produceRequestHolders(long frameNumber) {
+        ArrayList<RequestHolder> holders = new ArrayList<RequestHolder>();
+        int i = 0;
+        for (CaptureRequest r : mRequests) {
+            holders.add(new RequestHolder(mRequestId, i, r, mRepeating, frameNumber + i));
+            ++i;
+        }
+        return holders;
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
new file mode 100644
index 0000000..22ff9c6
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -0,0 +1,259 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.CameraBinderDecorator;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * Emulates a the state of a single Camera2 device.
+ *
+ * <p>
+ * This class acts as the state machine for a camera device.  Valid state transitions are given
+ * in the table below:
+ * </p>
+ *
+ * <ul>
+ *      <li>{@code UNCONFIGURED -> CONFIGURING}</li>
+ *      <li>{@code CONFIGURING -> IDLE}</li>
+ *      <li>{@code IDLE -> CONFIGURING}</li>
+ *      <li>{@code IDLE -> CAPTURING}</li>
+ *      <li>{@code CAPTURING -> IDLE}</li>
+ *      <li>{@code ANY -> ERROR}</li>
+ * </ul>
+ */
+public class CameraDeviceState {
+    private static final String TAG = "CameraDeviceState";
+    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+
+    private static final int STATE_ERROR = 0;
+    private static final int STATE_UNCONFIGURED = 1;
+    private static final int STATE_CONFIGURING = 2;
+    private static final int STATE_IDLE = 3;
+    private static final int STATE_CAPTURING = 4;
+
+    private int mCurrentState = STATE_UNCONFIGURED;
+    private int mCurrentError = CameraBinderDecorator.NO_ERROR;
+
+    private RequestHolder mCurrentRequest = null;
+
+    private Handler mCurrentHandler = null;
+    private CameraDeviceStateListener mCurrentListener = null;
+
+
+    /**
+     * CameraDeviceStateListener callbacks to be called after state transitions.
+     */
+    public interface CameraDeviceStateListener {
+        void onError(int errorCode, RequestHolder holder);
+        void onConfiguring();
+        void onIdle();
+        void onCaptureStarted(RequestHolder holder);
+        void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
+    }
+
+    /**
+     * Transition to the {@code ERROR} state.
+     *
+     * <p>
+     * The device cannot exit the {@code ERROR} state.  If the device was not already in the
+     * {@code ERROR} state, {@link CameraDeviceStateListener#onError(int, RequestHolder)} will be
+     * called.
+     * </p>
+     *
+     * @param error the error to set.  Should be one of the error codes defined in
+     *      {@link android.hardware.camera2.utils.CameraBinderDecorator}.
+     */
+    public synchronized void setError(int error) {
+        mCurrentError = error;
+        doStateTransition(STATE_ERROR);
+    }
+
+    /**
+     * Transition to the {@code CONFIGURING} state, or {@code ERROR} if in an invalid state.
+     *
+     * <p>
+     * If the device was not already in the {@code CONFIGURING} state,
+     * {@link CameraDeviceStateListener#onConfiguring()} will be called.
+     * </p>
+     *
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     */
+    public synchronized int setConfiguring() {
+        doStateTransition(STATE_CONFIGURING);
+        return mCurrentError;
+    }
+
+    /**
+     * Transition to the {@code IDLE} state, or {@code ERROR} if in an invalid state.
+     *
+     * <p>
+     * If the device was not already in the {@code IDLE} state,
+     * {@link CameraDeviceStateListener#onIdle()} will be called.
+     * </p>
+     *
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     */
+    public synchronized int setIdle() {
+        doStateTransition(STATE_IDLE);
+        return mCurrentError;
+    }
+
+    /**
+     * Transition to the {@code CAPTURING} state, or {@code ERROR} if in an invalid state.
+     *
+     * <p>
+     * If the device was not already in the {@code CAPTURING} state,
+     * {@link CameraDeviceStateListener#onCaptureStarted(RequestHolder)} will be called.
+     * </p>
+     *
+     * @param request A {@link RequestHolder} containing the request for the current capture.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     */
+    public synchronized int setCaptureStart(final RequestHolder request) {
+        mCurrentRequest = request;
+        doStateTransition(STATE_CAPTURING);
+        return mCurrentError;
+    }
+
+    /**
+     * Set the result for a capture.
+     *
+     * <p>
+     * If the device was in the {@code CAPTURING} state,
+     * {@link CameraDeviceStateListener#onCaptureResult(CameraMetadataNative, RequestHolder)} will
+     * be called with the given result, otherwise this will result in the device transitioning to
+     * the {@code ERROR} state,
+     * </p>
+     *
+     * @param request the {@link RequestHolder} request that created this result.
+     * @param result the {@link CameraMetadataNative} result to set.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     */
+    public synchronized int setCaptureResult(final RequestHolder request,
+                                             final CameraMetadataNative result) {
+        if (mCurrentState != STATE_CAPTURING) {
+            Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
+            mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+            doStateTransition(STATE_ERROR);
+            return mCurrentError;
+        }
+
+        if (mCurrentHandler != null && mCurrentListener != null) {
+            mCurrentHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCurrentListener.onCaptureResult(result, request);
+                }
+            });
+        }
+        return mCurrentError;
+    }
+
+    /**
+     * Set the listener for state transition callbacks.
+     *
+     * @param handler handler on which to call the callbacks.
+     * @param listener the {@link CameraDeviceStateListener} callbacks to call.
+     */
+    public synchronized void setCameraDeviceCallbacks(Handler handler,
+                                                      CameraDeviceStateListener listener) {
+        mCurrentHandler = handler;
+        mCurrentListener = listener;
+    }
+
+    private void doStateTransition(int newState) {
+        if (DEBUG) {
+            if (newState != mCurrentState) {
+                Log.d(TAG, "Transitioning to state " + newState);
+            }
+        }
+        switch(newState) {
+            case STATE_ERROR:
+                if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
+                        mCurrentListener != null) {
+                    mCurrentHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCurrentListener.onError(mCurrentError, mCurrentRequest);
+                        }
+                    });
+                }
+                mCurrentState = STATE_ERROR;
+                break;
+            case STATE_CONFIGURING:
+                if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) {
+                    Log.e(TAG, "Cannot call configure while in state: " + mCurrentState);
+                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+                    doStateTransition(STATE_ERROR);
+                    break;
+                }
+                if (mCurrentState != STATE_CONFIGURING && mCurrentHandler != null &&
+                        mCurrentListener != null) {
+                    mCurrentHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCurrentListener.onConfiguring();
+                        }
+                    });
+                }
+                mCurrentState = STATE_CONFIGURING;
+                break;
+            case STATE_IDLE:
+                if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
+                    Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
+                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+                    doStateTransition(STATE_ERROR);
+                    break;
+                }
+                if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
+                        mCurrentListener != null) {
+                    mCurrentHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCurrentListener.onIdle();
+                        }
+                    });
+                }
+                mCurrentState = STATE_IDLE;
+                break;
+            case STATE_CAPTURING:
+                if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) {
+                    Log.e(TAG, "Cannot call capture while in state: " + mCurrentState);
+                    mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
+                    doStateTransition(STATE_ERROR);
+                    break;
+                }
+                if (mCurrentHandler != null && mCurrentListener != null) {
+                    mCurrentHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCurrentListener.onCaptureStarted(mCurrentRequest);
+                        }
+                    });
+                }
+                mCurrentState = STATE_CAPTURING;
+                break;
+            default:
+                throw new IllegalStateException("Transition to unknown state: " + newState);
+        }
+    }
+
+
+}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
new file mode 100644
index 0000000..54d9c3c
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -0,0 +1,273 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.hardware.Camera;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.utils.LongParcelable;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.CameraBinderDecorator;
+import android.hardware.camera2.utils.CameraRuntimeException;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Compatibility implementation of the Camera2 API binder interface.
+ *
+ * <p>
+ * This is intended to be called from the same process as client
+ * {@link android.hardware.camera2.CameraDevice}, and wraps a
+ * {@link android.hardware.camera2.legacy.LegacyCameraDevice} that emulates Camera2 service using
+ * the Camera1 API.
+ * </p>
+ *
+ * <p>
+ * Keep up to date with ICameraDeviceUser.aidl.
+ * </p>
+ */
+public class CameraDeviceUserShim implements ICameraDeviceUser {
+    private static final String TAG = "CameraDeviceUserShim";
+
+    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+
+    private final LegacyCameraDevice mLegacyDevice;
+
+    private final Object mConfigureLock = new Object();
+    private int mSurfaceIdCounter;
+    private boolean mConfiguring;
+    private final SparseArray<Surface> mSurfaces;
+
+    protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera) {
+        mLegacyDevice = legacyCamera;
+        mConfiguring = false;
+        mSurfaces = new SparseArray<Surface>();
+
+        mSurfaceIdCounter = 0;
+    }
+
+    public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
+                                                         int cameraId) {
+        if (DEBUG) {
+            Log.d(TAG, "Opening shim Camera device");
+        }
+        // TODO: Move open/init into LegacyCameraDevice thread when API is switched to async.
+        Camera legacyCamera = Camera.openUninitialized();
+        int initErrors = legacyCamera.cameraInit(cameraId);
+        // Check errors old HAL initialization
+        if (Camera.checkInitErrors(initErrors)) {
+            // TODO: Map over old camera error codes.  This likely involves improving the error
+            // reporting in the HAL1 connect path.
+            throw new CameraRuntimeException(CameraAccessException.CAMERA_DISCONNECTED);
+        }
+        LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks);
+        return new CameraDeviceUserShim(cameraId, device);
+    }
+
+    @Override
+    public void disconnect() {
+        if (DEBUG) {
+            Log.d(TAG, "disconnect called.");
+        }
+        mLegacyDevice.close();
+    }
+
+    @Override
+    public int submitRequest(CaptureRequest request, boolean streaming,
+                             /*out*/LongParcelable lastFrameNumber) {
+        if (DEBUG) {
+            Log.d(TAG, "submitRequest called.");
+        }
+        synchronized(mConfigureLock) {
+            if (mConfiguring) {
+                Log.e(TAG, "Cannot submit request, configuration change in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+        }
+        return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber);
+    }
+
+    @Override
+    public int submitRequestList(List<CaptureRequest> request, boolean streaming,
+                                 /*out*/LongParcelable lastFrameNumber) {
+        if (DEBUG) {
+            Log.d(TAG, "submitRequestList called.");
+        }
+        synchronized(mConfigureLock) {
+            if (mConfiguring) {
+                Log.e(TAG, "Cannot submit request, configuration change in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+        }
+        return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber);
+    }
+
+    @Override
+    public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) {
+        if (DEBUG) {
+            Log.d(TAG, "cancelRequest called.");
+        }
+        synchronized(mConfigureLock) {
+            if (mConfiguring) {
+                Log.e(TAG, "Cannot cancel request, configuration change in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+        }
+        long lastFrame = mLegacyDevice.cancelRequest(requestId);
+        lastFrameNumber.setNumber(lastFrame);
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public int beginConfigure() {
+        if (DEBUG) {
+            Log.d(TAG, "beginConfigure called.");
+        }
+        synchronized(mConfigureLock) {
+            if (mConfiguring) {
+                Log.e(TAG, "Cannot begin configure, configuration change already in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+            mConfiguring = true;
+        }
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public int endConfigure() {
+        if (DEBUG) {
+            Log.d(TAG, "endConfigure called.");
+        }
+        ArrayList<Surface> surfaces = null;
+        synchronized(mConfigureLock) {
+            if (!mConfiguring) {
+                Log.e(TAG, "Cannot end configure, no configuration change in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+            int numSurfaces = mSurfaces.size();
+            if (numSurfaces > 0) {
+                surfaces = new ArrayList<Surface>();
+                for (int i = 0; i < numSurfaces; ++i) {
+                    surfaces.add(mSurfaces.valueAt(i));
+                }
+            }
+            mConfiguring = false;
+        }
+        return mLegacyDevice.configureOutputs(surfaces);
+    }
+
+    @Override
+    public int deleteStream(int streamId) {
+        if (DEBUG) {
+            Log.d(TAG, "deleteStream called.");
+        }
+        synchronized(mConfigureLock) {
+            if (!mConfiguring) {
+                Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+            int index = mSurfaces.indexOfKey(streamId);
+            if (index < 0) {
+                Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist.");
+                return CameraBinderDecorator.BAD_VALUE;
+            }
+            mSurfaces.removeAt(index);
+        }
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public int createStream(int width, int height, int format, Surface surface) {
+        if (DEBUG) {
+            Log.d(TAG, "createStream called.");
+        }
+        synchronized(mConfigureLock) {
+            if (!mConfiguring) {
+                Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+            int id = ++mSurfaceIdCounter;
+            mSurfaces.put(id, surface);
+            return id;
+        }
+    }
+
+    @Override
+    public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) {
+        if (DEBUG) {
+            Log.d(TAG, "createDefaultRequest called.");
+        }
+        // TODO: implement createDefaultRequest.
+        Log.e(TAG, "createDefaultRequest unimplemented.");
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public int getCameraInfo(/*out*/CameraMetadataNative info) {
+        if (DEBUG) {
+            Log.d(TAG, "getCameraInfo called.");
+        }
+        // TODO: implement getCameraInfo.
+        Log.e(TAG, "getCameraInfo unimplemented.");
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public int waitUntilIdle() throws RemoteException {
+        if (DEBUG) {
+            Log.d(TAG, "waitUntilIdle called.");
+        }
+        synchronized(mConfigureLock) {
+            if (mConfiguring) {
+                Log.e(TAG, "Cannot wait until idle, configuration change in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+        }
+        mLegacyDevice.waitUntilIdle();
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public int flush(/*out*/LongParcelable lastFrameNumber) {
+        if (DEBUG) {
+            Log.d(TAG, "flush called.");
+        }
+        synchronized(mConfigureLock) {
+            if (mConfiguring) {
+                Log.e(TAG, "Cannot flush, configuration change in progress.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
+        }
+        // TODO: implement flush.
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
+    @Override
+    public IBinder asBinder() {
+        // This is solely intended to be used for in-process binding.
+        return null;
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
new file mode 100644
index 0000000..3fd2309
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -0,0 +1,234 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.graphics.SurfaceTexture;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.Collection;
+
+/**
+ * GLThreadManager handles the thread used for rendering into the configured output surfaces.
+ */
+public class GLThreadManager {
+    private final String TAG;
+    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+
+    private static final int MSG_NEW_CONFIGURATION = 1;
+    private static final int MSG_NEW_FRAME = 2;
+    private static final int MSG_CLEANUP = 3;
+    private static final int MSG_DROP_FRAMES = 4;
+    private static final int MSG_ALLOW_FRAMES = 5;
+
+    private final SurfaceTextureRenderer mTextureRenderer;
+
+    private final RequestHandlerThread mGLHandlerThread;
+
+    private final RequestThreadManager.FpsCounter mPrevCounter =
+            new RequestThreadManager.FpsCounter("GL Preview Producer");
+
+    /**
+     * Container object for Configure messages.
+     */
+    private static class ConfigureHolder {
+        public final ConditionVariable condition;
+        public final Collection<Surface> surfaces;
+
+        public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) {
+            this.condition = condition;
+            this.surfaces = surfaces;
+        }
+    }
+
+    private final Handler.Callback mGLHandlerCb = new Handler.Callback() {
+        private boolean mCleanup = false;
+        private boolean mConfigured = false;
+        private boolean mDroppingFrames = false;
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean handleMessage(Message msg) {
+            if (mCleanup) {
+                return true;
+            }
+            switch (msg.what) {
+                case MSG_NEW_CONFIGURATION:
+                    ConfigureHolder configure = (ConfigureHolder) msg.obj;
+                    mTextureRenderer.cleanupEGLContext();
+                    mTextureRenderer.configureSurfaces(configure.surfaces);
+                    configure.condition.open();
+                    mConfigured = true;
+                    break;
+                case MSG_NEW_FRAME:
+                    if (mDroppingFrames) {
+                        Log.w(TAG, "Ignoring frame.");
+                        break;
+                    }
+                    if (DEBUG) {
+                        mPrevCounter.countAndLog();
+                    }
+                    if (!mConfigured) {
+                        Log.e(TAG, "Dropping frame, EGL context not configured!");
+                    }
+                    mTextureRenderer.drawIntoSurfaces((Collection<Surface>) msg.obj);
+                    break;
+                case MSG_CLEANUP:
+                    mTextureRenderer.cleanupEGLContext();
+                    mCleanup = true;
+                    mConfigured = false;
+                    break;
+                case MSG_DROP_FRAMES:
+                    mDroppingFrames = true;
+                    break;
+                case MSG_ALLOW_FRAMES:
+                    mDroppingFrames = false;
+                default:
+                    Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
+                    break;
+            }
+            return true;
+        }
+    };
+
+    /**
+     * Create a new GL thread and renderer.
+     *
+     * @param cameraId the camera id for this thread.
+     */
+    public GLThreadManager(int cameraId) {
+        mTextureRenderer = new SurfaceTextureRenderer();
+        TAG = String.format("CameraDeviceGLThread-%d", cameraId);
+        mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb);
+    }
+
+    /**
+     * Start the thread.
+     *
+     * <p>
+     * This must be called before queueing new frames.
+     * </p>
+     */
+    public void start() {
+        mGLHandlerThread.start();
+    }
+
+    /**
+     * Wait until the thread has started.
+     */
+    public void waitUntilStarted() {
+        mGLHandlerThread.waitUntilStarted();
+    }
+
+    /**
+     * Quit the thread.
+     *
+     * <p>
+     * No further methods can be called after this.
+     * </p>
+     */
+    public void quit() {
+        Handler handler = mGLHandlerThread.getHandler();
+        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
+        mGLHandlerThread.quitSafely();
+    }
+
+    /**
+     * Queue a new call to draw into a given set of surfaces.
+     *
+     * <p>
+     * The set of surfaces passed here must be a subset of the set of surfaces passed in
+     * the last call to {@link #setConfigurationAndWait}.
+     * </p>
+     *
+     * @param targets a collection of {@link android.view.Surface}s to draw into.
+     */
+    public void queueNewFrame(Collection<Surface> targets) {
+        Handler handler = mGLHandlerThread.getHandler();
+
+        /**
+         * Avoid queuing more than one new frame.  If we are not consuming faster than frames
+         * are produced, drop frames rather than allowing the queue to back up.
+         */
+        if (!handler.hasMessages(MSG_NEW_FRAME)) {
+            handler.sendMessage(handler.obtainMessage(MSG_NEW_FRAME, targets));
+        } else {
+            Log.e(TAG, "GLThread dropping frame.  Not consuming frames quickly enough!");
+        }
+    }
+
+    /**
+     * Configure the GL renderer for the given set of output surfaces, and block until
+     * this configuration has been applied.
+     *
+     * @param surfaces a collection of {@link android.view.Surface}s to configure.
+     */
+    public void setConfigurationAndWait(Collection<Surface> surfaces) {
+        Handler handler = mGLHandlerThread.getHandler();
+
+        final ConditionVariable condition = new ConditionVariable(/*closed*/false);
+        ConfigureHolder configure = new ConfigureHolder(condition, surfaces);
+
+        Message m = handler.obtainMessage(MSG_NEW_CONFIGURATION, /*arg1*/0, /*arg2*/0, configure);
+        handler.sendMessage(m);
+
+        // Block until configuration applied.
+        condition.block();
+    }
+
+    /**
+     * Get the underlying surface to produce frames from.
+     *
+     * <p>
+     * This returns the surface that is drawn into the set of surfaces passed in for each frame.
+     * This method should only be called after a call to
+     * {@link #setConfigurationAndWait(java.util.Collection)}.  Calling this before the first call
+     * to {@link #setConfigurationAndWait(java.util.Collection)}, after {@link #quit()}, or
+     * concurrently to one of these calls may result in an invalid
+     * {@link android.graphics.SurfaceTexture} being returned.
+     * </p>
+     *
+     * @return an {@link android.graphics.SurfaceTexture} to draw to.
+     */
+    public SurfaceTexture getCurrentSurfaceTexture() {
+        return mTextureRenderer.getSurfaceTexture();
+    }
+
+    /**
+     * Ignore any subsequent calls to {@link #queueNewFrame(java.util.Collection)}.
+     */
+    public void ignoreNewFrames() {
+        mGLHandlerThread.getHandler().sendEmptyMessage(MSG_DROP_FRAMES);
+    }
+
+    /**
+     * Wait until no messages are queued.
+     */
+    public void waitUntilIdle() {
+        mGLHandlerThread.waitUntilIdle();
+    }
+
+    /**
+     * Re-enable drawing new frames after a call to {@link #ignoreNewFrames()}.
+     */
+    public void allowNewFrames() {
+        mGLHandlerThread.getHandler().sendEmptyMessage(MSG_ALLOW_FRAMES);
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
new file mode 100644
index 0000000..f9cf905
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -0,0 +1,275 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.utils.LongParcelable;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.CameraBinderDecorator;
+import android.hardware.camera2.utils.CameraRuntimeException;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class emulates the functionality of a Camera2 device using a the old Camera class.
+ *
+ * <p>
+ * There are two main components that are used to implement this:
+ * - A state machine containing valid Camera2 device states ({@link CameraDeviceState}).
+ * - A message-queue based pipeline that manages an old Camera class, and executes capture and
+ *   configuration requests.
+ * </p>
+ */
+public class LegacyCameraDevice implements AutoCloseable {
+    public static final String DEBUG_PROP = "HAL1ShimLogging";
+
+    private final String TAG;
+
+    private final int mCameraId;
+    private final ICameraDeviceCallbacks mDeviceCallbacks;
+    private final CameraDeviceState mDeviceState = new CameraDeviceState();
+
+    private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
+    private final AtomicInteger mRequestIdCounter = new AtomicInteger(0);
+
+    private final HandlerThread mCallbackHandlerThread = new HandlerThread("ResultThread");
+    private final Handler mCallbackHandler;
+    private static final int ILLEGAL_VALUE = -1;
+
+    private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
+        if (holder == null) {
+            return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
+                    ILLEGAL_VALUE, ILLEGAL_VALUE);
+        }
+        return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
+                /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber());
+    }
+
+    /**
+     * Listener for the camera device state machine.  Calls the appropriate
+     * {@link ICameraDeviceCallbacks} for each state transition.
+     */
+    private final CameraDeviceState.CameraDeviceStateListener mStateListener =
+            new CameraDeviceState.CameraDeviceStateListener() {
+        @Override
+        public void onError(final int errorCode, RequestHolder holder) {
+            mIdle.open();
+            final CaptureResultExtras extras = getExtrasFromRequest(holder);
+            try {
+                mDeviceCallbacks.onCameraError(errorCode, extras);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
+            }
+
+        }
+
+        @Override
+        public void onConfiguring() {
+            // Do nothing
+        }
+
+        @Override
+        public void onIdle() {
+            mIdle.open();
+
+            try {
+                mDeviceCallbacks.onCameraIdle();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Received remote exception during onCameraIdle callback: ", e);
+            }
+        }
+
+        @Override
+        public void onCaptureStarted(RequestHolder holder) {
+            final CaptureResultExtras extras = getExtrasFromRequest(holder);
+
+            try {
+                // TODO: Don't fake timestamp
+                mDeviceCallbacks.onCaptureStarted(extras, System.nanoTime());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
+            }
+
+        }
+
+        @Override
+        public void onCaptureResult(CameraMetadataNative result, RequestHolder holder) {
+            final CaptureResultExtras extras = getExtrasFromRequest(holder);
+
+            try {
+                // TODO: Don't fake metadata
+                mDeviceCallbacks.onResultReceived(result, extras);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
+            }
+        }
+    };
+
+    private final RequestThreadManager mRequestThreadManager;
+
+    /**
+     * Check if a given surface uses {@link ImageFormat#YUV_420_888} format.
+     *
+     * @param s the surface to check.
+     * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888}.
+     */
+    static boolean needsConversion(Surface s) {
+        return LegacyCameraDevice.nativeDetectSurfaceType(s) == ImageFormat.YUV_420_888;
+    }
+
+    /**
+     * Create a new emulated camera device from a given Camera 1 API camera.
+     *
+     * <p>
+     * The {@link Camera} provided to this constructor must already have been successfully opened,
+     * and ownership of the provided camera is passed to this object.  No further calls to the
+     * camera methods should be made following this constructor.
+     * </p>
+     *
+     * @param cameraId the id of the camera.
+     * @param camera an open {@link Camera} device.
+     * @param callbacks {@link ICameraDeviceCallbacks} callbacks to call for Camera2 API operations.
+     */
+    public LegacyCameraDevice(int cameraId, Camera camera, ICameraDeviceCallbacks callbacks) {
+        mCameraId = cameraId;
+        mDeviceCallbacks = callbacks;
+        TAG = String.format("CameraDevice-%d-LE", mCameraId);
+
+        mCallbackHandlerThread.start();
+        mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
+        mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
+        mRequestThreadManager =
+                new RequestThreadManager(cameraId, camera, mDeviceState);
+        mRequestThreadManager.start();
+    }
+
+    /**
+     * Configure the device with a set of output surfaces.
+     *
+     * @param outputs a list of surfaces to set.
+     * @return an error code for this binder operation, or {@link CameraBinderDecorator.NO_ERROR}
+     *          on success.
+     */
+    public int configureOutputs(List<Surface> outputs) {
+        int error = mDeviceState.setConfiguring();
+        if (error == CameraBinderDecorator.NO_ERROR) {
+            mRequestThreadManager.configure(outputs);
+            error = mDeviceState.setIdle();
+        }
+        return error;
+    }
+
+    /**
+     * Submit a burst of capture requests.
+     *
+     * @param requestList a list of capture requests to execute.
+     * @param repeating {@code true} if this burst is repeating.
+     * @param frameNumber an output argument that contains either the frame number of the last frame
+     *                    that will be returned for this request, or the frame number of the last
+     *                    frame that will be returned for the current repeating request if this
+     *                    burst is set to be repeating.
+     * @return the request id.
+     */
+    public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
+            /*out*/LongParcelable frameNumber) {
+        // TODO: validate request here
+        mIdle.close();
+        return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
+                frameNumber);
+    }
+
+    /**
+     * Submit a single capture request.
+     *
+     * @param request the capture request to execute.
+     * @param repeating {@code true} if this request is repeating.
+     * @param frameNumber an output argument that contains either the frame number of the last frame
+     *                    that will be returned for this request, or the frame number of the last
+     *                    frame that will be returned for the current repeating request if this
+     *                    request is set to be repeating.
+     * @return the request id.
+     */
+    public int submitRequest(CaptureRequest request, boolean repeating,
+            /*out*/LongParcelable frameNumber) {
+        ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+        requestList.add(request);
+        return submitRequestList(requestList, repeating, frameNumber);
+    }
+
+    /**
+     * Cancel the repeating request with the given request id.
+     *
+     * @param requestId the request id of the request to cancel.
+     * @return the last frame number to be returned from the HAL for the given repeating request, or
+     *          {@code INVALID_FRAME} if none exists.
+     */
+    public long cancelRequest(int requestId) {
+        return mRequestThreadManager.cancelRepeating(requestId);
+    }
+
+    /**
+     * Block until the {@link ICameraDeviceCallbacks#onCameraIdle()} callback is received.
+     */
+    public void waitUntilIdle()  {
+        mIdle.block();
+    }
+
+    @Override
+    public void close() {
+        mRequestThreadManager.quit();
+        mCallbackHandlerThread.quitSafely();
+        // TODO: throw IllegalStateException in every method after close has been called
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } catch (CameraRuntimeException e) {
+            Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
+        } finally {
+            super.finalize();
+        }
+    }
+
+    protected static native int nativeDetectSurfaceType(Surface surface);
+
+    protected static native void nativeDetectSurfaceDimens(Surface surface, int[] dimens);
+
+    protected static native void nativeConfigureSurface(Surface surface, int width, int height,
+                                                        int pixelFormat);
+
+    protected static native void nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
+                                                    int height, int pixelFormat);
+
+    protected static native void nativeSetSurfaceFormat(Surface surface, int pixelFormat);
+
+    protected static native void nativeSetSurfaceDimens(Surface surface, int width, int height);
+
+}
diff --git a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
new file mode 100644
index 0000000..36cd907
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
@@ -0,0 +1,101 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.MessageQueue;
+
+public class RequestHandlerThread extends HandlerThread {
+    private final ConditionVariable mStarted = new ConditionVariable(false);
+    private final ConditionVariable mIdle = new ConditionVariable(true);
+    private Handler.Callback mCallback;
+    private volatile Handler mHandler;
+
+    public RequestHandlerThread(String name, Handler.Callback callback) {
+        super(name, Thread.MAX_PRIORITY);
+        mCallback = callback;
+    }
+
+    @Override
+    protected void onLooperPrepared() {
+        mHandler = new Handler(getLooper(), mCallback);
+        mStarted.open();
+    }
+
+    // Blocks until thread has started
+    public void waitUntilStarted() {
+        mStarted.block();
+    }
+
+    // May return null if the handler is not set up yet.
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    // Blocks until thread has started
+    public Handler waitAndGetHandler() {
+        waitUntilStarted();
+        return getHandler();
+    }
+
+    // Atomic multi-type message existence check
+    public boolean hasAnyMessages(int[] what) {
+        synchronized (mHandler.getLooper().getQueue()) {
+            for (int i : what) {
+                if (mHandler.hasMessages(i)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    // Atomic multi-type message remove
+    public void removeMessages(int[] what) {
+        synchronized (mHandler.getLooper().getQueue()) {
+            for (int i : what) {
+                mHandler.removeMessages(i);
+            }
+        }
+    }
+
+    private final MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
+        @Override
+        public boolean queueIdle() {
+            mIdle.open();
+            return false;
+        }
+    };
+
+    // Blocks until thread is idling
+    public void waitUntilIdle() {
+        Looper looper = waitAndGetHandler().getLooper();
+        if (looper.isIdling()) {
+            return;
+        }
+        mIdle.close();
+        looper.getQueue().addIdleHandler(mIdleHandler);
+        if (looper.isIdling()) {
+            return;
+        }
+        mIdle.block();
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
new file mode 100644
index 0000000..8a9052f
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java
@@ -0,0 +1,159 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.view.Surface;
+
+import java.util.Collection;
+
+/**
+ * Immutable container for a single capture request and associated information.
+ */
+public class RequestHolder {
+
+    private final boolean mRepeating;
+    private final CaptureRequest mRequest;
+    private final int mRequestId;
+    private final int mSubsequeceId;
+    private final long mFrameNumber;
+
+    RequestHolder(int requestId, int subsequenceId, CaptureRequest request, boolean repeating,
+                  long frameNumber) {
+        mRepeating = repeating;
+        mRequest = request;
+        mRequestId = requestId;
+        mSubsequeceId = subsequenceId;
+        mFrameNumber = frameNumber;
+    }
+
+    /**
+     * Return the request id for the contained {@link CaptureRequest}.
+     */
+    public int getRequestId() {
+        return mRequestId;
+    }
+
+    /**
+     * Returns true if the contained request is repeating.
+     */
+    public boolean isRepeating() {
+        return mRepeating;
+    }
+
+    /**
+     * Return the subsequence id for this request.
+     */
+    public int getSubsequeceId() {
+        return mSubsequeceId;
+    }
+
+    /**
+     * Returns the frame number for this request.
+     */
+    public long getFrameNumber() {
+        return mFrameNumber;
+    }
+
+    /**
+     * Returns the contained request.
+     */
+    public CaptureRequest getRequest() {
+        return mRequest;
+    }
+
+    /**
+     * Returns a read-only collection of the surfaces targeted by the contained request.
+     */
+    public Collection<Surface> getHolderTargets() {
+        return getRequest().getTargets();
+    }
+
+    /**
+     * Returns true if any of the surfaces targeted by the contained request require jpeg buffers.
+     */
+    public boolean hasJpegTargets() {
+        for (Surface s : getHolderTargets()) {
+            if (jpegType(s)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if any of the surfaces targeted by the contained request require a
+     * non-jpeg buffer type.
+     */
+    public boolean hasPreviewTargets() {
+        for (Surface s : getHolderTargets()) {
+            if (previewType(s)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return the first surface targeted by the contained request that requires a
+     * non-jpeg buffer type.
+     */
+    public Surface getFirstPreviewTarget() {
+        for (Surface s : getHolderTargets()) {
+            if (previewType(s)) {
+                return s;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the given surface requires jpeg buffers.
+     *
+     * @param s a {@link Surface} to check.
+     * @return true if the surface requires a jpeg buffer.
+     */
+    public static boolean jpegType(Surface s) {
+        if (LegacyCameraDevice.nativeDetectSurfaceType(s) ==
+                CameraMetadataNative.NATIVE_JPEG_FORMAT) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the given surface requires non-jpeg buffer types.
+     *
+     * <p>
+     * "Jpeg buffer" refers to the buffers returned in the jpeg
+     * {@link android.hardware.Camera.PictureCallback}.  Non-jpeg buffers are created using a tee
+     * of the preview stream drawn to the surface
+     * set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
+     * equivalent methods.
+     * </p>
+     * @param s a {@link Surface} to check.
+     * @return true if the surface requires a non-jpeg buffer type.
+     */
+    public static boolean previewType(Surface s) {
+        if (LegacyCameraDevice.nativeDetectSurfaceType(s) !=
+                CameraMetadataNative.NATIVE_JPEG_FORMAT) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
new file mode 100644
index 0000000..5c68303
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -0,0 +1,132 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.utils.LongParcelable;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayDeque;
+import java.util.List;
+
+/**
+ * A queue of bursts of requests.
+ *
+ * <p>This queue maintains the count of frames that have been produced, and is thread safe.</p>
+ */
+public class RequestQueue {
+    private static final String TAG = "RequestQueue";
+
+    private static final long INVALID_FRAME = -1;
+
+    private BurstHolder mRepeatingRequest = null;
+    private final ArrayDeque<BurstHolder> mRequestQueue = new ArrayDeque<BurstHolder>();
+
+    private long mCurrentFrameNumber = 0;
+    private long mCurrentRepeatingFrameNumber = INVALID_FRAME;
+    private int mCurrentRequestId = 0;
+
+    public RequestQueue() {}
+
+    /**
+     * Return and remove the next burst on the queue.
+     *
+     * <p>If a repeating burst is returned, it will not be removed.</p>
+     *
+     * @return a pair containing the next burst and the current frame number, or null if none exist.
+     */
+    public synchronized Pair<BurstHolder, Long> getNext() {
+        BurstHolder next = mRequestQueue.poll();
+        if (next == null && mRepeatingRequest != null) {
+            next = mRepeatingRequest;
+            mCurrentRepeatingFrameNumber = mCurrentFrameNumber +
+                    next.getNumberOfRequests();
+        }
+
+        if (next == null) {
+            return null;
+        }
+
+        Pair<BurstHolder, Long> ret =  new Pair<BurstHolder, Long>(next, mCurrentFrameNumber);
+        mCurrentFrameNumber += next.getNumberOfRequests();
+        return ret;
+    }
+
+    /**
+     * Cancel a repeating request.
+     *
+     * @param requestId the id of the repeating request to cancel.
+     * @return the last frame to be returned from the HAL for the given repeating request, or
+     *          {@code INVALID_FRAME} if none exists.
+     */
+    public synchronized long stopRepeating(int requestId) {
+        long ret = INVALID_FRAME;
+        if (mRepeatingRequest != null && mRepeatingRequest.getRequestId() == requestId) {
+            mRepeatingRequest = null;
+            ret = mCurrentRepeatingFrameNumber;
+            mCurrentRepeatingFrameNumber = INVALID_FRAME;
+        } else {
+            Log.e(TAG, "cancel failed: no repeating request exists for request id: " + requestId);
+        }
+        return ret;
+    }
+
+    /**
+     * Add a the given burst to the queue.
+     *
+     * <p>If the burst is repeating, replace the current repeating burst.</p>
+     *
+     * @param requests the burst of requests to add to the queue.
+     * @param repeating true if the burst is repeating.
+     * @param frameNumber an output argument that contains either the frame number of the last frame
+     *                    that will be returned for this request, or the frame number of the last
+     *                    frame that will be returned for the current repeating request if this
+     *                    burst is set to be repeating.
+     * @return the request id.
+     */
+    public synchronized int submit(List<CaptureRequest> requests, boolean repeating,
+            /*out*/LongParcelable frameNumber) {
+        int requestId = mCurrentRequestId++;
+        BurstHolder burst = new BurstHolder(requestId, repeating, requests);
+        long ret = INVALID_FRAME;
+        if (burst.isRepeating()) {
+            if (mRepeatingRequest != null) {
+                ret = mCurrentRepeatingFrameNumber;
+            }
+            mCurrentRepeatingFrameNumber = INVALID_FRAME;
+            mRepeatingRequest = burst;
+        } else {
+            mRequestQueue.offer(burst);
+            ret = calculateLastFrame(burst.getRequestId());
+        }
+        frameNumber.setNumber(ret);
+        return requestId;
+    }
+
+    private long calculateLastFrame(int requestId) {
+        long total = mCurrentFrameNumber;
+        for (BurstHolder b : mRequestQueue) {
+            total += b.getNumberOfRequests();
+            if (b.getRequestId() == requestId) {
+                return total;
+            }
+        }
+        throw new IllegalStateException(
+                "At least one request must be in the queue to calculate frame number");
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
new file mode 100644
index 0000000..c4669f5
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -0,0 +1,491 @@
+/*
+ * 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.hardware.camera2.legacy;
+
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.utils.LongParcelable;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This class executes requests to the {@link Camera}.
+ *
+ * <p>
+ * The main components of this class are:
+ * - A message queue of requests to the {@link Camera}.
+ * - A thread that consumes requests to the {@link Camera} and executes them.
+ * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
+ * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
+ * </p>
+ */
+public class RequestThreadManager {
+    private final String TAG;
+    private final int mCameraId;
+    private final RequestHandlerThread mRequestThread;
+
+    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+    private final Camera mCamera;
+
+    private final CameraDeviceState mDeviceState;
+
+    private static final int MSG_CONFIGURE_OUTPUTS = 1;
+    private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
+    private static final int MSG_CLEANUP = 3;
+
+    private static final int PREVIEW_FRAME_TIMEOUT = 300; // ms
+    private static final int JPEG_FRAME_TIMEOUT = 1000; // ms
+
+    private boolean mPreviewRunning = false;
+
+    private volatile RequestHolder mInFlightPreview;
+    private volatile RequestHolder mInFlightJpeg;
+
+    private List<Surface> mPreviewOutputs = new ArrayList<Surface>();
+    private List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+    private GLThreadManager mGLThreadManager;
+    private SurfaceTexture mPreviewTexture;
+
+    private final RequestQueue mRequestQueue = new RequestQueue();
+    private SurfaceTexture mDummyTexture;
+    private Surface mDummySurface;
+
+    private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
+
+    /**
+     * Container object for Configure messages.
+     */
+    private static class ConfigureHolder {
+        public final ConditionVariable condition;
+        public final Collection<Surface> surfaces;
+
+        public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) {
+            this.condition = condition;
+            this.surfaces = surfaces;
+        }
+    }
+
+    /**
+     * Counter class used to calculate and log the current FPS of frame production.
+     */
+    public static class FpsCounter {
+        //TODO: Hook this up to SystTrace?
+        private static final String TAG = "FpsCounter";
+        private int mFrameCount = 0;
+        private long mLastTime = 0;
+        private long mLastPrintTime = 0;
+        private double mLastFps = 0;
+        private final String mStreamType;
+        private static final long NANO_PER_SECOND = 1000000000; //ns
+
+        public FpsCounter(String streamType) {
+            mStreamType = streamType;
+        }
+
+        public synchronized void countFrame() {
+            mFrameCount++;
+            long nextTime = SystemClock.elapsedRealtimeNanos();
+            if (mLastTime == 0) {
+                mLastTime = nextTime;
+            }
+            if (nextTime > mLastTime + NANO_PER_SECOND) {
+                long elapsed = nextTime - mLastTime;
+                mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
+                mFrameCount = 0;
+                mLastTime = nextTime;
+            }
+        }
+
+        public synchronized double checkFps() {
+            return mLastFps;
+        }
+
+        public synchronized void staggeredLog() {
+            if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
+                mLastPrintTime = mLastTime;
+                Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
+            }
+        }
+
+        public synchronized void countAndLog() {
+            countFrame();
+            staggeredLog();
+        }
+    }
+    /**
+     * Fake preview for jpeg captures when there is no active preview
+     */
+    private void createDummySurface() {
+        if (mDummyTexture == null || mDummySurface == null) {
+            mDummyTexture = new SurfaceTexture(/*ignored*/0);
+            // TODO: use smallest default sizes
+            mDummyTexture.setDefaultBufferSize(640, 480);
+            mDummySurface = new Surface(mDummyTexture);
+        }
+    }
+
+    private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
+    private final ConditionVariable mReceivedPreview = new ConditionVariable(false);
+
+    private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
+        @Override
+        public void onPictureTaken(byte[] data, Camera camera) {
+            Log.i(TAG, "Received jpeg.");
+            RequestHolder holder = mInFlightJpeg;
+            if (holder == null) {
+                Log.w(TAG, "Dropping jpeg frame.");
+                mInFlightJpeg = null;
+                return;
+            }
+            for (Surface s : holder.getHolderTargets()) {
+                if (RequestHolder.jpegType(s)) {
+                    Log.i(TAG, "Producing jpeg buffer...");
+                    LegacyCameraDevice.nativeSetSurfaceDimens(s, data.length, /*height*/1);
+                    LegacyCameraDevice.nativeProduceFrame(s, data, data.length, /*height*/1,
+                            CameraMetadataNative.NATIVE_JPEG_FORMAT);
+                }
+            }
+            mReceivedJpeg.open();
+        }
+    };
+
+    private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
+            new SurfaceTexture.OnFrameAvailableListener() {
+                @Override
+                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+                    if (DEBUG) {
+                        mPrevCounter.countAndLog();
+                    }
+                    RequestHolder holder = mInFlightPreview;
+                    if (holder == null) {
+                        Log.w(TAG, "Dropping preview frame.");
+                        mInFlightPreview = null;
+                        return;
+                    }
+                    if (holder.hasPreviewTargets()) {
+                        mGLThreadManager.queueNewFrame(holder.getHolderTargets());
+                    }
+
+                    mReceivedPreview.open();
+                }
+            };
+
+    private void stopPreview() {
+        if (mPreviewRunning) {
+            mCamera.stopPreview();
+            mPreviewRunning = false;
+        }
+    }
+
+    private void startPreview() {
+        if (!mPreviewRunning) {
+            mCamera.startPreview();
+            mPreviewRunning = true;
+        }
+    }
+
+    private void doJpegCapture(RequestHolder request) throws IOException {
+        if (!mPreviewRunning) {
+            createDummySurface();
+            mCamera.setPreviewTexture(mDummyTexture);
+            startPreview();
+        }
+        mInFlightJpeg = request;
+        // TODO: Hook up shutter callback to CameraDeviceStateListener#onCaptureStarted
+        mCamera.takePicture(/*shutter*/null, /*raw*/null, mJpegCallback);
+        mPreviewRunning = false;
+    }
+
+    private void doPreviewCapture(RequestHolder request) throws IOException {
+        mInFlightPreview = request;
+        if (mPreviewRunning) {
+            return; // Already running
+        }
+
+        mPreviewTexture.setDefaultBufferSize(640, 480); // TODO: size selection based on request
+        mCamera.setPreviewTexture(mPreviewTexture);
+        Camera.Parameters params = mCamera.getParameters();
+        List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
+        int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
+        if (DEBUG) {
+            Log.d(TAG, "doPreviewCapture - Selected range [" +
+                    bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
+                    bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
+        }
+        params.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        params.setRecordingHint(true);
+        mCamera.setParameters(params);
+
+        startPreview();
+    }
+
+    private void configureOutputs(Collection<Surface> outputs) throws IOException {
+        stopPreview();
+        if (mGLThreadManager != null) {
+            mGLThreadManager.waitUntilStarted();
+            mGLThreadManager.ignoreNewFrames();
+            mGLThreadManager.waitUntilIdle();
+        }
+        mPreviewOutputs.clear();
+        mCallbackOutputs.clear();
+        mPreviewTexture = null;
+        mInFlightPreview = null;
+        mInFlightJpeg = null;
+
+        for (Surface s : outputs) {
+            int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
+            switch (format) {
+                case CameraMetadataNative.NATIVE_JPEG_FORMAT:
+                    mCallbackOutputs.add(s);
+                    break;
+                default:
+                    mPreviewOutputs.add(s);
+                    break;
+            }
+        }
+
+        // TODO: Detect and optimize single-output paths here to skip stream teeing.
+        if (mGLThreadManager == null) {
+            mGLThreadManager = new GLThreadManager(mCameraId);
+            mGLThreadManager.start();
+        }
+        mGLThreadManager.waitUntilStarted();
+        mGLThreadManager.setConfigurationAndWait(mPreviewOutputs);
+        mGLThreadManager.allowNewFrames();
+        mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
+        mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
+    }
+
+    // Calculate the highest FPS range supported
+    private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
+        if (frameRates.size() == 0) {
+            Log.e(TAG, "No supported frame rates returned!");
+            return null;
+        }
+
+        int bestMin = 0;
+        int bestMax = 0;
+        int bestIndex = 0;
+        int index = 0;
+        for (int[] rate : frameRates) {
+            int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
+            int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
+            if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
+                bestMin = minFps;
+                bestMax = maxFps;
+                bestIndex = index;
+            }
+            index++;
+        }
+
+        return frameRates.get(bestIndex);
+    }
+
+    private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
+        private boolean mCleanup = false;
+        private List<RequestHolder> mRepeating = null;
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean handleMessage(Message msg) {
+            if (mCleanup) {
+                return true;
+            }
+
+            switch (msg.what) {
+                case MSG_CONFIGURE_OUTPUTS:
+                    ConfigureHolder config = (ConfigureHolder) msg.obj;
+                    Log.i(TAG, "Configure outputs: " + config.surfaces.size() +
+                            " surfaces configured.");
+                    try {
+                        configureOutputs(config.surfaces);
+                    } catch (IOException e) {
+                        // TODO: report error to CameraDevice
+                        throw new IOError(e);
+                    }
+                    config.condition.open();
+                    break;
+                case MSG_SUBMIT_CAPTURE_REQUEST:
+                    Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
+
+                    // Get the next burst from the request queue.
+                    Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
+                    if (nextBurst == null) {
+                        mDeviceState.setIdle();
+                        stopPreview();
+                        break;
+                    } else {
+                        // Queue another capture if we did not get the last burst.
+                        handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
+                    }
+
+                    // Complete each request in the burst
+                    List<RequestHolder> requests =
+                            nextBurst.first.produceRequestHolders(nextBurst.second);
+                    for (RequestHolder holder : requests) {
+                        mDeviceState.setCaptureStart(holder);
+                        try {
+                            if (holder.hasPreviewTargets()) {
+                                mReceivedPreview.close();
+                                doPreviewCapture(holder);
+                                if (!mReceivedPreview.block(PREVIEW_FRAME_TIMEOUT)) {
+                                    // TODO: report error to CameraDevice
+                                    Log.e(TAG, "Hit timeout for preview callback!");
+                                }
+                            }
+                            if (holder.hasJpegTargets()) {
+                                mReceivedJpeg.close();
+                                doJpegCapture(holder);
+                                mReceivedJpeg.block();
+                                if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
+                                    // TODO: report error to CameraDevice
+                                    Log.e(TAG, "Hit timeout for jpeg callback!");
+                                }
+                                mInFlightJpeg = null;
+                            }
+                        } catch (IOException e) {
+                            // TODO: err handling
+                            throw new IOError(e);
+                        }
+                        // TODO: Set fields in result.
+                        mDeviceState.setCaptureResult(holder, new CameraMetadataNative());
+                    }
+                    break;
+                case MSG_CLEANUP:
+                    mCleanup = true;
+                    if (mGLThreadManager != null) {
+                        mGLThreadManager.quit();
+                    }
+                    if (mCamera != null) {
+                        mCamera.release();
+                    }
+                    break;
+                default:
+                    throw new AssertionError("Unhandled message " + msg.what +
+                            " on RequestThread.");
+            }
+            return true;
+        }
+    };
+
+    /**
+     * Create a new RequestThreadManager.
+     *
+     * @param cameraId the id of the camera to use.
+     * @param camera an open camera object.  The RequestThreadManager takes ownership of this camera
+     *               object, and is responsible for closing it.
+     * @param deviceState a {@link CameraDeviceState} state machine.
+     */
+    public RequestThreadManager(int cameraId, Camera camera,
+                                CameraDeviceState deviceState) {
+        mCamera = camera;
+        mCameraId = cameraId;
+        String name = String.format("RequestThread-%d", cameraId);
+        TAG = name;
+        mDeviceState = deviceState;
+        mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
+    }
+
+    /**
+     * Start the request thread.
+     */
+    public void start() {
+        mRequestThread.start();
+    }
+
+    /**
+     * Flush the pending requests.
+     */
+    public void flush() {
+        // TODO: Implement flush.
+        Log.e(TAG, "flush not yet implemented.");
+    }
+
+    /**
+     * Quit the request thread, and clean up everything.
+     */
+    public void quit() {
+        Handler handler = mRequestThread.waitAndGetHandler();
+        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
+        mRequestThread.quitSafely();
+    }
+
+    /**
+     * Submit the given burst of requests to be captured.
+     *
+     * <p>If the burst is repeating, replace the current repeating burst.</p>
+     *
+     * @param requests the burst of requests to add to the queue.
+     * @param repeating true if the burst is repeating.
+     * @param frameNumber an output argument that contains either the frame number of the last frame
+     *                    that will be returned for this request, or the frame number of the last
+     *                    frame that will be returned for the current repeating request if this
+     *                    burst is set to be repeating.
+     * @return the request id.
+     */
+    public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
+            /*out*/LongParcelable frameNumber) {
+        Handler handler = mRequestThread.waitAndGetHandler();
+        int ret = mRequestQueue.submit(requests, repeating, frameNumber);
+        handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
+        return ret;
+    }
+
+    /**
+     * Cancel a repeating request.
+     *
+     * @param requestId the id of the repeating request to cancel.
+     * @return the last frame to be returned from the HAL for the given repeating request, or
+     *          {@code INVALID_FRAME} if none exists.
+     */
+    public long cancelRepeating(int requestId) {
+        return mRequestQueue.stopRepeating(requestId);
+    }
+
+
+    /**
+     * Configure with the current output Surfaces.
+     *
+     * <p>
+     * This operation blocks until the configuration is complete.
+     * </p>
+     *
+     * @param outputs a {@link java.util.Collection} of outputs to configure.
+     */
+    public void configure(Collection<Surface> outputs) {
+        Handler handler = mRequestThread.waitAndGetHandler();
+        final ConditionVariable condition = new ConditionVariable(/*closed*/false);
+        ConfigureHolder holder = new ConfigureHolder(condition, outputs);
+        handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
+        condition.block();
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
new file mode 100644
index 0000000..2f0f6bc
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -0,0 +1,522 @@
+/*
+* 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.hardware.camera2.legacy;
+
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+import android.util.Log;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A renderer class that manages the GL state, and can draw a frame into a set of output
+ * {@link Surface}s.
+ */
+public class SurfaceTextureRenderer {
+    private static final String TAG = SurfaceTextureRenderer.class.getSimpleName();
+    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+    private static final int EGL_RECORDABLE_ANDROID = 0x3142; // from EGL/eglext.h
+    private static final int GL_MATRIX_SIZE = 16;
+    private static final int VERTEX_POS_SIZE = 3;
+    private static final int VERTEX_UV_SIZE = 2;
+    private static final int EGL_COLOR_BITLENGTH = 8;
+    private static final int GLES_VERSION = 2;
+    private static final int PBUFFER_PIXEL_BYTES = 4;
+
+    private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+    private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
+    private EGLConfig mConfigs;
+
+    private class EGLSurfaceHolder {
+        Surface surface;
+        EGLSurface eglSurface;
+        int width;
+        int height;
+    }
+
+    private List<EGLSurfaceHolder> mSurfaces = new ArrayList<EGLSurfaceHolder>();
+    private List<EGLSurfaceHolder> mConversionSurfaces = new ArrayList<EGLSurfaceHolder>();
+
+    private ByteBuffer mPBufferPixels;
+
+    // Hold this to avoid GC
+    private volatile SurfaceTexture mSurfaceTexture;
+
+    private static final int FLOAT_SIZE_BYTES = 4;
+    private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+    private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+    private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+    private final float[] mTriangleVerticesData = {
+            // X, Y, Z, U, V
+            -1.0f, -1.0f, 0, 0.f, 0.f,
+            1.0f, -1.0f, 0, 1.f, 0.f,
+            -1.0f,  1.0f, 0, 0.f, 1.f,
+            1.0f,  1.0f, 0, 1.f, 1.f,
+    };
+
+    private FloatBuffer mTriangleVertices;
+
+    /**
+     * As used in this file, this vertex shader maps a unit square to the view, and
+     * tells the fragment shader to interpolate over it.  Each surface pixel position
+     * is mapped to a 2D homogeneous texture coordinate of the form (s, t, 0, 1) with
+     * s and t in the inclusive range [0, 1], and the matrix from
+     * {@link SurfaceTexture#getTransformMatrix(float[])} is used to map this
+     * coordinate to a texture location.
+     */
+    private static final String VERTEX_SHADER =
+            "uniform mat4 uMVPMatrix;\n" +
+                    "uniform mat4 uSTMatrix;\n" +
+                    "attribute vec4 aPosition;\n" +
+                    "attribute vec4 aTextureCoord;\n" +
+                    "varying vec2 vTextureCoord;\n" +
+                    "void main() {\n" +
+                    "  gl_Position = uMVPMatrix * aPosition;\n" +
+                    "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+                    "}\n";
+
+    /**
+     * This fragment shader simply draws the color in the 2D texture at
+     * the location from the {@code VERTEX_SHADER}.
+     */
+    private static final String FRAGMENT_SHADER =
+            "#extension GL_OES_EGL_image_external : require\n" +
+                    "precision mediump float;\n" +
+                    "varying vec2 vTextureCoord;\n" +
+                    "uniform samplerExternalOES sTexture;\n" +
+                    "void main() {\n" +
+                    "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+                    "}\n";
+
+    private float[] mMVPMatrix = new float[GL_MATRIX_SIZE];
+    private float[] mSTMatrix = new float[GL_MATRIX_SIZE];
+
+    private int mProgram;
+    private int mTextureID = 0;
+    private int muMVPMatrixHandle;
+    private int muSTMatrixHandle;
+    private int maPositionHandle;
+    private int maTextureHandle;
+
+    public SurfaceTextureRenderer() {
+        mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length *
+                FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
+        mTriangleVertices.put(mTriangleVerticesData).position(0);
+        Matrix.setIdentityM(mSTMatrix, 0);
+    }
+
+    private int loadShader(int shaderType, String source) {
+        int shader = GLES20.glCreateShader(shaderType);
+        checkGlError("glCreateShader type=" + shaderType);
+        GLES20.glShaderSource(shader, source);
+        GLES20.glCompileShader(shader);
+        int[] compiled = new int[1];
+        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+        if (compiled[0] == 0) {
+            Log.e(TAG, "Could not compile shader " + shaderType + ":");
+            Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
+            GLES20.glDeleteShader(shader);
+            // TODO: handle this more gracefully
+            throw new IllegalStateException("Could not compile shader " + shaderType);
+        }
+        return shader;
+    }
+
+    private int createProgram(String vertexSource, String fragmentSource) {
+        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+        if (vertexShader == 0) {
+            return 0;
+        }
+        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+        if (pixelShader == 0) {
+            return 0;
+        }
+
+        int program = GLES20.glCreateProgram();
+        checkGlError("glCreateProgram");
+        if (program == 0) {
+            Log.e(TAG, "Could not create program");
+        }
+        GLES20.glAttachShader(program, vertexShader);
+        checkGlError("glAttachShader");
+        GLES20.glAttachShader(program, pixelShader);
+        checkGlError("glAttachShader");
+        GLES20.glLinkProgram(program);
+        int[] linkStatus = new int[1];
+        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+        if (linkStatus[0] != GLES20.GL_TRUE) {
+            Log.e(TAG, "Could not link program: ");
+            Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+            GLES20.glDeleteProgram(program);
+            // TODO: handle this more gracefully
+            throw new IllegalStateException("Could not link program");
+        }
+        return program;
+    }
+
+    private void drawFrame(SurfaceTexture st) {
+        checkGlError("onDrawFrame start");
+        st.getTransformMatrix(mSTMatrix);
+
+        if (DEBUG) {
+            GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+        }
+
+        GLES20.glUseProgram(mProgram);
+        checkGlError("glUseProgram");
+
+        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
+
+        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+        GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
+                /*normalized*/ false,TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+        checkGlError("glVertexAttribPointer maPosition");
+        GLES20.glEnableVertexAttribArray(maPositionHandle);
+        checkGlError("glEnableVertexAttribArray maPositionHandle");
+
+        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+        GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT,
+                /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+        checkGlError("glVertexAttribPointer maTextureHandle");
+        GLES20.glEnableVertexAttribArray(maTextureHandle);
+        checkGlError("glEnableVertexAttribArray maTextureHandle");
+
+        Matrix.setIdentityM(mMVPMatrix, 0);
+        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
+                /*offset*/ 0);
+        GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
+                /*offset*/ 0);
+
+        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
+        checkGlError("glDrawArrays");
+        GLES20.glFinish();
+    }
+
+    /**
+     * Initializes GL state.  Call this after the EGL surface has been created and made current.
+     */
+    private void initializeGLState() {
+        mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
+        if (mProgram == 0) {
+            throw new IllegalStateException("failed creating program");
+        }
+        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+        checkGlError("glGetAttribLocation aPosition");
+        if (maPositionHandle == -1) {
+            throw new IllegalStateException("Could not get attrib location for aPosition");
+        }
+        maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
+        checkGlError("glGetAttribLocation aTextureCoord");
+        if (maTextureHandle == -1) {
+            throw new IllegalStateException("Could not get attrib location for aTextureCoord");
+        }
+
+        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
+        checkGlError("glGetUniformLocation uMVPMatrix");
+        if (muMVPMatrixHandle == -1) {
+            throw new IllegalStateException("Could not get attrib location for uMVPMatrix");
+        }
+
+        muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
+        checkGlError("glGetUniformLocation uSTMatrix");
+        if (muSTMatrixHandle == -1) {
+            throw new IllegalStateException("Could not get attrib location for uSTMatrix");
+        }
+
+        int[] textures = new int[1];
+        GLES20.glGenTextures(/*n*/ 1, textures, /*offset*/ 0);
+
+        mTextureID = textures[0];
+        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
+        checkGlError("glBindTexture mTextureID");
+
+        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
+                GLES20.GL_NEAREST);
+        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
+                GLES20.GL_LINEAR);
+        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
+                GLES20.GL_CLAMP_TO_EDGE);
+        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
+                GLES20.GL_CLAMP_TO_EDGE);
+        checkGlError("glTexParameter");
+    }
+
+    private int getTextureId() {
+        return mTextureID;
+    }
+
+    private void clearState() {
+        mSurfaces.clear();
+        mConversionSurfaces.clear();
+        mPBufferPixels = null;
+        mSurfaceTexture = null;
+    }
+
+    private void configureEGLContext() {
+        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+            throw new IllegalStateException("No EGL14 display");
+        }
+        int[] version = new int[2];
+        if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) {
+            throw new IllegalStateException("Cannot initialize EGL14");
+        }
+
+        int[] attribList = {
+                EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH,
+                EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH,
+                EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH,
+                EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
+                EGL_RECORDABLE_ANDROID, 1,
+                EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT,
+                EGL14.EGL_NONE
+        };
+        EGLConfig[] configs = new EGLConfig[1];
+        int[] numConfigs = new int[1];
+        EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0,
+                configs.length, numConfigs, /*offset*/ 0);
+        checkEglError("eglCreateContext RGB888+recordable ES2");
+        mConfigs = configs[0];
+        int[] attrib_list = {
+                EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
+                EGL14.EGL_NONE
+        };
+        mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
+                attrib_list, /*offset*/ 0);
+        checkEglError("eglCreateContext");
+        if(mEGLContext == EGL14.EGL_NO_CONTEXT) {
+            throw new IllegalStateException("No EGLContext could be made");
+        }
+    }
+
+    private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) {
+        if (surfaces == null || surfaces.size() == 0) {
+            throw new IllegalStateException("No Surfaces were provided to draw to");
+        }
+        int[] surfaceAttribs = {
+                EGL14.EGL_NONE
+        };
+        for (EGLSurfaceHolder holder : surfaces) {
+            holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs, holder.surface,
+                    surfaceAttribs, 0);
+            checkEglError("eglCreateWindowSurface");
+        }
+    }
+
+    private void configureEGLPbufferSurfaces(Collection<EGLSurfaceHolder> surfaces) {
+        if (surfaces == null || surfaces.size() == 0) {
+            throw new IllegalStateException("No Surfaces were provided to draw to");
+        }
+
+        int maxLength = 0;
+        int[] dimens = new int[2];
+        for (EGLSurfaceHolder holder : surfaces) {
+            LegacyCameraDevice.nativeDetectSurfaceDimens(holder.surface, dimens);
+            int length = dimens[0] * dimens[1];
+            // Find max surface size, ensure PBuffer can hold this many pixels
+            maxLength = (length > maxLength) ? length : maxLength;
+            int[] surfaceAttribs = {
+                    EGL14.EGL_WIDTH, dimens[0],
+                    EGL14.EGL_HEIGHT, dimens[1],
+                    EGL14.EGL_NONE
+            };
+            holder.width = dimens[0];
+            holder.height = dimens[1];
+            holder.eglSurface =
+                    EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
+            checkEglError("eglCreatePbufferSurface");
+        }
+        mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES)
+                .order(ByteOrder.nativeOrder());
+    }
+
+    private void releaseEGLContext() {
+        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+            EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
+                    EGL14.EGL_NO_CONTEXT);
+            if (mSurfaces != null) {
+                for (EGLSurfaceHolder holder : mSurfaces) {
+                    if (holder.eglSurface != null) {
+                        EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
+                    }
+                }
+            }
+            if (mConversionSurfaces != null) {
+                for (EGLSurfaceHolder holder : mConversionSurfaces) {
+                    if (holder.eglSurface != null) {
+                        EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
+                    }
+                }
+            }
+            EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
+            EGL14.eglReleaseThread();
+            EGL14.eglTerminate(mEGLDisplay);
+        }
+
+        mConfigs = null;
+        mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+        mEGLContext = EGL14.EGL_NO_CONTEXT;
+        clearState();
+    }
+
+    private void makeCurrent(EGLSurface surface) {
+        EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
+        checkEglError("makeCurrent");
+    }
+
+    private boolean swapBuffers(EGLSurface surface) {
+        boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
+        checkEglError("swapBuffers");
+        return result;
+    }
+
+    private void checkEglError(String msg) {
+        int error;
+        if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
+            throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
+        }
+    }
+
+    private void checkGlError(String msg) {
+        int error;
+        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+            throw new IllegalStateException(msg + ": GLES20 error: 0x" + Integer.toHexString(error));
+        }
+    }
+
+    /**
+     * Return the surface texture to draw to - this is the texture use to when producing output
+     * surface buffers.
+     *
+     * @return a {@link SurfaceTexture}.
+     */
+    public SurfaceTexture getSurfaceTexture() {
+        return mSurfaceTexture;
+    }
+
+    /**
+     * Set a collection of output {@link Surface}s that can be drawn to.
+     *
+     * @param surfaces a {@link Collection} of surfaces.
+     */
+    public void configureSurfaces(Collection<Surface> surfaces) {
+        releaseEGLContext();
+
+        for (Surface s : surfaces) {
+            // If pixel conversions aren't handled by egl, use a pbuffer
+            if (LegacyCameraDevice.needsConversion(s)) {
+                LegacyCameraDevice.nativeSetSurfaceFormat(s, ImageFormat.NV21);
+                EGLSurfaceHolder holder = new EGLSurfaceHolder();
+                holder.surface = s;
+                mConversionSurfaces.add(holder);
+            } else {
+                EGLSurfaceHolder holder = new EGLSurfaceHolder();
+                holder.surface = s;
+                mSurfaces.add(holder);
+            }
+        }
+
+        // Set up egl display
+        configureEGLContext();
+
+        // Set up regular egl surfaces if needed
+        if (mSurfaces.size() > 0) {
+            configureEGLOutputSurfaces(mSurfaces);
+        }
+
+        // Set up pbuffer surface if needed
+        if (mConversionSurfaces.size() > 0) {
+            configureEGLPbufferSurfaces(mConversionSurfaces);
+        }
+        makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
+                mConversionSurfaces.get(0).eglSurface);
+        initializeGLState();
+        mSurfaceTexture = new SurfaceTexture(getTextureId());
+    }
+
+    /**
+     * Draw the current buffer in the {@link SurfaceTexture} returned from
+     * {@link #getSurfaceTexture()} into the given set of target surfaces.
+     *
+     * <p>
+     * The given surfaces must be a subset of the surfaces set in the last
+     * {@link #configureSurfaces(java.util.Collection)} call.
+     * </p>
+     *
+     * @param targetSurfaces the surfaces to draw to.
+     */
+    public void drawIntoSurfaces(Collection<Surface> targetSurfaces) {
+        if ((mSurfaces == null || mSurfaces.size() == 0)
+                && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
+            return;
+        }
+        checkGlError("before updateTexImage");
+        mSurfaceTexture.updateTexImage();
+        for (EGLSurfaceHolder holder : mSurfaces) {
+            if (targetSurfaces.contains(holder.surface)) {
+                makeCurrent(holder.eglSurface);
+                drawFrame(mSurfaceTexture);
+                swapBuffers(holder.eglSurface);
+            }
+
+        }
+        for (EGLSurfaceHolder holder : mConversionSurfaces) {
+            if (targetSurfaces.contains(holder.surface)) {
+                makeCurrent(holder.eglSurface);
+                drawFrame(mSurfaceTexture);
+                mPBufferPixels.clear();
+                GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, GLES20.GL_RGBA,
+                        GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
+                checkGlError("glReadPixels");
+                int format = LegacyCameraDevice.nativeDetectSurfaceType(holder.surface);
+                LegacyCameraDevice.nativeProduceFrame(holder.surface, mPBufferPixels.array(),
+                        holder.width, holder.height, format);
+                swapBuffers(holder.eglSurface);
+            }
+        }
+    }
+
+    /**
+     * Clean up the current GL context.
+     */
+    public void cleanupEGLContext() {
+        releaseEGLContext();
+    }
+
+    /**
+     * Drop all current GL operations on the floor.
+     */
+    public void flush() {
+        // TODO: implement flush
+        Log.e(TAG, "Flush not yet implemented.");
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/package.html b/core/java/android/hardware/camera2/legacy/package.html
new file mode 100644
index 0000000..db6f78b
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 328ccbe..40cda08 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -40,6 +40,7 @@
     public static final int ALREADY_EXISTS = -17;
     public static final int BAD_VALUE = -22;
     public static final int DEAD_OBJECT = -32;
+    public static final int INVALID_OPERATION = -38;
 
     /**
      * TODO: add as error codes in Errors.h
@@ -53,6 +54,7 @@
     public static final int EOPNOTSUPP = -95;
     public static final int EUSERS = -87;
 
+
     private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
 
         @Override
@@ -125,6 +127,9 @@
             case EOPNOTSUPP:
                 UncheckedThrow.throwAnyException(new CameraRuntimeException(
                         CAMERA_DEPRECATED_HAL));
+            case INVALID_OPERATION:
+                UncheckedThrow.throwAnyException(new IllegalStateException(
+                        "Illegal state encountered in camera service."));
         }
 
         /**
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 6c4cb71..495d5c688 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -37,6 +37,10 @@
 
 /**
  * InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
+ * <p>
+ * This class is designed to be used from and only from {@link InputMethodManagerService} by using
+ * {@link InputMethodManagerService#mMethodMap} as a global lock.
+ * </p>
  */
 public class InputMethodSubtypeSwitchingController {
     private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
@@ -101,6 +105,17 @@
             }
             return mSubtypeName.toString().compareTo(other.mSubtypeName.toString());
         }
+
+        @Override
+        public String toString() {
+            return "ImeSubtypeListItem{"
+                    + "mImeName=" + mImeName
+                    + " mSubtypeName=" + mSubtypeName
+                    + " mSubtypeId=" + mSubtypeId
+                    + " mIsSystemLocale=" + mIsSystemLocale
+                    + " mIsSystemLanguage=" + mIsSystemLanguage
+                    + "}";
+        }
     }
 
     private static class InputMethodAndSubtypeList {
@@ -196,12 +211,11 @@
         }
     }
 
-    private final Object mLock = new Object();
     private final InputMethodSettings mSettings;
     private InputMethodAndSubtypeList mSubtypeList;
 
     @VisibleForTesting
-    public static ImeSubtypeListItem getNextInputMethodImpl(List<ImeSubtypeListItem> imList,
+    public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList,
             boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
         if (imi == null) {
             return null;
@@ -245,34 +259,34 @@
         return null;
     }
 
-    public InputMethodSubtypeSwitchingController(InputMethodSettings settings) {
+    private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
         mSettings = settings;
+        resetCircularListLocked(context);
+    }
+
+    public static InputMethodSubtypeSwitchingController createInstanceLocked(
+            InputMethodSettings settings, Context context) {
+        return new InputMethodSubtypeSwitchingController(settings, context);
     }
 
     // TODO: write unit tests for this method and the logic that determines the next subtype
-    public void onCommitText(InputMethodInfo imi, InputMethodSubtype subtype) {
+    public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
         // TODO: Implement this.
     }
 
     public void resetCircularListLocked(Context context) {
-        synchronized(mLock) {
-            mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
-        }
+        mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
     }
 
-    public ImeSubtypeListItem getNextInputMethod(
+    public ImeSubtypeListItem getNextInputMethodLocked(
             boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
-        synchronized(mLock) {
-            return getNextInputMethodImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
-                    onlyCurrentIme, imi, subtype);
-        }
+        return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
+                onlyCurrentIme, imi, subtype);
     }
 
-    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes,
+    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
             boolean inputShown, boolean isScreenLocked) {
-        synchronized(mLock) {
-            return mSubtypeList.getSortedInputMethodAndSubtypeList(
-                    showSubtypes, inputShown, isScreenLocked);
-        }
+        return mSubtypeList.getSortedInputMethodAndSubtypeList(
+                showSubtypes, inputShown, isScreenLocked);
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8428f66..24e55e4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -328,11 +328,13 @@
 
     int mLastDischargeStepLevel;
     long mLastDischargeStepTime;
+    int mMinDischargeStepLevel;
     int mNumDischargeStepDurations;
     final long[] mDischargeStepDurations = new long[MAX_LEVEL_STEPS];
 
     int mLastChargeStepLevel;
     long mLastChargeStepTime;
+    int mMaxChargeStepLevel;
     int mNumChargeStepDurations;
     final long[] mChargeStepDurations = new long[MAX_LEVEL_STEPS];
 
@@ -887,6 +889,7 @@
             mLastTime = 0;
             mUnpluggedTime = in.readLong();
             timeBase.add(this);
+            if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
         }
 
         Timer(int type, TimeBase timeBase) {
@@ -917,6 +920,8 @@
         }
 
         public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+            if (DEBUG) Log.i(TAG, "**** WRITING TIMER #" + mType + ": mTotalTime="
+                    + computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
             out.writeInt(mCount);
             out.writeInt(mLoadedCount);
             out.writeInt(mUnpluggedCount);
@@ -5550,6 +5555,7 @@
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase);
         }
+        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
         mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -5581,7 +5587,6 @@
         }
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
-        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
         mOnBattery = mOnBatteryInternal = false;
         long uptime = SystemClock.uptimeMillis() * 1000;
         long realtime = SystemClock.elapsedRealtime() * 1000;
@@ -5958,6 +5963,7 @@
                 mNumDischargeStepDurations = 0;
             }
             mLastDischargeStepLevel = level;
+            mMinDischargeStepLevel = level;
             mLastDischargeStepTime = -1;
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
@@ -5996,6 +6002,7 @@
             updateTimeBasesLocked(false, !screenOn, uptime, realtime);
             mNumChargeStepDurations = 0;
             mLastChargeStepLevel = level;
+            mMaxChargeStepLevel = level;
             mLastChargeStepTime = -1;
         }
         if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
@@ -6117,19 +6124,21 @@
                     addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 if (onBattery) {
-                    if (mLastDischargeStepLevel != level) {
+                    if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
                         mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
                                 mNumDischargeStepDurations, mLastDischargeStepTime,
                                 mLastDischargeStepLevel - level, elapsedRealtime);
                         mLastDischargeStepLevel = level;
+                        mMinDischargeStepLevel = level;
                         mLastDischargeStepTime = elapsedRealtime;
                     }
                 } else {
-                    if (mLastChargeStepLevel != level) {
+                    if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                         mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
                                 mNumChargeStepDurations, mLastChargeStepTime,
                                 level - mLastChargeStepLevel, elapsedRealtime);
                         mLastChargeStepLevel = level;
+                        mMaxChargeStepLevel = level;
                         mLastChargeStepTime = elapsedRealtime;
                     }
                 }
@@ -7495,6 +7504,8 @@
             mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase,
                     in);
         }
+        mInteractive = false;
+        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
         mPhoneOn = false;
         mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
@@ -7536,8 +7547,6 @@
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOn = false;
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
-        mInteractive = false;
-        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
         mDischargeUnplugLevel = in.readInt();
         mDischargePlugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 1366499..a01e9b7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,9 +24,9 @@
 {
     void setIcon(int index, in StatusBarIcon icon);
     void removeIcon(int index);
-    void addNotification(IBinder key, in StatusBarNotification notification);
-    void updateNotification(IBinder key, in StatusBarNotification notification);
-    void removeNotification(IBinder key);
+    void addNotification(in StatusBarNotification notification);
+    void updateNotification(in StatusBarNotification notification);
+    void removeNotification(String key);
     void disable(int state);
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel();
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 2d6cf2e..a3b417f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -39,8 +39,8 @@
     // ---- Methods below are for use by the status bar policy services ----
     // You need the STATUS_BAR_SERVICE permission
     void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
-            out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications,
-            out int[] switches, out List<IBinder> binders);
+            out List<StatusBarNotification> notifications, out int[] switches,
+            out List<IBinder> binders);
     void onPanelRevealed();
     void onPanelHidden();
     void onNotificationClick(String key);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 83c26f6..2d72494 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -137,6 +137,7 @@
 	android_media_ToneGenerator.cpp \
 	android_hardware_Camera.cpp \
 	android_hardware_camera2_CameraMetadata.cpp \
+	android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
 	android_hardware_SensorManager.cpp \
 	android_hardware_SerialPort.cpp \
 	android_hardware_UsbDevice.cpp \
@@ -171,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 \
@@ -231,6 +234,7 @@
 	libaudioutils \
 	libpdfium \
 	libimg_utils \
+	libnetd_client \
 
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 948b51e..2d350e0 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -79,6 +79,7 @@
 
 extern int register_android_hardware_Camera(JNIEnv *env);
 extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
+extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
 extern int register_android_hardware_SensorManager(JNIEnv *env);
 extern int register_android_hardware_SerialPort(JNIEnv *env);
 extern int register_android_hardware_UsbDevice(JNIEnv *env);
@@ -1284,6 +1285,7 @@
     REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
     REG_JNI(register_android_hardware_Camera),
     REG_JNI(register_android_hardware_camera2_CameraMetadata),
+    REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
     REG_JNI(register_android_hardware_SensorManager),
     REG_JNI(register_android_hardware_SerialPort),
     REG_JNI(register_android_hardware_UsbDevice),
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_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 307293f..3a53331 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -27,6 +27,7 @@
 
 #include <cutils/properties.h>
 #include <utils/Vector.h>
+#include <utils/Errors.h>
 
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
@@ -464,7 +465,7 @@
 }
 
 // connect to camera service
-static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
+static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
     jobject weak_this, jint cameraId, jstring clientPackageName)
 {
     // Convert jstring to String16
@@ -477,20 +478,19 @@
             Camera::USE_CALLING_UID);
 
     if (camera == NULL) {
-        jniThrowRuntimeException(env, "Fail to connect to camera service");
-        return;
+        return -EACCES;
     }
 
     // make sure camera hardware is alive
     if (camera->getStatus() != NO_ERROR) {
-        jniThrowRuntimeException(env, "Camera initialization failed");
-        return;
+        return NO_INIT;
     }
 
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
+        // This should never happen
         jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
-        return;
+        return INVALID_OPERATION;
     }
 
     // We use a weak reference so the Camera object can be garbage collected.
@@ -501,6 +501,7 @@
 
     // save context in opaque field
     env->SetLongField(thiz, fields.context, (jlong)context.get());
+    return NO_ERROR;
 }
 
 // disconnect from camera service
@@ -538,9 +539,9 @@
     }
 }
 
-static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
+static void android_hardware_Camera_setPreviewSurface(JNIEnv *env, jobject thiz, jobject jSurface)
 {
-    ALOGV("setPreviewDisplay");
+    ALOGV("setPreviewSurface");
     sp<Camera> camera = get_native_camera(env, thiz, NULL);
     if (camera == 0) return;
 
@@ -890,14 +891,14 @@
     "(ILandroid/hardware/Camera$CameraInfo;)V",
     (void*)android_hardware_Camera_getCameraInfo },
   { "native_setup",
-    "(Ljava/lang/Object;ILjava/lang/String;)V",
+    "(Ljava/lang/Object;ILjava/lang/String;)I",
     (void*)android_hardware_Camera_native_setup },
   { "native_release",
     "()V",
     (void*)android_hardware_Camera_release },
-  { "setPreviewDisplay",
+  { "setPreviewSurface",
     "(Landroid/view/Surface;)V",
-    (void *)android_hardware_Camera_setPreviewDisplay },
+    (void *)android_hardware_Camera_setPreviewSurface },
   { "setPreviewTexture",
     "(Landroid/graphics/SurfaceTexture;)V",
     (void *)android_hardware_Camera_setPreviewTexture },
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 3312109..0d2df80 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -518,7 +518,7 @@
     SortedVector<String8> vendorSections;
     size_t vendorSectionCount = 0;
 
-    if (vTags != 0) {
+    if (vTags != NULL) {
         vendorSections = vTags->getAllSectionNames();
         vendorSectionCount = vendorSections.size();
     }
@@ -592,7 +592,7 @@
                                  "Could not find tag name for key '%s')", key);
             return 0;
         }
-    } else if (vTags != 0) {
+    } else if (vTags != NULL) {
         // Match vendor tags (typically com.*)
         const String8 sectionName(section);
         const String8 tagName(keyTagName);
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
new file mode 100644
index 0000000..40e9544
--- /dev/null
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -0,0 +1,419 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Legacy-CameraDevice-JNI"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/Trace.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
+
+#include <ui/GraphicBuffer.h>
+#include <system/window.h>
+
+using namespace android;
+
+// fully-qualified class name
+#define CAMERA_DEVICE_CLASS_NAME "android/hardware/camera2/legacy/LegacyCameraDevice"
+#define CAMERA_DEVICE_BUFFER_SLACK  3
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
+
+/**
+ * Convert from RGB 888 to Y'CbCr using the conversion specified in ITU-R BT.601 for
+ * digital RGB with K_b = 0.114, and K_r = 0.299.
+ */
+static void rgbToYuv420(uint8_t* rgbBuf, int32_t width, int32_t height, uint8_t* yPlane,
+        uint8_t* uPlane, uint8_t* vPlane, size_t chromaStep, size_t yStride, size_t chromaStride) {
+    uint8_t R, G, B;
+    size_t index = 0;
+
+    int32_t cStrideDiff = chromaStride - width;
+
+    for (int32_t j = 0; j < height; j++) {
+        for (int32_t i = 0; i < width; i++) {
+            R = rgbBuf[index++];
+            G = rgbBuf[index++];
+            B = rgbBuf[index++];
+            *(yPlane + i) = ((66 * R + 129 * G +  25 * B + 128) >> 8) +  16;
+
+            if (j % 2 == 0 && i % 2 == 0){
+                *uPlane = (( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128;
+                *vPlane = (( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128;
+                uPlane += chromaStep;
+                vPlane += chromaStep;
+            }
+            // Skip alpha
+            index++;
+        }
+        yPlane += yStride;
+        if (j % 2 == 0) {
+            uPlane += cStrideDiff;
+            vPlane += cStrideDiff;
+        }
+    }
+}
+
+static void rgbToYuv420(uint8_t* rgbBuf, int32_t width, int32_t height, android_ycbcr* ycbcr) {
+    size_t cStep = ycbcr->chroma_step;
+    size_t cStride = ycbcr->cstride;
+    size_t yStride = ycbcr->ystride;
+    rgbToYuv420(rgbBuf, width, height, reinterpret_cast<uint8_t*>(ycbcr->y),
+            reinterpret_cast<uint8_t*>(ycbcr->cb), reinterpret_cast<uint8_t*>(ycbcr->cr),
+            cStep, yStride, cStride);
+}
+
+static status_t configureSurface(const sp<ANativeWindow>& anw,
+                                 int32_t width,
+                                 int32_t height,
+                                 int32_t pixelFmt,
+                                 int32_t maxBufferSlack) {
+    status_t err = NO_ERROR;
+    err = native_window_set_buffers_dimensions(anw.get(), width, height);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to set native window buffer dimensions, error %s (%d).", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+
+    err = native_window_set_buffers_format(anw.get(), pixelFmt);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to set native window buffer format, error %s (%d).", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+
+    err = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to set native window usage flag, error %s (%d).", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+
+    int minUndequeuedBuffers;
+    err = anw.get()->query(anw.get(),
+            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+            &minUndequeuedBuffers);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to get native window min undequeued buffers, error %s (%d).",
+                __FUNCTION__, strerror(-err), err);
+        return err;
+    }
+
+    ALOGV("%s: Setting buffer count to %d", __FUNCTION__,
+          maxBufferSlack + 1 + minUndequeuedBuffers);
+    err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+    return NO_ERROR;
+}
+
+/**
+ * Produce a frame in the given surface.
+ *
+ * Args:
+ *    anw - a surface to produce a frame in.
+ *    pixelBuffer - image buffer to generate a frame from.
+ *    width - width of the pixelBuffer in pixels.
+ *    height - height of the pixelBuffer in pixels.
+ *    pixelFmt - format of the pixelBuffer, one of:
+ *               HAL_PIXEL_FORMAT_YCrCb_420_SP,
+ *               HAL_PIXEL_FORMAT_YCbCr_420_888,
+ *               HAL_PIXEL_FORMAT_BLOB
+ *    bufSize - the size of the pixelBuffer in bytes.
+ */
+static status_t produceFrame(const sp<ANativeWindow>& anw,
+                             uint8_t* pixelBuffer,
+                             int32_t width, // Width of the pixelBuffer
+                             int32_t height, // Height of the pixelBuffer
+                             int32_t pixelFmt, // Format of the pixelBuffer
+                             int64_t bufSize) {
+    ATRACE_CALL();
+    status_t err = NO_ERROR;
+    ANativeWindowBuffer* anb;
+    ALOGV("%s: Dequeue buffer from %p",__FUNCTION__, anw.get());
+
+    // TODO: Switch to using Surface::lock and Surface::unlockAndPost
+    err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
+    if (err != NO_ERROR) return err;
+
+    sp<GraphicBuffer> buf(new GraphicBuffer(anb, /*keepOwnership*/false));
+
+    switch(pixelFmt) {
+        case HAL_PIXEL_FORMAT_YCrCb_420_SP: {
+            if (bufSize < width * height * 4) {
+                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
+                        bufSize);
+                return BAD_VALUE;
+            }
+            uint8_t* img = NULL;
+            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
+            err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+            if (err != NO_ERROR) return err;
+
+            uint8_t* yPlane = img;
+            uint8_t* uPlane = img + height * width;
+            uint8_t* vPlane = uPlane + 1;
+            size_t chromaStep = 2;
+            size_t yStride = width;
+            size_t chromaStride = width;
+
+            rgbToYuv420(pixelBuffer, width, height, yPlane,
+                    uPlane, vPlane, chromaStep, yStride, chromaStride);
+            break;
+        }
+        case HAL_PIXEL_FORMAT_YCbCr_420_888: {
+            // Software writes with YCbCr_420_888 format are unsupported
+            // by the gralloc module for now
+            if (bufSize < width * height * 4) {
+                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
+                      bufSize);
+                return BAD_VALUE;
+            }
+            android_ycbcr ycbcr = android_ycbcr();
+            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
+
+            err = buf->lockYCbCr(GRALLOC_USAGE_SW_WRITE_OFTEN, &ycbcr);
+            if (err != NO_ERROR) {
+                ALOGE("%s: Failed to lock ycbcr buffer, error %s (%d).", __FUNCTION__,
+                        strerror(-err), err);
+                return err;
+            }
+            rgbToYuv420(pixelBuffer, width, height, &ycbcr);
+            break;
+        }
+        case HAL_PIXEL_FORMAT_BLOB: {
+            if (bufSize != width || height != 1) {
+                ALOGE("%s: Incorrect pixelBuffer size: %lld", __FUNCTION__, bufSize);
+                return BAD_VALUE;
+            }
+            int8_t* img = NULL;
+
+            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
+            err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+            if (err != NO_ERROR) {
+                ALOGE("%s: Failed to lock buffer, error %s (%d).", __FUNCTION__, strerror(-err),
+                        err);
+                return err;
+            }
+            memcpy(img, pixelBuffer, width);
+            break;
+        }
+        default: {
+            ALOGE("%s: Invalid pixel format in produceFrame: %x", __FUNCTION__, pixelFmt);
+            return BAD_VALUE;
+        }
+    }
+
+    ALOGV("%s: Unlock buffer from %p", __FUNCTION__, anw.get());
+    err = buf->unlock();
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to unlock buffer, error %s (%d).", __FUNCTION__, strerror(-err), err);
+        return err;
+    }
+
+    ALOGV("%s: Queue buffer to %p", __FUNCTION__, anw.get());
+    err = anw->queueBuffer(anw.get(), buf->getNativeBuffer(), /*fenceFd*/-1);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Failed to queue buffer, error %s (%d).", __FUNCTION__, strerror(-err), err);
+        return err;
+    }
+    return NO_ERROR;
+}
+
+static sp<ANativeWindow> getNativeWindow(JNIEnv* env, jobject surface) {
+    sp<ANativeWindow> anw;
+    if (surface) {
+        anw = android_view_Surface_getNativeWindow(env, surface);
+        if (env->ExceptionCheck()) {
+            return anw;
+        }
+    } else {
+        jniThrowNullPointerException(env, "surface");
+        return anw;
+    }
+    if (anw == NULL) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "Surface had no valid native window.");
+        return anw;
+    }
+    return anw;
+}
+
+extern "C" {
+
+static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) {
+    ALOGV("nativeDetectSurfaceType");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return 0;
+    }
+    int32_t fmt = 0;
+    status_t err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt);
+    if(err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                                "Error while querying surface pixel format (error code %d)", err);
+        return 0;
+    }
+    return fmt;
+}
+
+static void LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz,
+          jobject surface, jintArray dimens) {
+    ALOGV("nativeGetSurfaceDimens");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return;
+    }
+    int32_t dimenBuf[2];
+    status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
+    if(err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                        "Error while querying surface width (error code %d)", err);
+        return;
+    }
+    err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
+    if(err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Error while querying surface height (error code %d)", err);
+        return;
+    }
+    env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf);
+}
+
+static void LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface,
+        jint width, jint height, jint pixelFormat) {
+    ALOGV("nativeConfigureSurface");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return;
+    }
+    status_t err = configureSurface(anw, width, height, pixelFormat, CAMERA_DEVICE_BUFFER_SLACK);
+    if (err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Error while producing frame (error code %d)", err);
+        return;
+    }
+}
+
+static void LegacyCameraDevice_nativeProduceFrame(JNIEnv* env, jobject thiz, jobject surface,
+        jbyteArray pixelBuffer, jint width, jint height, jint pixelFormat) {
+    ALOGV("nativeProduceFrame");
+    sp<ANativeWindow> anw;
+
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return;
+    }
+
+    if (pixelBuffer == NULL) {
+        jniThrowNullPointerException(env, "pixelBuffer");
+        return;
+    }
+
+    int32_t bufSize = static_cast<int32_t>(env->GetArrayLength(pixelBuffer));
+    jbyte* pixels = env->GetByteArrayElements(pixelBuffer, /*is_copy*/NULL);
+
+    if (pixels == NULL) {
+        jniThrowNullPointerException(env, "pixels");
+        return;
+    }
+
+    status_t err = produceFrame(anw, reinterpret_cast<uint8_t*>(pixels), width, height,
+            pixelFormat, bufSize);
+    env->ReleaseByteArrayElements(pixelBuffer, pixels, JNI_ABORT);
+
+    if (err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Error while producing frame (error code %d)", err);
+        return;
+    }
+}
+
+static void LegacyCameraDevice_nativeSetSurfaceFormat(JNIEnv* env, jobject thiz, jobject surface,
+        jint pixelFormat) {
+    ALOGV("nativeSetSurfaceType");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return;
+    }
+    status_t err = native_window_set_buffers_format(anw.get(), pixelFormat);
+    if (err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Error while setting surface format (error code %d)", err);
+        return;
+    }
+}
+
+static void LegacyCameraDevice_nativeSetSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface,
+        jint width, jint height) {
+    ALOGV("nativeSetSurfaceDimens");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return;
+    }
+    status_t err = native_window_set_buffers_dimensions(anw.get(), width, height);
+    if (err != NO_ERROR) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Error while setting surface format (error code %d)", err);
+        return;
+    }
+}
+
+} // extern "C"
+
+static JNINativeMethod gCameraDeviceMethods[] = {
+    { "nativeDetectSurfaceType",
+    "(Landroid/view/Surface;)I",
+    (void *)LegacyCameraDevice_nativeDetectSurfaceType },
+    { "nativeDetectSurfaceDimens",
+    "(Landroid/view/Surface;[I)V",
+    (void *)LegacyCameraDevice_nativeDetectSurfaceDimens },
+    { "nativeConfigureSurface",
+    "(Landroid/view/Surface;III)V",
+    (void *)LegacyCameraDevice_nativeConfigureSurface },
+    { "nativeProduceFrame",
+    "(Landroid/view/Surface;[BIII)V",
+    (void *)LegacyCameraDevice_nativeProduceFrame },
+    { "nativeSetSurfaceFormat",
+    "(Landroid/view/Surface;I)V",
+    (void *)LegacyCameraDevice_nativeSetSurfaceFormat },
+    { "nativeSetSurfaceDimens",
+    "(Landroid/view/Surface;II)V",
+    (void *)LegacyCameraDevice_nativeSetSurfaceDimens },
+};
+
+// Get all the required offsets in java class and register native functions
+int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv* env)
+{
+    // Register native functions
+    return AndroidRuntime::registerNativeMethods(env,
+            CAMERA_DEVICE_CLASS_NAME,
+            gCameraDeviceMethods,
+            NELEM(gCameraDeviceMethods));
+}
+
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/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
index 80e34cb..e109425f4 100644
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ b/core/res/res/layout/alert_dialog_quantum.xml
@@ -89,10 +89,11 @@
         android:layout_height="wrap_content"
         android:minHeight="@dimen/alert_dialog_button_bar_height"
         android:orientation="vertical"
+        android:gravity="end"
         android:padding="16dip">
         <LinearLayout
             style="?android:attr/buttonBarStyle"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layoutDirection="locale">
             <Button android:id="@+id/button3"
@@ -102,11 +103,6 @@
                 android:layout_marginRight="8dip"
                 android:maxLines="2"
                 android:minHeight="@dimen/alert_dialog_button_bar_height" />
-            <View
-                android:layout_width="0dp"
-                android:layout_height="@dimen/alert_dialog_button_bar_height"
-                android:layout_weight="1"
-                android:visibility="invisible" />
             <Button android:id="@+id/button2"
                 style="?android:attr/buttonBarButtonStyle"
                 android:layout_width="wrap_content"
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/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index e714e12..e528278 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -364,7 +364,6 @@
         <item name="textColor">?attr/textColorPrimary</item>
         <item name="minHeight">48dip</item>
         <item name="minWidth">96dip</item>
-        <item name="elevation">2dip</item>
         <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item>
     </style>
 
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 2a4d921..0f343b1 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -105,44 +105,44 @@
 
         // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
         currentIme = imList.get(0);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                 imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                         currentIme.mSubtypeName.toString()));
         assertEquals(imList.get(3), nextIme);
@@ -158,46 +158,46 @@
 
         // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
         currentIme = imList.get(0);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        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.getNextInputMethodImpl(
+        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                 imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                         currentIme.mSubtypeName.toString()));
         assertEquals(imList.get(3), nextIme);
 
         // "switchAwareJapaneseIme/ja_JP" -> null
         currentIme = imList.get(5);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                 imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                         currentIme.mSubtypeName.toString()));
         assertNull(nextIme);
 
         // "nonSwitchAwareJapaneseIme/ja_JP" -> null
         currentIme = imList.get(6);
-        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl(
                 imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                         currentIme.mSubtypeName.toString()));
         assertNull(nextIme);
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/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 6755f3e..f3fcf2c 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -28,6 +28,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
@@ -588,47 +589,22 @@
         return mBitmapState.mPaint.getColorFilter();
     }
 
-    /**
-     * Specifies a tint for this drawable.
-     * <p>
-     * Setting a color filter via {@link #setColorFilter(ColorFilter)} overrides
-     * tint.
-     *
-     * @param tint Color state list to use for tinting this drawable, or null to
-     *            clear the tint
-     */
-    public void setTint(ColorStateList tint) {
-        if (mBitmapState.mTint != tint) {
-            mBitmapState.mTint = tint;
-            computeTintFilter();
-            invalidateSelf();
-        }
+    @Override
+    public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
+        mBitmapState.mTint = tint;
+        mBitmapState.mTintMode = tintMode;
+        computeTintFilter();
+        invalidateSelf();
     }
 
     /**
-     * Returns the tint color for this drawable.
-     *
-     * @return Color state list to use for tinting this drawable, or null if
-     *         none set
+     * @hide only needed by a hack within ProgressBar
      */
     public ColorStateList getTint() {
         return mBitmapState.mTint;
     }
 
     /**
-     * Specifies the blending mode used to apply tint.
-     *
-     * @param tintMode A Porter-Duff blending mode
-     */
-    public void setTintMode(Mode tintMode) {
-        if (mBitmapState.mTintMode != tintMode) {
-            mBitmapState.mTintMode = tintMode;
-            computeTintFilter();
-            invalidateSelf();
-        }
-    }
-
-    /**
      * @hide only needed by a hack within ProgressBar
      */
     public Mode getTintMode() {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index d6fb9e0..cc2a595 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -226,7 +227,7 @@
      * By default, this returns the full drawable bounds. Custom drawables may
      * override this method to perform more precise invalidation.
      *
-     * @hide
+     * @return The dirty bounds of this drawable
      */
     public Rect getDirtyBounds() {
         return getBounds();
@@ -469,6 +470,18 @@
     }
 
     /**
+     * Specifies a tint and blending mode for this drawable.
+     * <p>
+     * Setting a color filter via {@link #setColorFilter(ColorFilter)} overrides
+     * tint.
+     *
+     * @param tint Color state list to use for tinting this drawable, or null to
+     *            clear the tint
+     * @param tintMode A Porter-Duff blending mode
+     */
+    public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {}
+
+    /**
      * Returns the current color filter, or {@code null} if none set.
      *
      * @return the current color filter, or {@code null} if none set
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 3e09707..0a07332 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -28,6 +28,7 @@
 import android.graphics.NinePatch;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
@@ -327,44 +328,12 @@
         invalidateSelf();
     }
 
-    /**
-     * Specifies a tint for this drawable.
-     * <p>
-     * Setting a color filter via {@link #setColorFilter(ColorFilter)} overrides
-     * tint.
-     *
-     * @param tint Color state list to use for tinting this drawable, or null to
-     *            clear the tint
-     */
-    public void setTint(ColorStateList tint) {
-        if (mNinePatchState.mTint != tint) {
-            mNinePatchState.mTint = tint;
-            computeTintFilter();
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the tint color for this drawable.
-     *
-     * @return Color state list to use for tinting this drawable, or null if
-     *         none set
-     */
-    public ColorStateList getTint() {
-        return mNinePatchState.mTint;
-    }
-
-    /**
-     * Specifies the blending mode used to apply tint.
-     *
-     * @param tintMode A Porter-Duff blending mode
-     */
-    public void setTintMode(Mode tintMode) {
-        if (mNinePatchState.mTintMode != tintMode) {
-            mNinePatchState.mTintMode = tintMode;
-            computeTintFilter();
-            invalidateSelf();
-        }
+    @Override
+    public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
+        mNinePatchState.mTint = tint;
+        mNinePatchState.mTintMode = tintMode;
+        computeTintFilter();
+        invalidateSelf();
     }
 
     private void computeTintFilter() {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index add3d84..1bd7cac 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -207,35 +207,9 @@
         return true;
     }
 
-    /**
-     * Specifies a tint for drawing touch feedback ripples.
-     *
-     * @param tint Color state list to use for tinting touch feedback ripples,
-     *        or null to clear the tint
-     */
-    public void setTint(ColorStateList tint) {
-        if (mState.mTint != tint) {
-            mState.mTint = tint;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the tint color for touch feedback ripples.
-     *
-     * @return Color state list to use for tinting touch feedback ripples, or
-     *         null if none set
-     */
-    public ColorStateList getTint() {
-        return mState.mTint;
-    }
-
-    /**
-     * Specifies the blending mode used to draw touch feedback ripples.
-     *
-     * @param tintMode A Porter-Duff blending mode
-     */
-    public void setTintMode(Mode tintMode) {
+    @Override
+    public void setTint(ColorStateList tint, Mode tintMode) {
+        mState.mTint = tint;
         mState.setTintMode(tintMode);
         invalidateSelf();
     }
@@ -243,11 +217,13 @@
     @Override
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
-        final TypedArray a = obtainAttributes(
-                r, theme, attrs, R.styleable.RippleDrawable);
+        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RippleDrawable);
         updateStateFromTypedArray(a);
         a.recycle();
 
+        // Force padding default to STACK before inflating.
+        setPaddingMode(PADDING_MODE_STACK);
+
         super.inflate(r, parser, attrs, theme);
 
         setTargetDensity(r.getDisplayMetrics());
@@ -275,6 +251,25 @@
     }
 
     /**
+     * Specifies how layer padding should affect the bounds of subsequent
+     * layers. The default and recommended value for RippleDrawable is
+     * {@link #PADDING_MODE_STACK}.
+     *
+     * @param mode padding mode, one of:
+     *            <ul>
+     *            <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
+     *            padding of the previous layer
+     *            <li>{@link #PADDING_MODE_STACK} to stack each layer directly
+     *            atop the previous layer
+     *            </ul>
+     * @see #getPaddingMode()
+     */
+    @Override
+    public void setPaddingMode(int mode) {
+        super.setPaddingMode(mode);
+    }
+
+    /**
      * Initializes the constant state from the values in the typed array.
      */
     private void updateStateFromTypedArray(TypedArray a) {
@@ -643,8 +638,7 @@
         Drawable mMask;
         boolean mPinned = false;
 
-        public RippleState(
-                RippleState orig, RippleDrawable owner, Resources res) {
+        public RippleState(RippleState orig, RippleDrawable owner, Resources res) {
             super(orig, owner, res);
 
             if (orig != null) {
@@ -653,7 +647,6 @@
                 mTintXfermode = orig.mTintXfermode;
                 mTintXfermodeInverse = orig.mTintXfermodeInverse;
                 mPinned = orig.mPinned;
-                mMask = orig.mMask;
             }
         }
 
@@ -740,6 +733,8 @@
         }
 
         mState = ns;
+        mState.mMask = findDrawableByLayerId(R.id.mask);
+
         mLayerState = ns;
 
         if (ns.mNum > 0) {
@@ -749,7 +744,5 @@
         if (needsTheme) {
             applyTheme(theme);
         }
-
-        setPaddingMode(PADDING_MODE_STACK);
     }
 }
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/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 7a86811..36cfb0f 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -764,21 +764,30 @@
         return -1;
     }
 
-    if (ctx->getBufferFormat() != buffer->format) {
-        // Return the buffer to the queue.
-        consumer->unlockBuffer(*buffer);
-        ctx->returnLockedBuffer(buffer);
+    int imgReaderFmt = ctx->getBufferFormat();
+    int bufFmt = buffer->format;
+    if (imgReaderFmt != bufFmt) {
+        // Special casing for when producer switches format
+        if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && bufFmt ==
+                HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+            ctx->setBufferFormat(HAL_PIXEL_FORMAT_YCrCb_420_SP);
+            ALOGV("%s: Overriding NV21 to YUV_420_888.", __FUNCTION__);
+        } else {
+            // Return the buffer to the queue.
+            consumer->unlockBuffer(*buffer);
+            ctx->returnLockedBuffer(buffer);
 
-        // Throw exception
-        ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
-              buffer->format, ctx->getBufferFormat());
-        String8 msg;
-        msg.appendFormat("The producer output buffer format 0x%x doesn't "
-                "match the ImageReader's configured buffer format 0x%x.",
-                buffer->format, ctx->getBufferFormat());
-        jniThrowException(env, "java/lang/UnsupportedOperationException",
-                msg.string());
-        return -1;
+            // Throw exception
+            ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
+                    buffer->format, ctx->getBufferFormat());
+            String8 msg;
+            msg.appendFormat("The producer output buffer format 0x%x doesn't "
+                    "match the ImageReader's configured buffer format 0x%x.",
+                    buffer->format, ctx->getBufferFormat());
+            jniThrowException(env, "java/lang/UnsupportedOperationException",
+                    msg.string());
+            return -1;
+        }
     }
     // Set SurfaceImage instance member variables
     Image_setBuffer(env, image, buffer);
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 457d32e..252a850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -81,6 +81,8 @@
 import java.util.ArrayList;
 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";
@@ -173,6 +175,7 @@
      */
     protected int mState;
     protected boolean mBouncerShowing;
+    protected boolean mShowLockscreenNotifications;
 
     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
 
@@ -192,6 +195,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);
         }
     };
 
@@ -208,33 +214,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);
         }
     };
 
@@ -280,6 +300,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),
@@ -299,14 +322,13 @@
 
         // Connect in to the status bar manager service
         StatusBarIconList iconList = new StatusBarIconList();
-        ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
         ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
         mCommandQueue = new CommandQueue(this, iconList);
 
         int[] switches = new int[8];
         ArrayList<IBinder> binders = new ArrayList<IBinder>();
         try {
-            mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
+            mBarService.registerStatusBar(mCommandQueue, iconList, notifications,
                     switches, binders);
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
@@ -332,15 +354,10 @@
             }
         }
 
-        // Set up the initial notification state
-        N = notificationKeys.size();
-        if (N == notifications.size()) {
-            for (int i=0; i<N; i++) {
-                addNotification(notificationKeys.get(i), notifications.get(i));
-            }
-        } else {
-            Log.wtf(TAG, "Notification list length mismatch: keys=" + N
-                    + " notifications=" + notifications.size());
+        // Set up the initial notification state.
+        N = notifications.size();
+        for (int i=0; i<N; i++) {
+            addNotification(notifications.get(i));
         }
 
         if (DEBUG) {
@@ -381,6 +398,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;
@@ -946,47 +971,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);
+            });
         }
     }
 
@@ -1018,8 +1051,8 @@
      *
      * WARNING: this will call back into us.  Don't hold any locks.
      */
-    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
-        removeNotification(key);
+    void handleNotificationError(StatusBarNotification n, String message) {
+        removeNotification(n.getKey());
         try {
             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
                     n.getInitialPid(), message, n.getUserId());
@@ -1028,7 +1061,7 @@
         }
     }
 
-    protected StatusBarNotification removeNotificationViews(IBinder key) {
+    protected StatusBarNotification removeNotificationViews(String key) {
         NotificationData.Entry entry = mNotificationData.remove(key);
         if (entry == null) {
             Log.w(TAG, "removeNotification for unknown key: " + key);
@@ -1043,10 +1076,9 @@
         return entry.notification;
     }
 
-    protected NotificationData.Entry createNotificationViews(IBinder key,
-            StatusBarNotification notification) {
+    protected NotificationData.Entry createNotificationViews(StatusBarNotification notification) {
         if (DEBUG) {
-            Log.d(TAG, "createNotificationViews(key=" + key + ", notification=" + notification);
+            Log.d(TAG, "createNotificationViews(notification=" + notification);
         }
         // Construct the icon.
         final StatusBarIconView iconView = new StatusBarIconView(mContext,
@@ -1061,13 +1093,13 @@
                     notification.getNotification().number,
                     notification.getNotification().tickerText);
         if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
+            handleNotificationError(notification, "Couldn't create icon: " + ic);
             return null;
         }
         // Construct the expanded view.
-        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
+        NotificationData.Entry entry = new NotificationData.Entry(notification, iconView);
         if (!inflateViews(entry, mStackScroller)) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+            handleNotificationError(notification, "Couldn't expand RemoteViews for: "
                     + notification);
             return null;
         }
@@ -1087,8 +1119,8 @@
         updateRowStates();
     }
 
-    private void addNotificationViews(IBinder key, StatusBarNotification notification) {
-        addNotificationViews(createNotificationViews(key, notification));
+    private void addNotificationViews(StatusBarNotification notification) {
+        addNotificationViews(createNotificationViews(notification));
     }
 
     /**
@@ -1157,10 +1189,14 @@
         updateNotificationIcons();
     }
 
+    protected void setShowLockscreenNotifications(boolean show) {
+        mShowLockscreenNotifications = show;
+    }
+
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
     protected abstract void updateNotificationIcons();
-    protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
+    protected abstract void tick(StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
     protected abstract boolean shouldDisableNavbarGestures();
 
@@ -1168,12 +1204,12 @@
         return parent != null && parent.indexOfChild(entry.row) == 0;
     }
 
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
+    public void updateNotification(StatusBarNotification notification) {
+        if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
-        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
+        final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
         if (oldEntry == null) {
-            Log.w(TAG, "updateNotification for unknown key: " + key);
+            Log.w(TAG, "updateNotification for unknown key: " + notification.getKey());
             return;
         }
 
@@ -1252,7 +1288,7 @@
         boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
         if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
                 && (orderUnchanged || isTopAnyway)) {
-            if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
+            if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
             oldEntry.notification = notification;
             try {
                 updateNotificationViews(oldEntry, notification);
@@ -1276,7 +1312,7 @@
                         notification.getNotification().number,
                         notification.getNotification().tickerText);
                 if (!oldEntry.icon.set(ic)) {
-                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+                    handleNotificationError(notification, "Couldn't update icon: " + ic);
                     return;
                 }
                 updateRowStates();
@@ -1284,17 +1320,18 @@
             catch (RuntimeException e) {
                 // It failed to add cleanly.  Log, and remove the view from the panel.
                 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(key);
-                addNotificationViews(key, notification);
+                removeNotificationViews(notification.getKey());
+                addNotificationViews(notification);
             }
         } else {
-            if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
+            if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
             if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
             if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
             if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
-            removeNotificationViews(key);
-            addNotificationViews(key, notification);  // will also replace the heads up
-            final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
+            removeNotificationViews(notification.getKey());
+            addNotificationViews(notification);  // will also replace the heads up
+            final NotificationData.Entry newEntry = mNotificationData.findByKey(
+                    notification.getKey());
             final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
             if (userChangedExpansion) {
                 boolean userExpanded = oldEntry.row.isUserExpanded();
@@ -1314,7 +1351,7 @@
         // Restart the ticker if it's still running
         if (updateTicker && isForCurrentUser) {
             haltTicker();
-            tick(key, notification, false);
+            tick(notification, false);
         }
 
         // Recalculate the position of the sliding windows and the titles.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index b4a347b..aaeadb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,7 +21,6 @@
 import android.os.Message;
 import android.service.notification.StatusBarNotification;
 
-import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
@@ -73,11 +72,6 @@
     private Callbacks mCallbacks;
     private Handler mHandler = new H();
 
-    private class NotificationQueueEntry {
-        IBinder key;
-        StatusBarNotification notification;
-    }
-
     /**
      * These methods are called back on the main thread.
      */
@@ -86,9 +80,9 @@
         public void updateIcon(String slot, int index, int viewIndex,
                 StatusBarIcon old, StatusBarIcon icon);
         public void removeIcon(String slot, int index, int viewIndex);
-        public void addNotification(IBinder key, StatusBarNotification notification);
-        public void updateNotification(IBinder key, StatusBarNotification notification);
-        public void removeNotification(IBinder key);
+        public void addNotification(StatusBarNotification notification);
+        public void updateNotification(StatusBarNotification notification);
+        public void removeNotification(String key);
         public void disable(int state);
         public void animateExpandNotificationsPanel();
         public void animateCollapsePanels(int flags);
@@ -106,7 +100,6 @@
         public void showSearchPanel();
         public void hideSearchPanel();
         public void setWindowState(int window, int state);
-
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -130,25 +123,21 @@
         }
     }
 
-    public void addNotification(IBinder key, StatusBarNotification notification) {
+    @Override
+    public void addNotification(StatusBarNotification notification) {
         synchronized (mList) {
-            NotificationQueueEntry ne = new NotificationQueueEntry();
-            ne.key = key;
-            ne.notification = notification;
-            mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, ne).sendToTarget();
+            mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, notification).sendToTarget();
         }
     }
 
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
+    @Override
+    public void updateNotification(StatusBarNotification notification) {
         synchronized (mList) {
-            NotificationQueueEntry ne = new NotificationQueueEntry();
-            ne.key = key;
-            ne.notification = notification;
-            mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, ne).sendToTarget();
+            mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, notification).sendToTarget();
         }
     }
 
-    public void removeNotification(IBinder key) {
+    public void removeNotification(String key) {
         synchronized (mList) {
             mHandler.obtainMessage(MSG_REMOVE_NOTIFICATION, 0, 0, key).sendToTarget();
         }
@@ -291,17 +280,15 @@
                     break;
                 }
                 case MSG_ADD_NOTIFICATION: {
-                    final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj;
-                    mCallbacks.addNotification(ne.key, ne.notification);
+                    mCallbacks.addNotification((StatusBarNotification) msg.obj);
                     break;
                 }
                 case MSG_UPDATE_NOTIFICATION: {
-                    final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj;
-                    mCallbacks.updateNotification(ne.key, ne.notification);
+                    mCallbacks.updateNotification((StatusBarNotification) msg.obj);
                     break;
                 }
                 case MSG_REMOVE_NOTIFICATION: {
-                    mCallbacks.removeNotification((IBinder)msg.obj);
+                    mCallbacks.removeNotification((String) msg.obj);
                     break;
                 }
                 case MSG_DISABLE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 8440b9f..bd511f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -18,8 +18,6 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.os.Binder;
-import android.os.IBinder;
 import android.os.Process;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
@@ -33,13 +31,14 @@
 public class InterceptedNotifications {
     private static final String TAG = "InterceptedNotifications";
     private static final String EXTRA_INTERCEPT = "android.intercept";
+    private static final String SYNTHETIC_KEY = "InterceptedNotifications.SYNTHETIC_KEY";
 
     private final Context mContext;
     private final PhoneStatusBar mBar;
-    private final ArrayMap<IBinder, StatusBarNotification> mIntercepted
-            = new ArrayMap<IBinder, StatusBarNotification>();
+    private final ArrayMap<String, StatusBarNotification> mIntercepted
+            = new ArrayMap<String, StatusBarNotification>();
 
-    private Binder mSynKey;
+    private String mSynKey;
 
     public InterceptedNotifications(Context context, PhoneStatusBar bar) {
         mContext = context;
@@ -49,36 +48,35 @@
     public void releaseIntercepted() {
         final int n = mIntercepted.size();
         for (int i = 0; i < n; i++) {
-            final IBinder key = mIntercepted.keyAt(i);
             final StatusBarNotification sbn = mIntercepted.valueAt(i);
             sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
-            mBar.addNotification(key, sbn);
+            mBar.addNotification(sbn);
         }
         mIntercepted.clear();
         updateSyntheticNotification();
     }
 
-    public boolean tryIntercept(IBinder key, StatusBarNotification notification) {
+    public boolean tryIntercept(StatusBarNotification notification) {
         if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false;
         if (shouldDisplayIntercepted()) return false;
-        mIntercepted.put(key, notification);
+        mIntercepted.put(notification.getKey(), notification);
         updateSyntheticNotification();
         return true;
     }
 
-    public void remove(IBinder key) {
+    public void remove(String key) {
         if (mIntercepted.remove(key) != null) {
             updateSyntheticNotification();
         }
     }
 
     public boolean isSyntheticEntry(Entry ent) {
-        return mSynKey != null && ent.key.equals(mSynKey);
+        return ent.key.equals(SYNTHETIC_KEY);
     }
 
-    public void update(IBinder key, StatusBarNotification notification) {
-        if (mIntercepted.containsKey(key)) {
-            mIntercepted.put(key, notification);
+    public void update(StatusBarNotification notification) {
+        if (mIntercepted.containsKey(notification.getKey())) {
+            mIntercepted.put(notification.getKey(), notification);
         }
     }
 
@@ -108,10 +106,10 @@
                 TAG.hashCode(), TAG, Process.myUid(), Process.myPid(), 0, n,
                 mBar.getCurrentUserHandle());
         if (mSynKey == null) {
-            mSynKey = new Binder();
-            mBar.addNotification(mSynKey, sbn);
+            mSynKey = sbn.getKey();
+            mBar.addNotification(sbn);
         } else {
-           mBar.updateNotification(mSynKey, sbn);
+           mBar.updateNotification(sbn);
         }
         final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
         entry.row.setOnClickListener(mSynClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index b1a5750..5696246 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import android.os.IBinder;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.widget.ImageView;
@@ -29,7 +28,7 @@
  */
 public class NotificationData {
     public static final class Entry {
-        public IBinder key;
+        public String key;
         public StatusBarNotification notification;
         public StatusBarIconView icon;
         public ExpandableNotificationRow row; // the outer expanded view
@@ -39,8 +38,8 @@
         public View expandedBig;
         private boolean interruption;
         public Entry() {}
-        public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
-            this.key = key;
+        public Entry(StatusBarNotification n, StatusBarIconView ic) {
+            this.key = n.getKey();
             this.notification = n;
             this.icon = ic;
         }
@@ -63,6 +62,7 @@
             interruption = true;
         }
     }
+
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
     private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
         // sort first by score, then by when
@@ -88,9 +88,9 @@
         return mEntries.get(i);
     }
 
-    public Entry findByKey(IBinder key) {
+    public Entry findByKey(String key) {
         for (Entry e : mEntries) {
-            if (e.key == key) {
+            if (e.key.equals(key)) {
                 return e;
             }
         }
@@ -100,7 +100,7 @@
     public int add(Entry entry) {
         int i;
         int N = mEntries.size();
-        for (i=0; i<N; i++) {
+        for (i = 0; i < N; i++) {
             if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
                 break;
             }
@@ -109,7 +109,7 @@
         return i;
     }
 
-    public Entry remove(IBinder key) {
+    public Entry remove(String key) {
         Entry e = findByKey(key);
         if (e != null) {
             mEntries.remove(e);
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/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
new file mode 100644
index 0000000..6a83a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -0,0 +1,186 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.content.res.Resources;
+import android.graphics.Path;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.R;
+
+/**
+ * Utility class to calculate the clock position and top padding of notifications on Keyguard.
+ */
+public class KeyguardClockPositionAlgorithm {
+
+    private static final float SLOW_DOWN_FACTOR = 0.4f;
+
+    private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
+    private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
+
+    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
+    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
+
+    private int mClockNotificationsMarginMin;
+    private int mClockNotificationsMarginMax;
+    private float mClockYFractionMin;
+    private float mClockYFractionMax;
+    private int mMaxKeyguardNotifications;
+    private int mMaxPanelHeight;
+    private float mExpandedHeight;
+    private int mNotificationCount;
+    private int mHeight;
+    private int mKeyguardStatusHeight;
+
+    /**
+     * The number (fractional) of notifications the "more" card counts when calculating how many
+     * notifications are currently visible for the y positioning of the clock.
+     */
+    private float mMoreCardNotificationAmount;
+
+    private static final PathInterpolator sSlowDownInterpolator;
+
+    static {
+        Path path = new Path();
+        path.moveTo(0, 0);
+        path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
+        sSlowDownInterpolator = new PathInterpolator(path);
+    }
+
+    /**
+     * Refreshes the dimension values.
+     */
+    public void loadDimens(Resources res) {
+        mClockNotificationsMarginMin = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin_min);
+        mClockNotificationsMarginMax = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin_max);
+        mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
+        mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
+        mMoreCardNotificationAmount =
+                (float) res.getDimensionPixelSize(R.dimen.notification_summary_height) /
+                        res.getDimensionPixelSize(R.dimen.notification_min_height);
+    }
+
+    public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
+            int notificationCount, int height, int keyguardStatusHeight) {
+        mMaxKeyguardNotifications = maxKeyguardNotifications;
+        mMaxPanelHeight = maxPanelHeight;
+        mExpandedHeight = expandedHeight;
+        mNotificationCount = notificationCount;
+        mHeight = height;
+        mKeyguardStatusHeight = keyguardStatusHeight;
+    }
+
+    public void run(Result result) {
+        int y = getClockY() - mKeyguardStatusHeight/2;
+        float clockAdjustment = getClockYExpansionAdjustment();
+        float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
+        result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+        int clockNotificationsPadding = getClockNotificationsPadding()
+                + result.stackScrollerPaddingAdjustment;
+        int padding = y + clockNotificationsPadding;
+        y += clockAdjustment;
+        result.clockY = y;
+        result.stackScrollerPadding = mKeyguardStatusHeight + padding;
+        result.clockAlpha = getClockAlpha(result.stackScrollerPadding
+                - (y + mKeyguardStatusHeight));
+    }
+
+    private int getClockNotificationsPadding() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
+    }
+
+    private float getClockYFraction() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
+    }
+
+    private int getClockY() {
+        return (int) (getClockYFraction() * mHeight);
+    }
+
+    private float getClockYExpansionAdjustment() {
+        float rubberbandFactor = getClockYExpansionRubberbandFactor();
+        float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
+        float t = value / mMaxPanelHeight;
+        float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
+                * mMaxPanelHeight;
+        if (mNotificationCount == 0) {
+            return (-2*value + slowedDownValue)/3;
+        } else {
+            return slowedDownValue;
+        }
+    }
+
+    private float getClockYExpansionRubberbandFactor() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        t = (float) Math.pow(t, 0.3f);
+        return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
+    }
+
+    private float getTopPaddingAdjMultiplier() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
+                + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
+    }
+
+    private float getClockAlpha(int clockNotificationPadding) {
+        float t = getNotificationAmountT();
+        t = (float) Math.pow(t, 0.3f);
+        float multiplier = 1 + 2 * (1 - t);
+        float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3;
+        return Math.max(0, Math.min(1, alpha));
+    }
+
+    /**
+     * @return a value from 0 to 1 depending on how many notification there are
+     */
+    private float getNotificationAmountT() {
+        return mNotificationCount
+                / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
+    }
+
+    public static class Result {
+
+        /**
+         * The y translation of the clock.
+         */
+        public int clockY;
+
+        /**
+         * The alpha value of the clock.
+         */
+        public float clockAlpha;
+
+        /**
+         * The top padding of the stack scroller, in pixels.
+         */
+        public int stackScrollerPadding;
+
+        /**
+         * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
+         * the padding, but not the overall panel size.
+         */
+        public int stackScrollerPaddingAdjustment;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7e3bf68..fe7546d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -19,10 +19,11 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Path;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -31,6 +32,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
@@ -82,19 +84,14 @@
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
 
-    private int mClockNotificationsMarginMin;
-    private int mClockNotificationsMarginMax;
-    private float mClockYFractionMin;
-    private float mClockYFractionMax;
     private Interpolator mFastOutSlowInInterpolator;
     private ObjectAnimator mClockAnimator;
     private int mClockAnimationTarget = -1;
-
-    /**
-     * The number (fractional) of notifications the "more" card counts when calculating how many
-     * notifications are currently visible for the y positioning of the clock.
-     */
-    private float mMoreCardNotificationAmount;
+    private int mTopPaddingAdjustment;
+    private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
+            new KeyguardClockPositionAlgorithm();
+    private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
+            new KeyguardClockPositionAlgorithm.Result();
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -139,20 +136,10 @@
         mNotificationTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notifications_top_padding);
         mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
-        mClockNotificationsMarginMin = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_min);
-        mClockNotificationsMarginMax = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_max);
-        mClockYFractionMin =
-                getResources().getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
-        mClockYFractionMax =
-                getResources().getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
-        mMoreCardNotificationAmount =
-                (float) getResources().getDimensionPixelSize(R.dimen.notification_summary_height) /
-                        getResources().getDimensionPixelSize(R.dimen.notification_min_height);
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
         mStatusBarMinHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
+        mClockPositionAlgorithm.loadDimens(getResources());
     }
 
     @Override
@@ -160,6 +147,7 @@
         super.onLayout(changed, left, top, right, bottom);
         if (!mQsExpanded) {
             positionClockAndNotifications();
+            mNotificationStackScroller.setStackHeight(getExpandedHeight());
         }
 
         // Calculate quick setting heights.
@@ -178,16 +166,24 @@
         boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
             mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding;
+            mTopPaddingAdjustment = 0;
         } else {
-            int notificationCount = mNotificationStackScroller.getNotGoneChildCount();
-            int y = getClockY(notificationCount) - mKeyguardStatusView.getHeight()/2;
-            int padding = getClockNotificationsPadding(notificationCount);
+            mClockPositionAlgorithm.setup(
+                    mStatusBar.getMaxKeyguardNotifications(),
+                    getMaxPanelHeight(),
+                    getExpandedHeight(),
+                    mNotificationStackScroller.getNotGoneChildCount(),
+                    getHeight(),
+                    mKeyguardStatusView.getHeight());
+            mClockPositionAlgorithm.run(mClockPositionResult);
             if (animateClock || mClockAnimator != null) {
-                startClockAnimation(y);
+                startClockAnimation(mClockPositionResult.clockY);
             } else {
-                mKeyguardStatusView.setY(y);
+                mKeyguardStatusView.setY(mClockPositionResult.clockY);
             }
-            mStackScrollerIntrinsicPadding = y + mKeyguardStatusView.getHeight() + padding;
+            applyClockAlpha(mClockPositionResult.clockAlpha);
+            mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding;
+            mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
                 mAnimateNextTopPaddingChange || animateClock);
@@ -224,22 +220,13 @@
         });
     }
 
-    private int getClockNotificationsPadding(int notificationCount) {
-        float t = notificationCount
-                / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
-        t = Math.min(t, 1.0f);
-        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
-    }
-
-    private float getClockYFraction(int notificationCount) {
-        float t = notificationCount
-                / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
-        t = Math.min(t, 1.0f);
-        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
-    }
-
-    private int getClockY(int notificationCount) {
-        return (int) (getClockYFraction(notificationCount) * getHeight());
+    private void applyClockAlpha(float alpha) {
+        if (alpha != 1.0f) {
+            mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null);
+        } else {
+            mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null);
+        }
+        mKeyguardStatusView.setAlpha(alpha);
     }
 
     public void animateToFullShade() {
@@ -545,6 +532,9 @@
     }
     private void flingSettings(float vel, boolean expand) {
         float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
+        if (target == mQsExpansionHeight) {
+            return;
+        }
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
         mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -623,7 +613,7 @@
         int emptyBottomMargin = mStackScrollerContainer.getHeight()
                 - mNotificationStackScroller.getHeight()
                 + mNotificationStackScroller.getEmptyBottomMargin();
-        int maxHeight = maxPanelHeight - emptyBottomMargin;
+        int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment;
         maxHeight = Math.max(maxHeight, mStatusBarMinHeight);
         return maxHeight;
     }
@@ -634,6 +624,9 @@
 
     @Override
     protected void onHeightUpdated(float expandedHeight) {
+        if (!mQsExpanded) {
+            positionClockAndNotifications();
+        }
         mNotificationStackScroller.setStackHeight(expandedHeight);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index b6a43a7..8631e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -402,7 +402,7 @@
 
     public void setExpandedHeightInternal(float h) {
         float fh = getMaxPanelHeight();
-        mExpandedHeight = h;
+        mExpandedHeight = Math.min(fh, h);
 
         if (DEBUG) {
             logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");
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 54af2c5..574ce62 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();
@@ -1027,18 +1034,19 @@
         return new UserHandle(mCurrentUserId);
     }
 
-    public void addNotification(IBinder key, StatusBarNotification notification) {
+    @Override
+    public void addNotification(StatusBarNotification notification) {
         if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
-        Entry shadeEntry = createNotificationViews(key, notification);
+        Entry shadeEntry = createNotificationViews(notification);
         if (shadeEntry == null) {
             return;
         }
-        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(key, notification)) {
+        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
             return;
         }
         if (mUseHeadsUp && shouldInterrupt(notification)) {
             if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
-            Entry interruptionCandidate = new Entry(key, notification, null);
+            Entry interruptionCandidate = new Entry(notification, null);
             ViewGroup holder = mHeadsUpNotificationView.getHolder();
             if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
                 mInterruptingNotificationTime = System.currentTimeMillis();
@@ -1070,7 +1078,7 @@
 
             // show the ticker if there isn't already a heads up
             if (mInterruptingNotificationEntry == null) {
-                tick(null, notification, true);
+                tick(notification, true);
             }
         }
         addNotificationViews(shadeEntry);
@@ -1089,12 +1097,13 @@
     }
 
     @Override
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        super.updateNotification(key, notification);
-        mIntercepted.update(key, notification);
+    public void updateNotification(StatusBarNotification notification) {
+        super.updateNotification(notification);
+        mIntercepted.update(notification);
     }
 
-    public void removeNotification(IBinder key) {
+    @Override
+    public void removeNotification(String key) {
         StatusBarNotification old = removeNotificationViews(key);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
@@ -2019,7 +2028,7 @@
     public void setHardKeyboardStatus(boolean available, boolean enabled) {}
 
     @Override
-    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
+    protected void tick(StatusBarNotification n, boolean firstTime) {
         // no ticking in lights-out mode
         if (!areLightsOn()) return;
 
@@ -2344,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) {
@@ -2772,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/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 9006c9a..846d248 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -46,15 +46,15 @@
     }
 
     @Override
-    public void addNotification(IBinder key, StatusBarNotification notification) {
+    public void addNotification(StatusBarNotification notification) {
     }
 
     @Override
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
+    public void updateNotification(StatusBarNotification notification) {
     }
 
     @Override
-    public void removeNotification(IBinder key) {
+    public void removeNotification(String key) {
     }
 
     @Override
@@ -113,7 +113,7 @@
     }
 
     @Override
-    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
+    protected void tick(StatusBarNotification n, boolean firstTime) {
     }
 
     @Override
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 0ad5ce2..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;
@@ -5750,10 +5709,11 @@
                 // updateNetworkSettings();
             }
             // notify battery stats service about this network
-//            try {
-                // TODO
-                //BatteryStatsService.getService().noteNetworkInterfaceType(iface, netType);
-//            } catch (RemoteException e) { }
+            try {
+                BatteryStatsService.getService().noteNetworkInterfaceType(
+                        newNetwork.linkProperties.getInterfaceName(),
+                        newNetwork.networkInfo.getType());
+            } catch (RemoteException e) { }
             notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
         } else {
             if (DBG && newNetwork.networkRequests.size() != 0) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 2d270e7..fb69c86 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -692,8 +692,10 @@
                 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
         updateCurrentProfileIds();
         mFileManager = new InputMethodFileManager(mMethodMap, userId);
-        mSwitchingController = new InputMethodSubtypeSwitchingController(mSettings);
-        mSwitchingController.resetCircularListLocked(context);
+        synchronized (mMethodMap) {
+            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                    mSettings, context);
+        }
 
         // Just checking if defaultImiId is empty or not
         final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -702,17 +704,23 @@
         }
         mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
 
-        buildInputMethodListLocked(mMethodList, mMethodMap,
-                !mImeSelectedOnBoot /* resetDefaultEnabledIme */);
+        synchronized (mMethodMap) {
+            buildInputMethodListLocked(mMethodList, mMethodMap,
+                    !mImeSelectedOnBoot /* resetDefaultEnabledIme */);
+        }
         mSettings.enableAllIMEsIfThereIsNoEnabledIME();
 
         if (!mImeSelectedOnBoot) {
             Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
-            resetDefaultImeLocked(context);
+            synchronized (mMethodMap) {
+                resetDefaultImeLocked(context);
+            }
         }
 
         mSettingsObserver = new SettingsObserver(mHandler);
-        updateFromSettingsLocked(true);
+        synchronized (mMethodMap) {
+            updateFromSettingsLocked(true);
+        }
 
         // IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME
         // according to the new system locale.
@@ -2174,7 +2182,7 @@
             return false;
         }
         synchronized (mMethodMap) {
-            final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethod(
+            final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
                     onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
             if (nextSubtype == null) {
                 return false;
@@ -2190,7 +2198,7 @@
             return false;
         }
         synchronized (mMethodMap) {
-            final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethod(
+            final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
                     false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype);
             if (nextSubtype == null) {
                 return false;
@@ -2273,9 +2281,11 @@
         if (DEBUG) {
             Slog.d(TAG, "Got the notification of commitText");
         }
-        final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
-        if (imi != null) {
-            mSwitchingController.onCommitText(imi, mCurrentSubtype);
+        synchronized (mMethodMap) {
+            final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
+            if (imi != null) {
+                mSwitchingController.onCommitTextLocked(imi, mCurrentSubtype);
+            }
         }
     }
 
@@ -2698,7 +2708,7 @@
             hideInputMethodMenuLocked();
 
             final List<ImeSubtypeListItem> imList =
-                    mSwitchingController.getSortedInputMethodAndSubtypeList(
+                    mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
                             showSubtypes, mInputShown, isScreenLocked);
 
             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index cf91782..137387e 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -240,9 +240,8 @@
         mPhoneStateListener = new PhoneStateListener(mDaemonHandler.getLooper()) {
             public void onDataConnectionRealTimeInfoChanged(
                     DataConnectionRealTimeInfo dcRtInfo) {
-                // Disabled for now, until we are getting good data.
-                //notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
-                //        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
+                notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
+                        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
             }
         };
 
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0b3e02a6..b9a69ab 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -457,7 +457,7 @@
     {
         final StatusBarNotification sbn;
         SingleNotificationStats stats;
-        IBinder statusBarKey;
+        boolean isCanceled;
 
         // These members are used by NotificationSignalExtractors
         // to communicate with the ranking module.
@@ -472,6 +472,7 @@
         public Notification getNotification() { return sbn.getNotification(); }
         public int getFlags() { return sbn.getNotification().flags; }
         public int getUserId() { return sbn.getUserId(); }
+        public String getKey() { return sbn.getKey(); }
 
         void dump(PrintWriter pw, String prefix, Context baseContext) {
             final Notification notification = sbn.getNotification();
@@ -1668,7 +1669,7 @@
                         "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
                 synchronized (mNotificationList) {
                     NotificationRecord old = null;
-                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
+                    int index = indexOfNotificationLocked(n.getKey());
                     if (index < 0) {
                         mNotificationList.add(r);
                         mUsageStats.registerPostedByApp(r);
@@ -1677,12 +1678,8 @@
                         mNotificationList.set(index, r);
                         mUsageStats.registerUpdatedByApp(r, old);
                         // Make sure we don't lose the foreground service state.
-                        if (old != null) {
-                            notification.flags |=
-                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
-                        }
-                    }
-                    if (old != null) {
+                        notification.flags |=
+                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                         mNotificationsByKey.remove(old.sbn.getKey());
                     }
                     mNotificationsByKey.put(n.getKey(), r);
@@ -1705,18 +1702,17 @@
                     }
 
                     if (notification.icon != 0) {
-                        if (old != null && old.statusBarKey != null) {
-                            r.statusBarKey = old.statusBarKey;
+                        if (old != null && !old.isCanceled) {
                             final long identity = Binder.clearCallingIdentity();
                             try {
-                                mStatusBar.updateNotification(r.statusBarKey, n);
+                                mStatusBar.updateNotification(n);
                             } finally {
                                 Binder.restoreCallingIdentity(identity);
                             }
                         } else {
                             final long identity = Binder.clearCallingIdentity();
                             try {
-                                r.statusBarKey = mStatusBar.addNotification(n);
+                                mStatusBar.addNotification(n);
                                 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
                                         && canInterrupt) {
                                     mAttentionLight.pulse();
@@ -1733,10 +1729,10 @@
                         mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked());
                     } else {
                         Slog.e(TAG, "Not posting notification with icon==0: " + notification);
-                        if (old != null && old.statusBarKey != null) {
+                        if (old != null && !old.isCanceled) {
                             final long identity = Binder.clearCallingIdentity();
                             try {
-                                mStatusBar.removeNotification(old.statusBarKey);
+                                mStatusBar.removeNotification(r.getKey());
                             } finally {
                                 Binder.restoreCallingIdentity(identity);
                             }
@@ -2120,11 +2116,11 @@
         if (r.getNotification().icon != 0) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                mStatusBar.removeNotification(r.statusBarKey);
+                mStatusBar.removeNotification(r.getKey());
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-            r.statusBarKey = null;
+            r.isCanceled = true;
             mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
         }
 
@@ -2392,6 +2388,18 @@
         return -1;
     }
 
+    // lock on mNotificationList
+    int indexOfNotificationLocked(String key) {
+        NotificationRecord r = mNotificationsByKey.get(key);
+        if (r == null) {
+            return -1;
+        }
+        int index = Collections.binarySearch(mNotificationList, r, mRankingComparator);
+        // Guarantee to return -1 when not found.
+        return (index >= 0) ? index : -1;
+    }
+
+
     private void updateNotificationPulse() {
         synchronized (mNotificationList) {
             updateLightsLocked();
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/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 4f75189..8d905ba 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -23,7 +23,7 @@
 
 public interface StatusBarManagerInternal {
     void setNotificationDelegate(NotificationDelegate delegate);
-    IBinder addNotification(StatusBarNotification notification);
-    void updateNotification(IBinder key, StatusBarNotification notification);
-    void removeNotification(IBinder key);
+    void addNotification(StatusBarNotification notification);
+    void updateNotification(StatusBarNotification notification);
+    void removeNotification(String key);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8b8c73d..55b5e3b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -60,8 +60,8 @@
     private NotificationDelegate mNotificationDelegate;
     private volatile IStatusBar mBar;
     private StatusBarIconList mIcons = new StatusBarIconList();
-    private HashMap<IBinder,StatusBarNotification> mNotifications
-            = new HashMap<IBinder,StatusBarNotification>();
+    private HashMap<String,StatusBarNotification> mNotifications
+            = new HashMap<String,StatusBarNotification>();
 
     // for disabling the status bar
     private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
@@ -117,30 +117,29 @@
         }
 
         @Override
-        public IBinder addNotification(StatusBarNotification notification) {
+        public void addNotification(StatusBarNotification notification) {
             synchronized (mNotifications) {
-                IBinder key = new Binder();
-                mNotifications.put(key, notification);
+                mNotifications.put(notification.getKey(), notification);
                 if (mBar != null) {
                     try {
-                        mBar.addNotification(key, notification);
+                        mBar.addNotification(notification);
                     } catch (RemoteException ex) {
                     }
                 }
-                return key;
             }
         }
 
         @Override
-        public void updateNotification(IBinder key, StatusBarNotification notification) {
+        public void updateNotification(StatusBarNotification notification) {
             synchronized (mNotifications) {
+                String key = notification.getKey();
                 if (!mNotifications.containsKey(key)) {
                     throw new IllegalArgumentException("updateNotification key not found: " + key);
                 }
-                mNotifications.put(key, notification);
+                mNotifications.put(notification.getKey(), notification);
                 if (mBar != null) {
                     try {
-                        mBar.updateNotification(key, notification);
+                        mBar.updateNotification(notification);
                     } catch (RemoteException ex) {
                     }
                 }
@@ -148,7 +147,7 @@
         }
 
         @Override
-        public void removeNotification(IBinder key) {
+        public void removeNotification(String key) {
             synchronized (mNotifications) {
                 final StatusBarNotification n = mNotifications.remove(key);
                 if (n == null) {
@@ -512,8 +511,7 @@
     // ================================================================================
     @Override
     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
-            List<IBinder> notificationKeys, List<StatusBarNotification> notifications,
-            int switches[], List<IBinder> binders) {
+            List<StatusBarNotification> notifications, int switches[], List<IBinder> binders) {
         enforceStatusBarService();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -522,9 +520,8 @@
             iconList.copyFrom(mIcons);
         }
         synchronized (mNotifications) {
-            for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
-                notificationKeys.add(e.getKey());
-                notifications.add(e.getValue());
+            for (StatusBarNotification sbn : mNotifications.values()) {
+                notifications.add(sbn);
             }
         }
         synchronized (mLock) {
@@ -714,7 +711,7 @@
         synchronized (mNotifications) {
             int i=0;
             pw.println("Notification list:");
-            for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+            for (Map.Entry<String,StatusBarNotification> e: mNotifications.entrySet()) {
                 pw.printf("  %2d: %s\n", i, e.getValue().toString());
                 i++;
             }
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/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 9ace36f..492b08e 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -40,7 +40,7 @@
         @Override
         public void onStateChanged(Connection c, int state) {
             String id = mIdByConnection.get(c);
-            Log.d(this, "Adapter set state %d %s", id, Connection.stateToString(state));
+            Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
             switch (state) {
                 case Connection.State.ACTIVE:
                     getAdapter().setActive(id);
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;