Merge "Ensuring that the second card is maximally visible when you go into recents." into lmp-preview-dev
diff --git a/Android.mk b/Android.mk
index 9c41f9f..8603d99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
 	core/java/android/app/IServiceConnection.aidl \
 	core/java/android/app/IStopUserCallback.aidl \
 	core/java/android/app/task/ITaskCallback.aidl \
+	core/java/android/app/task/ITaskManager.aidl \
 	core/java/android/app/task/ITaskService.aidl \
 	core/java/android/app/IThumbnailRetriever.aidl \
 	core/java/android/app/ITransientNotification.aidl \
@@ -216,6 +217,8 @@
 	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
 	core/java/android/service/wallpaper/IWallpaperService.aidl \
 	core/java/android/tv/ITvInputClient.aidl \
+	core/java/android/tv/ITvInputHardware.aidl \
+	core/java/android/tv/ITvInputHardwareCallback.aidl \
 	core/java/android/tv/ITvInputManager.aidl \
 	core/java/android/tv/ITvInputService.aidl \
 	core/java/android/tv/ITvInputServiceCallback.aidl \
@@ -326,6 +329,7 @@
 	telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
+	telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c841338..48a20a4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -191,6 +191,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index 282f062..9c56fb5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1399,8 +1399,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 +4536,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 +4577,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 +4620,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 +4651,52 @@
     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_FULL_SCREEN = 5; // 0x5
+    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);
@@ -5298,6 +5351,58 @@
 
 package android.app.task {
 
+  public class Task implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBackoffPolicy();
+    method public android.os.Bundle getExtras();
+    method public int getId();
+    method public long getInitialBackoffMillis();
+    method public long getIntervalMillis();
+    method public long getMaxExecutionDelayMillis();
+    method public long getMinLatencyMillis();
+    method public int getNetworkCapabilities();
+    method public android.content.ComponentName getService();
+    method public boolean isPeriodic();
+    method public boolean isRequireCharging();
+    method public boolean isRequireDeviceIdle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static abstract interface Task.BackoffPolicy {
+    field public static final int EXPONENTIAL = 1; // 0x1
+    field public static final int LINEAR = 0; // 0x0
+  }
+
+  public static final class Task.Builder {
+    ctor public Task.Builder(int, android.content.ComponentName);
+    method public android.app.task.Task build();
+    method public android.app.task.Task.Builder setBackoffCriteria(long, int);
+    method public android.app.task.Task.Builder setExtras(android.os.Bundle);
+    method public android.app.task.Task.Builder setMinimumLatency(long);
+    method public android.app.task.Task.Builder setOverrideDeadline(long);
+    method public android.app.task.Task.Builder setPeriodic(long);
+    method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int);
+    method public android.app.task.Task.Builder setRequiresCharging(boolean);
+    method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean);
+  }
+
+  public static abstract interface Task.NetworkType {
+    field public static final int ANY = 1; // 0x1
+    field public static final int NONE = 0; // 0x0
+    field public static final int UNMETERED = 2; // 0x2
+  }
+
+  public abstract class TaskManager {
+    ctor public TaskManager();
+    method public abstract void cancel(int);
+    method public abstract void cancelAll();
+    method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
+    method public abstract int schedule(android.app.task.Task);
+    field public static final int RESULT_FAILURE = 0; // 0x0
+    field public static final int RESULT_SUCCESS = 1; // 0x1
+  }
+
   public class TaskParams implements android.os.Parcelable {
     method public int describeContents();
     method public android.os.Bundle getExtras();
@@ -5317,80 +5422,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 {
@@ -6995,6 +7026,7 @@
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
     field public static final java.lang.String STORAGE_SERVICE = "storage";
+    field public static final java.lang.String TASK_SERVICE = "task";
     field public static final java.lang.String TELEPHONY_SERVICE = "phone";
     field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
     field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -7933,55 +7965,6 @@
     method public abstract void onStatusChanged(int);
   }
 
-  public class Task implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getBackoffPolicy();
-    method public android.os.Bundle getExtras();
-    method public long getInitialBackoffMillis();
-    method public long getIntervalMillis();
-    method public long getMaxExecutionDelayMillis();
-    method public long getMinLatencyMillis();
-    method public int getNetworkCapabilities();
-    method public android.content.ComponentName getService();
-    method public int getTaskId();
-    method public boolean isPeriodic();
-    method public boolean isRequireCharging();
-    method public boolean isRequireDeviceIdle();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static abstract interface Task.BackoffPolicy {
-    field public static final int EXPONENTIAL = 1; // 0x1
-    field public static final int LINEAR = 0; // 0x0
-  }
-
-  public final class Task.Builder {
-    ctor public Task.Builder(int, android.content.ComponentName);
-    method public android.content.Task build();
-    method public android.content.Task.Builder setBackoffCriteria(long, int);
-    method public android.content.Task.Builder setExtras(android.os.Bundle);
-    method public android.content.Task.Builder setMinimumLatency(long);
-    method public android.content.Task.Builder setOverrideDeadline(long);
-    method public android.content.Task.Builder setPeriodic(long);
-    method public android.content.Task.Builder setRequiredNetworkCapabilities(int);
-    method public android.content.Task.Builder setRequiresCharging(boolean);
-    method public android.content.Task.Builder setRequiresDeviceIdle(boolean);
-  }
-
-  public static abstract interface Task.NetworkType {
-    field public static final int ANY = 0; // 0x0
-    field public static final int UNMETERED = 1; // 0x1
-  }
-
-  public abstract class TaskManager {
-    ctor public TaskManager();
-    method public abstract void cancel(int);
-    method public abstract void cancelAll();
-    method public abstract java.util.List<android.content.Task> getAllPendingTasks();
-    method public abstract int schedule(android.content.Task);
-  }
-
   public class UriMatcher {
     ctor public UriMatcher(int);
     method public void addURI(java.lang.String, java.lang.String, int);
@@ -11570,16 +11553,6 @@
     method public void startTransition(int);
   }
 
-  public class VectorDrawable extends android.graphics.drawable.Drawable {
-    ctor public VectorDrawable();
-    method public void draw(android.graphics.Canvas);
-    method public int getOpacity();
-    method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void setPadding(android.graphics.Rect);
-    method public void setPadding(int, int, int, int);
-  }
-
 }
 
 package android.graphics.drawable.shapes {
@@ -12150,9 +12123,11 @@
 
   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 onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
     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 onCaptureProgressed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureSequenceAborted(android.hardware.camera2.CameraDevice, int);
+    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, long);
     method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
   }
 
@@ -12166,70 +12141,78 @@
   }
 
   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();
-    method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureResultKeys();
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_STEP;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_AVAILABLE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_EFFECTS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_SCENE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_AVAILABLE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MAX_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key EDGE_AVAILABLE_EDGE_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_INFO_AVAILABLE;
-    field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key INFO_SUPPORTED_HARDWARE_LEVEL;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_AVAILABLE_THUMBNAIL_SIZES;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FACING;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_APERTURES;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_FILTER_DENSITIES;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_HYPERFOCAL_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_INFO_SHADING_MAP_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_AVAILABLE_CAPABILITIES;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_INPUT_STREAMS;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_OUTPUT_STREAMS;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PARTIAL_RESULT_COUNT;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_MAX_DEPTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROPPING_TYPE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_STREAM_CONFIGURATION_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BASE_GAIN_FACTOR;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BLACK_LEVEL_PATTERN;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_CALIBRATION_TRANSFORM1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_CALIBRATION_TRANSFORM2;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_COLOR_TRANSFORM1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_COLOR_TRANSFORM2;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FORWARD_MATRIX1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FORWARD_MATRIX2;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_ACTIVE_ARRAY_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_EXPOSURE_TIME_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_MAX_FRAME_DURATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_PHYSICAL_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_SENSITIVITY_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_WHITE_LEVEL;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_MAX_ANALOG_SENSITIVITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT1;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT2;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_MAX_FACE_COUNT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SYNC_MAX_LATENCY;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_AVAILABLE_TONE_MAP_MODES;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MAX_CURVE_POINTS;
+    method public T get(android.hardware.camera2.CameraCharacteristics.Key<T>);
+    method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
+    method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_COMPENSATION_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AE_COMPENSATION_STEP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AF_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_EFFECTS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_SCENE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_AWB_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS_AE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS_AF;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key CONTROL_MAX_REGIONS_AWB;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key EDGE_AVAILABLE_EDGE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key FLASH_INFO_AVAILABLE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key INFO_SUPPORTED_HARDWARE_LEVEL;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key JPEG_AVAILABLE_THUMBNAIL_SIZES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_FACING;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_APERTURES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_FILTER_DENSITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_HYPERFOCAL_DISTANCE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_AVAILABLE_CAPABILITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_INPUT_STREAMS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_PROC;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_RAW;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PARTIAL_RESULT_COUNT;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_PIPELINE_MAX_DEPTH;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_CROPPING_TYPE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SCALER_STREAM_CONFIGURATION_MAP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_BLACK_LEVEL_PATTERN;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_CALIBRATION_TRANSFORM1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_CALIBRATION_TRANSFORM2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_COLOR_TRANSFORM1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_COLOR_TRANSFORM2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_FORWARD_MATRIX1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_FORWARD_MATRIX2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_EXPOSURE_TIME_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_MAX_FRAME_DURATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PHYSICAL_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_PIXEL_ARRAY_SIZE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_SENSITIVITY_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_INFO_WHITE_LEVEL;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_MAX_ANALOG_SENSITIVITY;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_ORIENTATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_REFERENCE_ILLUMINANT1;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SENSOR_REFERENCE_ILLUMINANT2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key STATISTICS_INFO_MAX_FACE_COUNT;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key SYNC_MAX_LATENCY;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key TONEMAP_AVAILABLE_TONE_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key TONEMAP_MAX_CURVE_POINTS;
+  }
+
+  public static final class CameraCharacteristics.Key {
+    method public final boolean equals(java.lang.Object);
+    method public java.lang.String getName();
+    method public final int hashCode();
   }
 
   public abstract interface CameraDevice implements java.lang.AutoCloseable {
@@ -12254,9 +12237,11 @@
 
   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 onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
     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 onCaptureProgressed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureSequenceAborted(android.hardware.camera2.CameraDevice, int);
+    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, long);
     method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
   }
 
@@ -12292,8 +12277,7 @@
   }
 
   public abstract class CameraMetadata {
-    method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>);
-    method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getKeys();
+    method public java.util.List<TKey> getKeys();
     field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1
     field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0
@@ -12411,6 +12395,7 @@
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; // 0x4
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
@@ -12463,12 +12448,6 @@
     field public static final int TONEMAP_MODE_HIGH_QUALITY = 2; // 0x2
   }
 
-  public static class CameraMetadata.Key {
-    method public final boolean equals(java.lang.Object);
-    method public final java.lang.String getName();
-    method public final int hashCode();
-  }
-
   public class CaptureFailure {
     method public int getFrameNumber();
     method public int getReason();
@@ -12481,146 +12460,166 @@
 
   public final class CaptureRequest extends android.hardware.camera2.CameraMetadata implements android.os.Parcelable {
     method public int describeContents();
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+    method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method public java.lang.Object getTag();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key BLACK_LEVEL_LOCK;
+    field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_GAINS;
+    field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key COLOR_CORRECTION_TRANSFORM;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_ANTIBANDING_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_EXPOSURE_COMPENSATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_LOCK;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_PRECAPTURE_TRIGGER;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_REGIONS;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AE_TARGET_FPS_RANGE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_REGIONS;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AF_TRIGGER;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_LOCK;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_AWB_REGIONS;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_CAPTURE_INTENT;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_SCENE_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key CONTROL_VIDEO_STABILIZATION_MODE;
     field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_COORDINATES;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_PROCESSING_METHOD;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_TIMESTAMP;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_APERTURE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FILTER_DENSITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCAL_LENGTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_OPTICAL_STABILIZATION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROP_REGION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key EDGE_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key FLASH_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key HOT_PIXEL_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_LOCATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_ORIENTATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_QUALITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_QUALITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_THUMBNAIL_SIZE;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_APERTURE;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_FILTER_DENSITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_FOCAL_LENGTH;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CaptureRequest.Key LENS_OPTICAL_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key SCALER_CROP_REGION;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_EXPOSURE_TIME;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_FRAME_DURATION;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_SENSITIVITY;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_TEST_PATTERN_DATA;
+    field public static final android.hardware.camera2.CaptureRequest.Key SENSOR_TEST_PATTERN_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key SHADING_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_FACE_DETECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_HOT_PIXEL_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_MODE;
   }
 
   public static final class CaptureRequest.Builder {
     method public void addTarget(android.view.Surface);
     method public android.hardware.camera2.CaptureRequest build();
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+    method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
     method public void removeTarget(android.view.Surface);
-    method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
+    method public void set(android.hardware.camera2.CaptureRequest.Key<T>, T);
     method public void setTag(java.lang.Object);
   }
 
-  public final class CaptureResult extends android.hardware.camera2.CameraMetadata {
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+  public static final class CaptureRequest.Key {
+    method public final boolean equals(java.lang.Object);
+    method public java.lang.String getName();
+    method public final int hashCode();
+  }
+
+  public class CaptureResult extends android.hardware.camera2.CameraMetadata {
+    method public T get(android.hardware.camera2.CaptureResult.Key<T>);
     method public int getFrameNumber();
     method public android.hardware.camera2.CaptureRequest getRequest();
     method public int getSequenceId();
-    field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key FLASH_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key HOT_PIXEL_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_COORDINATES;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_PROCESSING_METHOD;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_GPS_TIMESTAMP;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_QUALITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key JPEG_THUMBNAIL_SIZE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_APERTURE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FILTER_DENSITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCAL_LENGTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_DISTANCE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_FOCUS_RANGE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_OPTICAL_STABILIZATION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key LENS_STATE;
-    field public static final android.hardware.camera2.CameraMetadata.Key NOISE_REDUCTION_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_FRAME_COUNT;
-    field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_DEPTH;
-    field public static final android.hardware.camera2.CameraMetadata.Key SCALER_CROP_REGION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_EXPOSURE_TIME;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_GREEN_SPLIT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_NEUTRAL_COLOR_POINT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TIMESTAMP;
-    field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACES;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE;
-    field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_SCENE_FLICKER;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_RED;
-    field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key BLACK_LEVEL_LOCK;
+    field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_GAINS;
+    field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key COLOR_CORRECTION_TRANSFORM;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_ANTIBANDING_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_EXPOSURE_COMPENSATION;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_LOCK;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_PRECAPTURE_TRIGGER;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AE_TARGET_FPS_RANGE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AF_TRIGGER;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_LOCK;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_AWB_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_CAPTURE_INTENT;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_SCENE_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key CONTROL_VIDEO_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key EDGE_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key FLASH_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key FLASH_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key HOT_PIXEL_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_LOCATION;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_ORIENTATION;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_QUALITY;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_QUALITY;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_THUMBNAIL_SIZE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_APERTURE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FILTER_DENSITY;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCAL_LENGTH;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_FOCUS_RANGE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_OPTICAL_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key LENS_STATE;
+    field public static final android.hardware.camera2.CaptureResult.Key NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key REQUEST_FRAME_COUNT;
+    field public static final android.hardware.camera2.CaptureResult.Key REQUEST_PIPELINE_DEPTH;
+    field public static final android.hardware.camera2.CaptureResult.Key SCALER_CROP_REGION;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_EXPOSURE_TIME;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_FRAME_DURATION;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_GREEN_SPLIT;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_NEUTRAL_COLOR_POINT;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_SENSITIVITY;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_DATA;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TEST_PATTERN_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key SENSOR_TIMESTAMP;
+    field public static final android.hardware.camera2.CaptureResult.Key SHADING_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACES;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_FACE_DETECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_HOT_PIXEL_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_CORRECTION_MAP;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key STATISTICS_SCENE_FLICKER;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_MODE;
+  }
+
+  public static final class CaptureResult.Key {
+    method public final boolean equals(java.lang.Object);
+    method public java.lang.String getName();
+    method public final int hashCode();
+  }
+
+  public final class DngCreator implements java.lang.AutoCloseable {
+    ctor public DngCreator(android.hardware.camera2.CameraCharacteristics, android.hardware.camera2.CaptureResult);
+    method public void close();
+    method public android.hardware.camera2.DngCreator setDescription(java.lang.String);
+    method public android.hardware.camera2.DngCreator setLocation(android.location.Location);
+    method public android.hardware.camera2.DngCreator setOrientation(int);
+    method public android.hardware.camera2.DngCreator setThumbnail(android.graphics.Bitmap);
+    method public android.hardware.camera2.DngCreator setThumbnail(android.media.Image);
+    method public void writeByteBuffer(java.io.OutputStream, android.util.Size, java.nio.ByteBuffer, long) throws java.io.IOException;
+    method public void writeImage(java.io.OutputStream, android.media.Image) throws java.io.IOException;
+    method public void writeInputStream(java.io.OutputStream, android.util.Size, java.io.InputStream, long) throws java.io.IOException;
+  }
+
+  public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
+    method public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults();
   }
 
 }
@@ -13719,45 +13718,6 @@
     method public void stop();
   }
 
-  public final class AudioAttributes {
-    method public int getContentType();
-    method public int getFlags();
-    method public java.util.Set<java.lang.String> getTags();
-    method public int getUsage();
-    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
-    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
-    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
-    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
-    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
-    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
-    field public static final int USAGE_ALARM = 4; // 0x4
-    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
-    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
-    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
-    field public static final int USAGE_GAME = 14; // 0xe
-    field public static final int USAGE_MEDIA = 1; // 0x1
-    field public static final int USAGE_NOTIFICATION = 5; // 0x5
-    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
-    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
-    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
-    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
-    field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6
-    field public static final int USAGE_UNKNOWN = 0; // 0x0
-    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
-    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
-  }
-
-  public static class AudioAttributes.Builder {
-    ctor public AudioAttributes.Builder();
-    ctor public AudioAttributes.Builder(android.media.AudioAttributes);
-    method public android.media.AudioAttributes.Builder addTag(java.lang.String);
-    method public android.media.AudioAttributes build();
-    method public android.media.AudioAttributes.Builder setContentType(int);
-    method public android.media.AudioAttributes.Builder setFlags(int);
-    method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
-    method public android.media.AudioAttributes.Builder setUsage(int);
-  }
-
   public class AudioFormat {
     ctor public AudioFormat();
     field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -14091,19 +14051,6 @@
     ctor public DeniedByServerException(java.lang.String);
   }
 
-  public final class DngCreator implements java.lang.AutoCloseable {
-    ctor public DngCreator(android.hardware.camera2.CameraCharacteristics, android.hardware.camera2.CaptureResult);
-    method public void close();
-    method public android.media.DngCreator setDescription(java.lang.String);
-    method public android.media.DngCreator setLocation(android.location.Location);
-    method public android.media.DngCreator setOrientation(int);
-    method public android.media.DngCreator setThumbnail(android.graphics.Bitmap);
-    method public android.media.DngCreator setThumbnail(android.media.Image);
-    method public void writeByteBuffer(java.io.OutputStream, android.util.Size, java.nio.ByteBuffer, long) throws java.io.IOException;
-    method public void writeImage(java.io.OutputStream, android.media.Image) throws java.io.IOException;
-    method public void writeInputStream(java.io.OutputStream, android.util.Size, java.io.InputStream, long) throws java.io.IOException;
-  }
-
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -21684,9 +21631,7 @@
     method public abstract void cancel();
     method public abstract boolean hasVibrator();
     method public void vibrate(long);
-    method public void vibrate(long, int);
     method public void vibrate(long[], int);
-    method public void vibrate(long[], int, int);
   }
 
   public class WorkSource implements android.os.Parcelable {
@@ -21724,10 +21669,6 @@
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
-    field public static final int CRYPT_TYPE_DEFAULT = 1; // 0x1
-    field public static final int CRYPT_TYPE_PASSWORD = 0; // 0x0
-    field public static final int CRYPT_TYPE_PATTERN = 2; // 0x2
-    field public static final int CRYPT_TYPE_PIN = 3; // 0x3
   }
 
 }
@@ -25061,7 +25002,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";
@@ -25099,10 +25042,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";
@@ -26399,7 +26344,7 @@
     field public static final int TOUCHABLE_INSETS_CONTENT = 1; // 0x1
     field public static final int TOUCHABLE_INSETS_FRAME = 0; // 0x0
     field public static final int TOUCHABLE_INSETS_REGION = 3; // 0x3
-    field public int contentTopInsets;
+    field public final android.graphics.Rect contentInsets;
     field public int touchableInsets;
     field public final android.graphics.Region touchableRegion;
   }
@@ -27478,306 +27423,6 @@
 
 }
 
-package android.telecomm {
-
-  public final class CallAudioState implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static int ROUTE_ALL;
-    field public static int ROUTE_BLUETOOTH;
-    field public static int ROUTE_EARPIECE;
-    field public static int ROUTE_SPEAKER;
-    field public static int ROUTE_WIRED_HEADSET;
-    field public static int ROUTE_WIRED_OR_EARPIECE;
-    field public final boolean isMuted;
-    field public final int route;
-    field public final int supportedRouteMask;
-  }
-
-  public class CallCapabilities {
-    ctor public CallCapabilities();
-    field public static final int ADD_CALL = 16; // 0x10
-    field public static final int ALL = 511; // 0x1ff
-    field public static final int CONNECTION_HANDOFF = 256; // 0x100
-    field public static final int GENERIC_CONFERENCE = 128; // 0x80
-    field public static final int HOLD = 1; // 0x1
-    field public static final int MERGE_CALLS = 4; // 0x4
-    field public static final int MUTE = 64; // 0x40
-    field public static final int RESPOND_VIA_TEXT = 32; // 0x20
-    field public static final int SUPPORT_HOLD = 2; // 0x2
-    field public static final int SWAP_CALLS = 8; // 0x8
-  }
-
-  public final class CallInfo implements android.os.Parcelable {
-    ctor public CallInfo(java.lang.String, android.telecomm.CallState, android.net.Uri);
-    method public int describeContents();
-    method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
-    method public android.os.Bundle getExtras();
-    method public android.telecomm.GatewayInfo getGatewayInfo();
-    method public android.net.Uri getHandle();
-    method public java.lang.String getId();
-    method public android.net.Uri getOriginalHandle();
-    method public android.telecomm.CallState getState();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public final class CallNumberPresentation extends java.lang.Enum {
-    method public static android.telecomm.CallNumberPresentation valueOf(java.lang.String);
-    method public static final android.telecomm.CallNumberPresentation[] values();
-    enum_constant public static final android.telecomm.CallNumberPresentation ALLOWED;
-    enum_constant public static final android.telecomm.CallNumberPresentation PAYPHONE;
-    enum_constant public static final android.telecomm.CallNumberPresentation RESTRICTED;
-    enum_constant public static final android.telecomm.CallNumberPresentation UNKNOWN;
-  }
-
-  public abstract class CallService extends android.app.Service {
-    ctor public CallService();
-    method public abstract void abort(java.lang.String);
-    method public abstract void answer(java.lang.String);
-    method public abstract void call(android.telecomm.CallInfo);
-    method public abstract void disconnect(java.lang.String);
-    method protected final android.telecomm.CallServiceAdapter getAdapter();
-    method public final android.os.IBinder getBinder();
-    method public abstract void hold(java.lang.String);
-    method public abstract void isCompatibleWith(android.telecomm.CallInfo);
-    method protected void onAdapterAttached(android.telecomm.CallServiceAdapter);
-    method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void playDtmfTone(java.lang.String, char);
-    method public abstract void reject(java.lang.String);
-    method public abstract void setIncomingCallId(java.lang.String, android.os.Bundle);
-    method public abstract void stopDtmfTone(java.lang.String);
-    method public abstract void unhold(java.lang.String);
-  }
-
-  public final class CallServiceAdapter {
-    method public void handleFailedOutgoingCall(java.lang.String, java.lang.String);
-    method public void handleSuccessfulOutgoingCall(java.lang.String);
-    method public void notifyIncomingCall(android.telecomm.CallInfo);
-    method public void setActive(java.lang.String);
-    method public void setDialing(java.lang.String);
-    method public void setDisconnected(java.lang.String, int, java.lang.String);
-    method public void setIsCompatibleWith(java.lang.String, boolean);
-    method public void setOnHold(java.lang.String);
-    method public void setRinging(java.lang.String);
-  }
-
-  public final class CallServiceDescriptor implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.lang.String getCallServiceId();
-    method public int getNetworkType();
-    method public android.content.ComponentName getServiceComponent();
-    method public static android.telecomm.CallServiceDescriptor.Builder newBuilder(android.content.Context);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int FLAG_MOBILE = 4; // 0x4
-    field public static final int FLAG_PSTN = 2; // 0x2
-    field public static final int FLAG_WIFI = 1; // 0x1
-  }
-
-  public static class CallServiceDescriptor.Builder {
-    method public android.telecomm.CallServiceDescriptor build();
-    method public android.telecomm.CallServiceDescriptor.Builder setCallService(java.lang.Class<? extends android.telecomm.CallService>);
-    method public android.telecomm.CallServiceDescriptor.Builder setNetworkType(int);
-  }
-
-  public final class CallServiceLookupResponse {
-    method public void setCallServiceDescriptors(java.util.List<android.telecomm.CallServiceDescriptor>);
-  }
-
-  public abstract class CallServiceProvider extends android.app.Service {
-    ctor protected CallServiceProvider();
-    method public abstract void lookupCallServices(android.telecomm.CallServiceLookupResponse);
-    method public android.os.IBinder onBind(android.content.Intent);
-  }
-
-  public abstract class CallServiceSelector extends android.app.Service {
-    ctor protected CallServiceSelector();
-    method protected final void cancelOutgoingCall(android.telecomm.CallInfo);
-    method protected final android.telecomm.CallServiceSelectorAdapter getAdapter();
-    method protected final java.util.Collection<android.telecomm.CallInfo> getCalls();
-    method protected void onAdapterAttached(android.telecomm.CallServiceSelectorAdapter);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method protected abstract void select(android.telecomm.CallInfo, java.util.List<android.telecomm.CallServiceDescriptor>);
-  }
-
-  public final class CallServiceSelectorAdapter {
-    method public void cancelOutgoingCall(java.lang.String);
-    method public void setHandoffInfo(java.lang.String, android.net.Uri, android.os.Bundle);
-    method public void setSelectedCallServices(java.lang.String, java.util.List<android.telecomm.CallServiceDescriptor>);
-  }
-
-  public final class CallState extends java.lang.Enum {
-    method public static android.telecomm.CallState valueOf(java.lang.String);
-    method public static final android.telecomm.CallState[] values();
-    enum_constant public static final android.telecomm.CallState ACTIVE;
-    enum_constant public static final android.telecomm.CallState DIALING;
-    enum_constant public static final android.telecomm.CallState DISCONNECTED;
-    enum_constant public static final android.telecomm.CallState NEW;
-    enum_constant public static final android.telecomm.CallState ON_HOLD;
-    enum_constant public static final android.telecomm.CallState POST_DIAL;
-    enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT;
-    enum_constant public static final android.telecomm.CallState RINGING;
-  }
-
-  public abstract class Connection {
-    ctor protected Connection();
-    method public final android.telecomm.CallAudioState getCallAudioState();
-    method public final android.net.Uri getHandle();
-    method protected void onAbort();
-    method protected void onAnswer();
-    method protected void onDisconnect();
-    method protected void onHold();
-    method protected void onPlayDtmfTone(char);
-    method protected void onReject();
-    method protected void onSetAudioState(android.telecomm.CallAudioState);
-    method protected void onSetSignal(android.os.Bundle);
-    method protected void onStopDtmfTone();
-    method protected void onUnhold();
-    method protected void setActive();
-    method public void setAudioState(android.telecomm.CallAudioState);
-    method protected void setDialing();
-    method protected void setDisconnected(int, java.lang.String);
-    method protected void setHandle(android.net.Uri);
-    method protected void setOnHold();
-    method protected void setRinging();
-    method public static java.lang.String stateToString(int);
-  }
-
-  public static abstract interface Connection.Listener {
-    method public abstract void onAudioStateChanged(android.telecomm.Connection, android.telecomm.CallAudioState);
-    method public abstract void onDestroyed(android.telecomm.Connection);
-    method public abstract void onDisconnected(android.telecomm.Connection, int, java.lang.String);
-    method public abstract void onHandleChanged(android.telecomm.Connection, android.net.Uri);
-    method public abstract void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
-    method public abstract void onStateChanged(android.telecomm.Connection, int);
-  }
-
-  public static class Connection.ListenerBase implements android.telecomm.Connection.Listener {
-    ctor public Connection.ListenerBase();
-    method public void onAudioStateChanged(android.telecomm.Connection, android.telecomm.CallAudioState);
-    method public void onDestroyed(android.telecomm.Connection);
-    method public void onDisconnected(android.telecomm.Connection, int, java.lang.String);
-    method public void onHandleChanged(android.telecomm.Connection, android.net.Uri);
-    method public void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
-    method public void onStateChanged(android.telecomm.Connection, int);
-  }
-
-  public final class Connection.State {
-    field public static final int ACTIVE = 3; // 0x3
-    field public static final int DIALING = 2; // 0x2
-    field public static final int DISCONNECTED = 5; // 0x5
-    field public static final int HOLDING = 4; // 0x4
-    field public static final int NEW = 0; // 0x0
-    field public static final int RINGING = 1; // 0x1
-  }
-
-  public final class ConnectionRequest {
-    ctor public ConnectionRequest(android.net.Uri, android.os.Bundle);
-    method public android.os.Bundle getExtras();
-    method public android.net.Uri getHandle();
-  }
-
-  public abstract class ConnectionService extends android.telecomm.CallService {
-    ctor public ConnectionService();
-    method public final void abort(java.lang.String);
-    method public final void answer(java.lang.String);
-    method public final void call(android.telecomm.CallInfo);
-    method public final void disconnect(java.lang.String);
-    method public final void hold(java.lang.String);
-    method public final void isCompatibleWith(android.telecomm.CallInfo);
-    method public final void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
-    method public void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
-    method public void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
-    method public void onFindSubscriptions(android.net.Uri, android.telecomm.Response<android.net.Uri, android.telecomm.Subscription>);
-    method public final void playDtmfTone(java.lang.String, char);
-    method public final void reject(java.lang.String);
-    method public final void setIncomingCallId(java.lang.String, android.os.Bundle);
-    method public final void stopDtmfTone(java.lang.String);
-    method public final void unhold(java.lang.String);
-  }
-
-  public class GatewayInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.net.Uri getGatewayHandle();
-    method public java.lang.String getGatewayProviderPackageName();
-    method public android.net.Uri getOriginalHandle();
-    method public boolean isEmpty();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public final class InCallAdapter {
-    method public void answerCall(java.lang.String);
-    method public void disconnectCall(java.lang.String);
-    method public void handoffCall(java.lang.String);
-    method public void holdCall(java.lang.String);
-    method public void mute(boolean);
-    method public void playDtmfTone(java.lang.String, char);
-    method public void postDialContinue(java.lang.String);
-    method public void rejectCall(java.lang.String);
-    method public void setAudioRoute(int);
-    method public void stopDtmfTone(java.lang.String);
-    method public void unholdCall(java.lang.String);
-  }
-
-  public final class InCallCall implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getCapabilities();
-    method public long getConnectTimeMillis();
-    method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
-    method public int getDisconnectCause();
-    method public android.telecomm.GatewayInfo getGatewayInfo();
-    method public android.net.Uri getHandle();
-    method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor();
-    method public java.lang.String getId();
-    method public android.telecomm.CallState getState();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public abstract class InCallService extends android.app.Service {
-    ctor protected InCallService();
-    method protected abstract void addCall(android.telecomm.InCallCall);
-    method protected final android.telecomm.InCallAdapter getAdapter();
-    method protected void onAdapterAttached(android.telecomm.InCallAdapter);
-    method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method protected abstract void setPostDial(java.lang.String, java.lang.String);
-    method protected abstract void setPostDialWait(java.lang.String, java.lang.String);
-    method protected abstract void updateCall(android.telecomm.InCallCall);
-  }
-
-  public abstract interface Response {
-    method public abstract void onError(IN, java.lang.String);
-    method public abstract void onResult(IN, OUT...);
-  }
-
-  public class Subscription implements android.os.Parcelable {
-    ctor public Subscription();
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public final class TelecommConstants {
-    ctor public TelecommConstants();
-    field public static final java.lang.String ACTION_CALL_SERVICE;
-    field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
-    field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
-    field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
-    field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
-    field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
-    field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
-    field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
-    field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
-    field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
-  }
-
-}
-
 package android.telephony {
 
   public final class CellIdentityCdma implements android.os.Parcelable {
@@ -35900,10 +35545,14 @@
   public class ActionMenuView extends android.widget.LinearLayout {
     ctor public ActionMenuView(android.content.Context);
     ctor public ActionMenuView(android.content.Context, android.util.AttributeSet);
+    method public void dismissPopupMenus();
     method public android.view.Menu getMenu();
+    method public boolean hideOverflowMenu();
+    method public boolean isOverflowMenuShowing();
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public void onDetachedFromWindow();
     method public void setOnMenuItemClickListener(android.widget.ActionMenuView.OnMenuItemClickListener);
+    method public boolean showOverflowMenu();
   }
 
   public static class ActionMenuView.LayoutParams extends android.widget.LinearLayout.LayoutParams {
@@ -38081,6 +37730,8 @@
     ctor public Toolbar(android.content.Context, android.util.AttributeSet);
     ctor public Toolbar(android.content.Context, android.util.AttributeSet, int);
     ctor public Toolbar(android.content.Context, android.util.AttributeSet, int, int);
+    method public void collapseActionView();
+    method public void dismissPopupMenus();
     method public int getContentInsetEnd();
     method public int getContentInsetLeft();
     method public int getContentInsetRight();
@@ -38091,7 +37742,10 @@
     method public android.graphics.drawable.Drawable getNavigationIcon();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
+    method public boolean hasExpandedActionView();
+    method public boolean hideOverflowMenu();
     method public void inflateMenu(int);
+    method public boolean isOverflowMenuShowing();
     method protected void onLayout(boolean, int, int, int, int);
     method public void setContentInsetsAbsolute(int, int);
     method public void setContentInsetsRelative(int, int);
@@ -38099,6 +37753,8 @@
     method public void setLogo(android.graphics.drawable.Drawable);
     method public void setLogoDescription(int);
     method public void setLogoDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(java.lang.CharSequence);
+    method public void setNavigationContentDescription(int);
     method public void setNavigationDescription(int);
     method public void setNavigationDescription(java.lang.CharSequence);
     method public void setNavigationIcon(int);
@@ -38109,17 +37765,18 @@
     method public void setSubtitle(java.lang.CharSequence);
     method public void setTitle(int);
     method public void setTitle(java.lang.CharSequence);
+    method public boolean showOverflowMenu();
   }
 
-  public static class Toolbar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+  public static class Toolbar.LayoutParams extends android.app.ActionBar.LayoutParams {
     ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet);
     ctor public Toolbar.LayoutParams(int, int);
     ctor public Toolbar.LayoutParams(int, int, int);
     ctor public Toolbar.LayoutParams(int);
     ctor public Toolbar.LayoutParams(android.widget.Toolbar.LayoutParams);
+    ctor public Toolbar.LayoutParams(android.app.ActionBar.LayoutParams);
     ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
     ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams);
-    field public int gravity;
   }
 
   public static abstract interface Toolbar.OnMenuItemClickListener {
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 3c3df01..f05f4c7 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -1291,6 +1291,7 @@
 
         public LayoutParams(int width, int height) {
             super(width, height);
+            this.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
         }
 
         public LayoutParams(int width, int height, int gravity) {
@@ -1305,6 +1306,7 @@
 
         public LayoutParams(LayoutParams source) {
             super(source);
+            this.gravity = source.gravity;
         }
 
         public LayoutParams(ViewGroup.LayoutParams source) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 07de85c..b5281ff 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4194,7 +4194,11 @@
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode) {
-        startActivityFromFragment(fragment, intent, requestCode, null);
+        Bundle options = null;
+        if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+            options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
+        }
+        startActivityFromFragment(fragment, intent, requestCode, options);
     }
 
     /**
@@ -4219,6 +4223,9 @@
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode, @Nullable Bundle options) {
+        if (options != null) {
+            mActivityTransitionState.startExitOutTransition(this, options);
+        }
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, fragment,
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..5e4ddd0 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,14 +15,23 @@
  */
 package android.app;
 
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.ResultReceiver;
 import android.transition.Transition;
 import android.transition.TransitionSet;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.widget.ImageView;
 
@@ -181,6 +190,11 @@
      */
     public static final int MSG_CANCEL = 106;
 
+    /**
+     * When returning, this is the destination location for the shared element.
+     */
+    public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
+
     final private Window mWindow;
     final protected ArrayList<String> mAllSharedElementNames;
     final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -189,15 +203,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);
@@ -332,6 +348,210 @@
 
     protected abstract Transition getViewsTransition();
 
+    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] parentLoc) {
+        Bundle sharedElementBundle = transitionArgs.getBundle(name);
+        if (sharedElementBundle == null) {
+            return;
+        }
+
+        if (view instanceof ImageView) {
+            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
+            if (scaleTypeInt >= 0) {
+                ImageView imageView = (ImageView) view;
+                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
+                imageView.setScaleType(scaleType);
+                if (scaleType == ImageView.ScaleType.MATRIX) {
+                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
+                    Matrix matrix = new Matrix();
+                    matrix.setValues(matrixValues);
+                    imageView.setImageMatrix(matrix);
+                }
+            }
+        }
+
+        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
+        view.setTranslationZ(z);
+
+        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
+        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
+        int width = sharedElementBundle.getInt(KEY_WIDTH);
+        int height = sharedElementBundle.getInt(KEY_HEIGHT);
+
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+        view.measure(widthSpec, heightSpec);
+
+        int left = x - parentLoc[0];
+        int top = y - parentLoc[1];
+        int right = left + width;
+        int bottom = top + height;
+        view.layout(left, top, right, bottom);
+    }
+
+    protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+            Bundle sharedElementState, final ArrayList<View> snapshots) {
+        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
+                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+        if (sharedElementState != null) {
+            int[] tempLoc = new int[2];
+            for (int i = 0; i < mSharedElementNames.size(); i++) {
+                View sharedElement = mSharedElements.get(i);
+                String name = mSharedElementNames.get(i);
+                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+                        name, sharedElementState);
+                if (originalState != null) {
+                    originalImageState.put((ImageView) sharedElement, originalState);
+                }
+                View parent = (View) sharedElement.getParent();
+                parent.getLocationOnScreen(tempLoc);
+                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+            }
+        }
+        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
+
+        getDecor().getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
+                                snapshots);
+                        return true;
+                    }
+                }
+        );
+        return originalImageState;
+    }
+
+    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+            Bundle transitionArgs) {
+        if (!(view instanceof ImageView)) {
+            return null;
+        }
+        Bundle bundle = transitionArgs.getBundle(name);
+        if (bundle == null) {
+            return null;
+        }
+        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
+        if (scaleTypeInt < 0) {
+            return null;
+        }
+
+        ImageView imageView = (ImageView) view;
+        ImageView.ScaleType originalScaleType = imageView.getScaleType();
+
+        Matrix originalMatrix = null;
+        if (originalScaleType == ImageView.ScaleType.MATRIX) {
+            originalMatrix = new Matrix(imageView.getImageMatrix());
+        }
+
+        return Pair.create(originalScaleType, originalMatrix);
+    }
+
+    protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
+        int numSharedElements = names.size();
+        if (numSharedElements == 0) {
+            return null;
+        }
+        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
+        Context context = getWindow().getContext();
+        int[] parentLoc = new int[2];
+        getDecor().getLocationOnScreen(parentLoc);
+        for (String name: names) {
+            Bundle sharedElementBundle = state.getBundle(name);
+            if (sharedElementBundle != null) {
+                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+                View snapshot = new View(context);
+                Resources resources = getWindow().getContext().getResources();
+                if (bitmap != null) {
+                    snapshot.setBackground(new BitmapDrawable(resources, bitmap));
+                }
+                snapshot.setViewName(name);
+                setSharedElementState(snapshot, name, state, parentLoc);
+                snapshots.add(snapshot);
+            }
+        }
+        return snapshots;
+    }
+
+    protected static void setOriginalImageViewState(
+            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+        for (int i = 0; i < originalState.size(); i++) {
+            ImageView imageView = originalState.keyAt(i);
+            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
+            imageView.setScaleType(state.first);
+            imageView.setImageMatrix(state.second);
+        }
+    }
+
+    protected Bundle captureSharedElementState() {
+        Bundle bundle = new Bundle();
+        int[] tempLoc = new int[2];
+        for (int i = 0; i < mSharedElementNames.size(); i++) {
+            View sharedElement = mSharedElements.get(i);
+            String name = mSharedElementNames.get(i);
+            captureSharedElementState(sharedElement, name, bundle, tempLoc);
+        }
+        return bundle;
+    }
+
+    /**
+     * Captures placement information for Views with a shared element name for
+     * Activity Transitions.
+     *
+     * @param view           The View to capture the placement information for.
+     * @param name           The shared element name in the target Activity to apply the placement
+     *                       information for.
+     * @param transitionArgs Bundle to store shared element placement information.
+     * @param tempLoc        A temporary int[2] for capturing the current location of views.
+     */
+    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] tempLoc) {
+        Bundle sharedElementBundle = new Bundle();
+        view.getLocationOnScreen(tempLoc);
+        float scaleX = view.getScaleX();
+        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
+        int width = Math.round(view.getWidth() * scaleX);
+        sharedElementBundle.putInt(KEY_WIDTH, width);
+
+        float scaleY = view.getScaleY();
+        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
+        int height = Math.round(view.getHeight() * scaleY);
+        sharedElementBundle.putInt(KEY_HEIGHT, height);
+
+        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
+
+        if (width > 0 && height > 0) {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            view.draw(canvas);
+            sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+        }
+
+        if (view instanceof ImageView) {
+            ImageView imageView = (ImageView) view;
+            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
+            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
+            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
+                float[] matrix = new float[9];
+                imageView.getImageMatrix().getValues(matrix);
+                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
+            }
+        }
+
+        transitionArgs.putBundle(name, sharedElementBundle);
+    }
+
+    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
+        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
+            if (scaleType == SCALE_TYPE_VALUES[i]) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
         private Rect mEpicenter;
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1634d11..ff8688d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -133,10 +133,12 @@
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.task.ITaskManager;
 import android.app.trust.TrustManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.appwidget.IAppWidgetService.Stub;
 import com.android.internal.os.IDropBoxManagerService;
 
 import java.io.File;
@@ -693,6 +695,12 @@
                 public Object createService(ContextImpl ctx) {
                 return new UsageStatsManager(ctx.getOuterContext());
         }});
+
+        registerService(TASK_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(TASK_SERVICE);
+                return new TaskManagerImpl(ITaskManager.Stub.asInterface(b));
+            }});
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 4cca355..a8617b8 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,11 +18,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Matrix;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,7 +34,6 @@
 import android.widget.ImageView;
 
 import java.util.ArrayList;
-import java.util.Collection;
 
 /**
  * This ActivityTransitionCoordinator is created by the Activity to manage
@@ -55,16 +50,15 @@
     private boolean mHasStopped;
     private Handler mHandler;
     private boolean mIsCanceled;
-    private boolean mIsReturning;
     private ObjectAnimator mBackgroundAnimator;
+    private boolean mIsExitTransitionComplete;
 
     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();
@@ -78,6 +72,8 @@
                 }
             };
             mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+            Bundle state = captureSharedElementState();
+            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
         }
     }
 
@@ -100,9 +96,8 @@
                 break;
             case MSG_EXIT_TRANSITION_COMPLETE:
                 if (!mIsCanceled) {
-                    if (!mSharedElementTransitionStarted) {
-                        send(resultCode, resultData);
-                    } else {
+                    mIsExitTransitionComplete = true;
+                    if (mSharedElementTransitionStarted) {
                         onRemoteExitTransitionComplete();
                     }
                 }
@@ -185,6 +180,7 @@
         setViewVisibility(mSharedElements, View.VISIBLE);
         ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
+        requestLayoutForSharedElements();
 
         boolean startEnterTransition = allowOverlappingTransitions();
         boolean startSharedElementTransition = true;
@@ -202,6 +198,13 @@
         mResultReceiver = null; // all done sending messages.
     }
 
+    private void requestLayoutForSharedElements() {
+        int numSharedElements = mSharedElements.size();
+        for (int i = 0; i < numSharedElements; i++) {
+            mSharedElements.get(i).requestLayout();
+        }
+    }
+
     private Transition beginTransition(boolean startEnterTransition,
             boolean startSharedElementTransition) {
         Transition sharedElementTransition = null;
@@ -215,6 +218,19 @@
         }
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
+        if (startSharedElementTransition) {
+            if (transition == null) {
+                sharedElementTransitionStarted();
+            } else {
+                transition.addListener(new Transition.TransitionListenerAdapter() {
+                    @Override
+                    public void onTransitionStart(Transition transition) {
+                        transition.removeListener(this);
+                        sharedElementTransitionStarted();
+                    }
+                });
+            }
+        }
         if (transition != null) {
             TransitionManager.beginDelayedTransition(getDecor(), transition);
             if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
@@ -226,6 +242,13 @@
         return transition;
     }
 
+    private void sharedElementTransitionStarted() {
+        mSharedElementTransitionStarted = true;
+        if (mIsExitTransitionComplete) {
+            send(MSG_EXIT_TRANSITION_COMPLETE, null);
+        }
+    }
+
     private void startEnterTransition(Transition transition) {
         setViewVisibility(mTransitioningViews, View.VISIBLE);
         if (!mIsReturning) {
@@ -312,142 +335,4 @@
             startEnterTransition(transition);
         }
     }
-
-    private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
-        int numSharedElements = names.size();
-        if (numSharedElements == 0) {
-            return null;
-        }
-        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
-        Context context = getWindow().getContext();
-        int[] parentLoc = new int[2];
-        getDecor().getLocationOnScreen(parentLoc);
-        for (String name: names) {
-            Bundle sharedElementBundle = state.getBundle(name);
-            if (sharedElementBundle != null) {
-                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
-                View snapshot = new View(context);
-                Resources resources = getWindow().getContext().getResources();
-                snapshot.setBackground(new BitmapDrawable(resources, bitmap));
-                snapshot.setViewName(name);
-                setSharedElementState(snapshot, name, state, parentLoc);
-                snapshots.add(snapshot);
-            }
-        }
-        return snapshots;
-    }
-
-    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] parentLoc) {
-        Bundle sharedElementBundle = transitionArgs.getBundle(name);
-        if (sharedElementBundle == null) {
-            return;
-        }
-
-        if (view instanceof ImageView) {
-            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
-            if (scaleTypeInt >= 0) {
-                ImageView imageView = (ImageView) view;
-                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
-                imageView.setScaleType(scaleType);
-                if (scaleType == ImageView.ScaleType.MATRIX) {
-                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
-                    Matrix matrix = new Matrix();
-                    matrix.setValues(matrixValues);
-                    imageView.setImageMatrix(matrix);
-                }
-            }
-        }
-
-        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
-        view.setTranslationZ(z);
-
-        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
-        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
-        int width = sharedElementBundle.getInt(KEY_WIDTH);
-        int height = sharedElementBundle.getInt(KEY_HEIGHT);
-
-        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
-        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
-        view.measure(widthSpec, heightSpec);
-
-        int left = x - parentLoc[0];
-        int top = y - parentLoc[1];
-        int right = left + width;
-        int bottom = top + height;
-        view.layout(left, top, right, bottom);
-    }
-
-    private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
-            Bundle sharedElementState, final ArrayList<View> snapshots) {
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
-                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
-        if (sharedElementState != null) {
-            int[] tempLoc = new int[2];
-            for (int i = 0; i < mSharedElementNames.size(); i++) {
-                View sharedElement = mSharedElements.get(i);
-                String name = mSharedElementNames.get(i);
-                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
-                        name, sharedElementState);
-                if (originalState != null) {
-                    originalImageState.put((ImageView) sharedElement, originalState);
-                }
-                View parent = (View) sharedElement.getParent();
-                parent.getLocationOnScreen(tempLoc);
-                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
-                sharedElement.requestLayout();
-            }
-        }
-        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
-
-        getDecor().getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
-                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
-                                snapshots);
-                        mSharedElementTransitionStarted = true;
-                        return true;
-                    }
-                }
-        );
-        return originalImageState;
-    }
-
-    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
-            Bundle transitionArgs) {
-        if (!(view instanceof ImageView)) {
-            return null;
-        }
-        Bundle bundle = transitionArgs.getBundle(name);
-        if (bundle == null) {
-            return null;
-        }
-        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
-        if (scaleTypeInt < 0) {
-            return null;
-        }
-
-        ImageView imageView = (ImageView) view;
-        ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
-        Matrix originalMatrix = null;
-        if (originalScaleType == ImageView.ScaleType.MATRIX) {
-            originalMatrix = new Matrix(imageView.getImageMatrix());
-        }
-
-        return Pair.create(originalScaleType, originalMatrix);
-    }
-
-    private static void setOriginalImageViewState(
-            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
-        for (int i = 0; i < originalState.size(); i++) {
-            ImageView imageView = originalState.keyAt(i);
-            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
-            imageView.setScaleType(state.first);
-            imageView.setImageMatrix(state.second);
-        }
-    }
-
 }
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f36c36a..a71d649 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -19,8 +19,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -29,7 +27,7 @@
 import android.transition.Transition;
 import android.transition.TransitionManager;
 import android.view.View;
-import android.widget.ImageView;
+import android.view.ViewTreeObserver;
 
 import java.util.ArrayList;
 
@@ -58,16 +56,18 @@
 
     private Handler mHandler;
 
-    private boolean mIsReturning;
-
     private ObjectAnimator mBackgroundAnimator;
 
     private boolean mIsHidden;
 
+    private boolean mExitTransitionStarted;
+
+    private Bundle mExitSharedElementBundle;
+
     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;
     }
@@ -104,15 +104,32 @@
                 setViewVisibility(mSharedElements, View.VISIBLE);
                 mIsHidden = true;
                 break;
+            case MSG_SHARED_ELEMENT_DESTINATION:
+                mExitSharedElementBundle = resultData;
+                if (mExitTransitionStarted) {
+                    startSharedElementExit();
+                }
+                break;
+        }
+    }
+
+    private void startSharedElementExit() {
+        if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
+            Transition transition = getSharedElementExitTransition();
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
+                    mSharedElementNames);
+            setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
         }
     }
 
     private void hideSharedElements() {
         setViewVisibility(mSharedElements, View.INVISIBLE);
+        finishIfNecessary();
     }
 
     public void startExit() {
-        beginTransition();
+        beginTransitions();
         setViewVisibility(mTransitioningViews, View.INVISIBLE);
     }
 
@@ -142,7 +159,30 @@
                 }
             }
         }, options);
-        startExit();
+        Transition sharedElementTransition = mSharedElements.isEmpty()
+                ? null : getSharedElementTransition();
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        }
+        Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
+        if (transition == null) {
+            mExitTransitionStarted = true;
+        } else {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            setViewVisibility(mTransitioningViews, View.INVISIBLE);
+            getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                    mExitTransitionStarted = true;
+                    if (mExitSharedElementBundle != null) {
+                        startSharedElementExit();
+                    }
+                    notifyComplete();
+                    return true;
+                }
+            });
+        }
     }
 
     private void fadeOutBackground() {
@@ -164,24 +204,13 @@
         }
     }
 
-    private void beginTransition() {
-        Transition sharedElementTransition = configureTransition(getSharedElementTransition());
-        Transition viewsTransition = configureTransition(getViewsTransition());
-        viewsTransition = addTargets(viewsTransition, mTransitioningViews);
-        if (sharedElementTransition == null || mSharedElements.isEmpty()) {
-            sharedElementTransitionComplete();
-            sharedElementTransition = null;
-        } else {
-            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
-                @Override
-                public void onTransitionEnd(Transition transition) {
-                    sharedElementTransitionComplete();
-                }
-            });
+    private Transition getExitTransition() {
+        Transition viewsTransition = null;
+        if (!mTransitioningViews.isEmpty()) {
+            viewsTransition = configureTransition(getViewsTransition());
         }
-        if (viewsTransition == null || mTransitioningViews.isEmpty()) {
+        if (viewsTransition == null) {
             exitTransitionComplete();
-            viewsTransition = null;
         } else {
             viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
                 @Override
@@ -191,13 +220,46 @@
                         setViewVisibility(mTransitioningViews, View.VISIBLE);
                     }
                 }
+
+                @Override
+                public void onTransitionCancel(Transition transition) {
+                    super.onTransitionCancel(transition);
+                }
             });
         }
+        return viewsTransition;
+    }
+
+    private Transition getSharedElementExitTransition() {
+        Transition sharedElementTransition = null;
+        if (!mSharedElements.isEmpty()) {
+            sharedElementTransition = configureTransition(getSharedElementTransition());
+        }
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        } else {
+            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    sharedElementTransitionComplete();
+                    if (mIsHidden) {
+                        setViewVisibility(mSharedElements, View.VISIBLE);
+                    }
+                }
+            });
+            mSharedElements.get(0).invalidate();
+        }
+        return sharedElementTransition;
+    }
+
+    private void beginTransitions() {
+        Transition sharedElementTransition = getSharedElementExitTransition();
+        Transition viewsTransition = getExitTransition();
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
-        TransitionManager.beginDelayedTransition(getDecor(), transition);
-        if (viewsTransition == null && sharedElementTransition != null) {
-            mSharedElements.get(0).requestLayout();
+        mExitTransitionStarted = true;
+        if (transition != null) {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
         }
     }
 
@@ -207,18 +269,12 @@
     }
 
     protected boolean isReadyToNotify() {
-        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
+        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
+                && mExitTransitionStarted;
     }
 
     private void sharedElementTransitionComplete() {
-        Bundle bundle = new Bundle();
-        int[] tempLoc = new int[2];
-        for (int i = 0; i < mSharedElementNames.size(); i++) {
-            View sharedElement = mSharedElements.get(i);
-            String name = mSharedElementNames.get(i);
-            captureSharedElementState(sharedElement, name, bundle, tempLoc);
-        }
-        mSharedElementBundle = bundle;
+        mSharedElementBundle = captureSharedElementState();
         notifyComplete();
     }
 
@@ -232,15 +288,23 @@
                 mExitNotified = true;
                 mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
                 mResultReceiver = null; // done talking
-                if (mIsReturning) {
-                    mActivity.finish();
-                    mActivity.overridePendingTransition(0, 0);
-                }
-                mActivity = null;
+                finishIfNecessary();
             }
         }
     }
 
+    private void finishIfNecessary() {
+        if (mIsReturning && mExitNotified && (mSharedElements.isEmpty()
+                || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+            mActivity.finish();
+            mActivity.overridePendingTransition(0, 0);
+            mActivity = null;
+        }
+        if (!mIsReturning && mExitNotified) {
+            mActivity = null; // don't need it anymore
+        }
+    }
+
     @Override
     protected Transition getViewsTransition() {
         if (mIsReturning) {
@@ -257,58 +321,4 @@
             return getWindow().getSharedElementExitTransition();
         }
     }
-
-    /**
-     * Captures placement information for Views with a shared element name for
-     * Activity Transitions.
-     *
-     * @param view           The View to capture the placement information for.
-     * @param name           The shared element name in the target Activity to apply the placement
-     *                       information for.
-     * @param transitionArgs Bundle to store shared element placement information.
-     * @param tempLoc        A temporary int[2] for capturing the current location of views.
-     */
-    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] tempLoc) {
-        Bundle sharedElementBundle = new Bundle();
-        view.getLocationOnScreen(tempLoc);
-        float scaleX = view.getScaleX();
-        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
-        int width = Math.round(view.getWidth() * scaleX);
-        sharedElementBundle.putInt(KEY_WIDTH, width);
-
-        float scaleY = view.getScaleY();
-        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
-        int height = Math.round(view.getHeight() * scaleY);
-        sharedElementBundle.putInt(KEY_HEIGHT, height);
-
-        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
-
-        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
-        sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
-
-        if (view instanceof ImageView) {
-            ImageView imageView = (ImageView) view;
-            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
-            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
-            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
-                float[] matrix = new float[9];
-                imageView.getImageMatrix().getValues(matrix);
-                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
-            }
-        }
-
-        transitionArgs.putBundle(name, sharedElementBundle);
-    }
-
-    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
-        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
-            if (scaleType == SCALE_TYPE_VALUES[i]) {
-                return i;
-            }
-        }
-        return -1;
-    }
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index dfd927f..90aeaae 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,120 @@
                 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)
+         *         .extend(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 +2273,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 +3254,670 @@
             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;
+
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * full screen.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_FULL_SCREEN = 5;
+
+        /** 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. The {@link PendingIntent} provided should be for an activity.
+         *
+         * <pre class="prettyprint">
+         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
+         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
+         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+         * Notification notif = new Notification.Builder(context)
+         *         .extend(new Notification.WearableExtender()
+         *                 .setDisplayIntent(displayPendingIntent)
+         *                 .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM))
+         *         .build();</pre>
+         *
+         * <p>The activity to launch needs to allow embedding, must be exported, and
+         * should have an empty task affinity.
+         *
+         * <p>Example AndroidManifest.xml entry:
+         * <pre class="prettyprint">
+         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
+         *     android:exported=&quot;true&quot;
+         *     android:allowEmbedded=&quot;true&quot;
+         *     android:taskAffinity=&quot;&quot; /&gt;</pre>
+         *
+         * @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. This action will no longer display separately from the
+         * notification's content.
+         *
+         * <p>For notifications with multiple pages, child pages can also have content actions
+         * set, although the list of available actions comes from the main notification and not
+         * from the child page's notification.
+         *
+         * @param actionIndex The index of the action to hoist onto the current notification page.
+         *                    If wearable actions were added to the main notification, this index
+         *                    will apply to that list, otherwise it will apply to the regular
+         *                    actions list.
+         */
+        public WearableExtender setContentAction(int actionIndex) {
+            mContentActionIndex = actionIndex;
+            return this;
+        }
+
+        /**
+         * Get the index of the notification action, if any, that was specified as being clickable
+         * with the content of this notification. This action will no longer display separately
+         * from the notification's content.
+         *
+         * <p>For notifications with multiple pages, child pages can also have content actions
+         * set, although the list of available actions comes from the main notification and not
+         * from the child page's notification.
+         *
+         * <p>If wearable specific actions were added to the main notification, this index will
+         * apply to that list, otherwise it will apply to the regular actions list.
+         *
+         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
+         */
+        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/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java
new file mode 100644
index 0000000..f42839e
--- /dev/null
+++ b/core/java/android/app/TaskManagerImpl.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+// in android.app so ContextImpl has package access
+package android.app;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
+import android.app.task.TaskManager;
+
+import java.util.List;
+
+
+/**
+ * Concrete implementation of the TaskManager interface
+ * @hide 
+ */
+public class TaskManagerImpl extends TaskManager {
+    ITaskManager mBinder;
+
+    /* package */ TaskManagerImpl(ITaskManager binder) {
+        mBinder = binder;
+    }
+
+    @Override
+    public int schedule(Task task) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void cancel(int taskId) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void cancelAll() {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public List<Task> getAllPendingTasks() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl
new file mode 100644
index 0000000..b56c78a
--- /dev/null
+++ b/core/java/android/app/task/ITaskManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * 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.task;
+
+import android.app.task.Task;
+
+ /**
+  * IPC interface that supports the app-facing {@link #TaskManager} api.
+  * {@hide}
+  */
+interface ITaskManager {
+    int schedule(in Task task);
+    void cancel(int taskId);
+    void cancelAll();
+    List<Task> getAllPendingTasks();
+}
diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl
new file mode 100644
index 0000000..1f25439
--- /dev/null
+++ b/core/java/android/app/task/Task.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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.task;
+
+parcelable Task;
+ 
\ No newline at end of file
diff --git a/core/java/android/content/Task.java b/core/java/android/app/task/Task.java
similarity index 85%
rename from core/java/android/content/Task.java
rename to core/java/android/app/task/Task.java
index 407880f..ca4aeb2 100644
--- a/core/java/android/content/Task.java
+++ b/core/java/android/app/task/Task.java
@@ -14,23 +14,26 @@
  * limitations under the License
  */
 
-package android.content;
+package android.app.task;
 
-import android.app.task.TaskService;
+import android.content.ComponentName;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
+ * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the
  * parameters required to schedule work against the calling application. These are constructed
  * using the {@link Task.Builder}.
  */
 public class Task implements Parcelable {
-
     public interface NetworkType {
-        public final int ANY = 0;
-        public final int UNMETERED = 1;
+        /** Default. */
+        public final int NONE = 0;
+        /** This task requires network connectivity. */
+        public final int ANY = 1;
+        /** This task requires network connectivity that is unmetered. */
+        public final int UNMETERED = 2;
     }
 
     /**
@@ -48,6 +51,8 @@
     private final ComponentName service;
     private final boolean requireCharging;
     private final boolean requireDeviceIdle;
+    private final boolean hasEarlyConstraint;
+    private final boolean hasLateConstraint;
     private final int networkCapabilities;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
@@ -59,7 +64,7 @@
     /**
      * Unique task id associated with this class. This is assigned to your task by the scheduler.
      */
-    public int getTaskId() {
+    public int getId() {
         return taskId;
     }
 
@@ -92,7 +97,7 @@
     }
 
     /**
-     * See {@link android.content.Task.NetworkType} for a description of this value.
+     * See {@link android.app.task.Task.NetworkType} for a description of this value.
      */
     public int getNetworkCapabilities() {
         return networkCapabilities;
@@ -139,13 +144,31 @@
     }
 
     /**
-     * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
+     * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field
      * can take. This defaults to exponential.
      */
     public int getBackoffPolicy() {
         return backoffPolicy;
     }
 
+    /**
+     * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
+     * function was called at all.
+     * @hide
+     */
+    public boolean hasEarlyConstraint() {
+        return hasEarlyConstraint;
+    }
+
+    /**
+     * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
+     * function was called at all.
+     * @hide
+     */
+    public boolean hasLateConstraint() {
+        return hasLateConstraint;
+    }
+
     private Task(Parcel in) {
         taskId = in.readInt();
         extras = in.readBundle();
@@ -159,6 +182,8 @@
         intervalMillis = in.readLong();
         initialBackoffMillis = in.readLong();
         backoffPolicy = in.readInt();
+        hasEarlyConstraint = in.readInt() == 1;
+        hasLateConstraint = in.readInt() == 1;
     }
 
     private Task(Task.Builder b) {
@@ -174,6 +199,8 @@
         intervalMillis = b.mIntervalMillis;
         initialBackoffMillis = b.mInitialBackoffMillis;
         backoffPolicy = b.mBackoffPolicy;
+        hasEarlyConstraint = b.mHasEarlyConstraint;
+        hasLateConstraint = b.mHasLateConstraint;
     }
 
     @Override
@@ -195,6 +222,8 @@
         out.writeLong(intervalMillis);
         out.writeLong(initialBackoffMillis);
         out.writeInt(backoffPolicy);
+        out.writeInt(hasEarlyConstraint ? 1 : 0);
+        out.writeInt(hasLateConstraint ? 1 : 0);
     }
 
     public static final Creator<Task> CREATOR = new Creator<Task>() {
@@ -212,7 +241,7 @@
     /**
      * Builder class for constructing {@link Task} objects.
      */
-    public final class Builder {
+    public static final class Builder {
         private int mTaskId;
         private Bundle mExtras;
         private ComponentName mTaskService;
@@ -225,6 +254,8 @@
         private long mMaxExecutionDelayMillis;
         // Periodic parameters.
         private boolean mIsPeriodic;
+        private boolean mHasEarlyConstraint;
+        private boolean mHasLateConstraint;
         private long mIntervalMillis;
         // Back-off parameters.
         private long mInitialBackoffMillis = 5000L;
@@ -255,7 +286,7 @@
 
         /**
          * Set some description of the kind of network capabilities you would like to have. This
-         * will be a parameter defined in {@link android.content.Task.NetworkType}.
+         * will be a parameter defined in {@link android.app.task.Task.NetworkType}.
          * Not calling this function means the network is not necessary.
          * Bear in mind that calling this function defines network as a strict requirement for your
          * task if the network requested is not available your task will never run. See
@@ -307,6 +338,7 @@
         public Builder setPeriodic(long intervalMillis) {
             mIsPeriodic = true;
             mIntervalMillis = intervalMillis;
+            mHasEarlyConstraint = mHasLateConstraint = true;
             return this;
         }
 
@@ -314,12 +346,13 @@
          * Specify that this task should be delayed by the provided amount of time.
          * Because it doesn't make sense setting this property on a periodic task, doing so will
          * throw an {@link java.lang.IllegalArgumentException} when
-         * {@link android.content.Task.Builder#build()} is called.
+         * {@link android.app.task.Task.Builder#build()} is called.
          * @param minLatencyMillis Milliseconds before which this task will not be considered for
          *                         execution.
          */
         public Builder setMinimumLatency(long minLatencyMillis) {
             mMinLatencyMillis = minLatencyMillis;
+            mHasEarlyConstraint = true;
             return this;
         }
 
@@ -328,10 +361,11 @@
          * deadline even if other requirements are not met. Because it doesn't make sense setting
          * this property on a periodic task, doing so will throw an
          * {@link java.lang.IllegalArgumentException} when
-         * {@link android.content.Task.Builder#build()} is called.
+         * {@link android.app.task.Task.Builder#build()} is called.
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+            mHasLateConstraint = true;
             return this;
         }
 
@@ -360,31 +394,18 @@
          * @return The task object to hand to the TaskManager. This object is immutable.
          */
         public Task build() {
-            // Check that extras bundle only contains primitive types.
-            try {
-                for (String key : extras.keySet()) {
-                    Object value = extras.get(key);
-                    if (value == null) continue;
-                    if (value instanceof Long) continue;
-                    if (value instanceof Integer) continue;
-                    if (value instanceof Boolean) continue;
-                    if (value instanceof Float) continue;
-                    if (value instanceof Double) continue;
-                    if (value instanceof String) continue;
-                    throw new IllegalArgumentException("Unexpected value type: "
-                            + value.getClass().getName());
-                }
-            } catch (IllegalArgumentException e) {
-                throw e;
-            } catch (RuntimeException exc) {
-                throw new IllegalArgumentException("error unparcelling Bundle", exc);
+            if (mExtras == null) {
+                mExtras = Bundle.EMPTY;
+            }
+            if (mTaskId < 0) {
+                throw new IllegalArgumentException("Task id must be greater than 0.");
             }
             // Check that a deadline was not set on a periodic task.
-            if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+            if (mIsPeriodic && mHasLateConstraint) {
                 throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
                         "periodic task.");
             }
-            if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+            if (mIsPeriodic && mHasEarlyConstraint) {
                 throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
                         "periodic task");
             }
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/app/task/TaskManager.java
similarity index 76%
rename from core/java/android/content/TaskManager.java
rename to core/java/android/app/task/TaskManager.java
index d28d78a..00f57da 100644
--- a/core/java/android/content/TaskManager.java
+++ b/core/java/android/app/task/TaskManager.java
@@ -14,14 +14,19 @@
  * limitations under the License
  */
 
-package android.content;
+package android.app.task;
 
 import java.util.List;
 
+import android.content.Context;
+
 /**
  * Class for scheduling various types of tasks with the scheduling framework on the device.
  *
- * Get an instance of this class through {@link Context#getSystemService(String)}.
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TASK_SERVICE)}.
  */
 public abstract class TaskManager {
     /*
@@ -29,18 +34,19 @@
      * if the run-time for your task is too short, or perhaps the system can't resolve the
      * requisite {@link TaskService} in your package.
      */
-    static final int RESULT_INVALID_PARAMETERS = -1;
+    public static final int RESULT_FAILURE = 0;
     /**
      * Returned from {@link #schedule(Task)} if this application has made too many requests for
      * work over too short a time.
      */
     // TODO: Determine if this is necessary.
-    static final int RESULT_OVER_QUOTA = -2;
+    public static final int RESULT_SUCCESS = 1;
 
-    /*
-     * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
-     * the sorts of tasks you can schedule.
-     * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
+    /**
+     * @param task The task you wish scheduled. See
+     * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks
+     * you can schedule.
+     * @return If >0, this int returns the taskId of the successfully scheduled task.
      * Otherwise you have to compare the return value to the error codes defined in this class.
      */
     public abstract int schedule(Task task);
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index 0351082..dacb3480 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -47,7 +47,7 @@
 
     /**
      * @return The extras you passed in when constructing this task with
-     * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+     * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will
      * never be null. If you did not set any extras this will be an empty bundle.
      */
     public Bundle getExtras() {
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
index ab1a565..8ce4484 100644
--- a/core/java/android/app/task/TaskService.java
+++ b/core/java/android/app/task/TaskService.java
@@ -28,7 +28,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 /**
- * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p>
+ * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p>
  * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
  * are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
  * you will implement your task logic.</p>
@@ -215,9 +215,9 @@
      *
      * <p>This will happen if the requirements specified at schedule time are no longer met. For
      * example you may have requested WiFi with
-     * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+     * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
      * task was executing the user toggled WiFi. Another example is if you had specified
-     * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+     * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
      * idle maintenance window. You are solely responsible for the behaviour of your application
      * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
      * immediate repercussion is that the system will cease holding a wakelock for you.</p>
@@ -237,7 +237,7 @@
      *     You can specify post-execution behaviour to the scheduler here with
      *     <code>needsReschedule </code>. This will apply a back-off timer to your task based on
      *     the default, or what was set with
-     *     {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original
+     *     {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original
      *     requirements are always honoured even for a backed-off task. Note that a task running in
      *     idle mode will not be backed-off. Instead what will happen is the task will be re-added
      *     to the queue and re-executed within a future idle maintenance window.
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/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index d3e9089..e5bf7d0 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -317,9 +317,9 @@
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
 
     /**
-     * Sent to providers after AppWidget state related to the provider has been restored from
-     * backup. The intent contains information about how to translate AppWidget ids from the
-     * restored data to their new equivalents.
+     * Sent to an {@link AppWidgetProvider} after AppWidget state related to that provider has
+     * been restored from backup. The intent contains information about how to translate AppWidget
+     * ids from the restored data to their new equivalents.
      *
      * <p>The intent will contain the following extras:
      *
@@ -343,7 +343,7 @@
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
-     * @see {@link #ACTION_APPWIDGET_HOST_RESTORED} for the corresponding host broadcast
+     * @see #ACTION_APPWIDGET_HOST_RESTORED
      */
     public static final String ACTION_APPWIDGET_RESTORED
             = "android.appwidget.action.APPWIDGET_RESTORED";
@@ -352,7 +352,7 @@
      * Sent to widget hosts after AppWidget state related to the host has been restored from
      * backup. The intent contains information about how to translate AppWidget ids from the
      * restored data to their new equivalents.  If an application maintains multiple separate
-     * widget hosts instances, it will receive this broadcast separately for each one.
+     * widget host instances, it will receive this broadcast separately for each one.
      *
      * <p>The intent will contain the following extras:
      *
@@ -380,7 +380,7 @@
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
-     * @see {@link #ACTION_APPWIDGET_RESTORED} for the corresponding provider broadcast
+     * @see #ACTION_APPWIDGET_RESTORED
      */
     public static final String ACTION_APPWIDGET_HOST_RESTORED
             = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ae006c..b0673b5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2023,6 +2023,7 @@
             PRINT_SERVICE,
             MEDIA_SESSION_SERVICE,
             BATTERY_SERVICE,
+            TASK_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -2079,6 +2080,8 @@
      * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
      * <dt> {@link #BATTERY_SERVICE} ("batterymanager")
      * <dd> A {@link android.os.BatteryManager} for managing battery state
+     * <dt> {@link #TASK_SERVICE} ("taskmanager")
+     * <dd>  A {@link android.app.task.TaskManager} for managing scheduled tasks
      * </dl>
      *
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -2134,6 +2137,8 @@
      * @see android.app.DownloadManager
      * @see #BATTERY_SERVICE
      * @see android.os.BatteryManager
+     * @see #TASK_SERVICE
+     * @see android.app.task.TaskManager
      */
     public abstract Object getSystemService(@ServiceName @NonNull String name);
 
@@ -2728,6 +2733,15 @@
     public static final String USAGE_STATS_SERVICE = "usagestats";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.app.task.TaskManager} instance for managing occasional
+     * background tasks.
+     * @see #getSystemService
+     * @see android.app.task.TaskManager
+     */
+    public static final String TASK_SERVICE = "task";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 076f657..bd07470 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -45,6 +45,7 @@
 import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.Serializable;
@@ -604,6 +605,15 @@
  * of all possible flags.
  */
 public class Intent implements Parcelable, Cloneable {
+    private static final String ATTR_ACTION = "action";
+    private static final String TAG_CATEGORIES = "categories";
+    private static final String ATTR_CATEGORY = "category";
+    private static final String TAG_EXTRA = "extra";
+    private static final String ATTR_TYPE = "type";
+    private static final String ATTR_COMPONENT = "component";
+    private static final String ATTR_DATA = "data";
+    private static final String ATTR_FLAGS = "flags";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent activity actions (see action variable).
@@ -3729,32 +3739,27 @@
      */
     public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
     /**
-     * This flag is used to break out "documents" into separate tasks that can
-     * be reached via the Recents mechanism. Such a document is any kind of
-     * item for which an application may want to maintain multiple simultaneous
-     * instances. Examples might be text files, web pages, spreadsheets, or
-     * emails. Each such document will be in a separate task in the Recents list.
+     * This flag is used to open a document into a new task rooted at the activity launched
+     * by this Intent. Through the use of this flag, or its equivalent attribute,
+     * {@link android.R.attr#documentLaunchMode} multiple instances of the same activity
+     * containing different douments will appear in the recent tasks list.
      *
-     * <p>When set, the activity specified by this Intent will launch into a
-     * separate task rooted at that activity. The activity launched must be
-     * defined with {@link android.R.attr#launchMode} <code>standard</code>
-     * or <code>singleTop</code>.
+     * <p>The use of the activity attribute form of this,
+     * {@link android.R.attr#documentLaunchMode}, is
+     * preferred over the Intent flag described here. The attribute form allows the
+     * Activity to specify multiple document behavior for all launchers of the Activity
+     * whereas using this flag requires each Intent that launches the Activity to specify it.
      *
-     * <p>If FLAG_ACTIVITY_NEW_DOCUMENT is used without
-     * {@link #FLAG_ACTIVITY_MULTIPLE_TASK} then the activity manager will
-     * search for an existing task with a matching target activity and Intent
-     * data URI and relaunch that task, first finishing all activities down to
-     * the root activity and then calling the root activity's
-     * {@link android.app.Activity#onNewIntent(Intent)} method. If no existing
-     * task's root activity matches the Intent's data URI then a new task will
-     * be launched with the target activity as root.
+     * <p>FLAG_ACTIVITY_NEW_DOCUMENT may be used in conjunction with {@link
+     * #FLAG_ACTIVITY_MULTIPLE_TASK}. When used alone it is the
+     * equivalent of the Activity manifest specifying {@link
+     * android.R.attr#documentLaunchMode}="intoExisting". When used with
+     * FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
+     * {@link android.R.attr#documentLaunchMode}="always".
      *
-     * <p>When paired with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} this will
-     * always create a new task. Thus the same document may be made to appear
-     * more than one time in Recents.
+     * Refer to {@link android.R.attr#documentLaunchMode} for more information.
      *
-     * <p>This is equivalent to the attribute {@link android.R.attr#documentLaunchMode}.
-     *
+     * @see android.R.attr#documentLaunchMode
      * @see #FLAG_ACTIVITY_MULTIPLE_TASK
      */
     public static final int FLAG_ACTIVITY_NEW_DOCUMENT =
@@ -7347,7 +7352,7 @@
             }
 
             String nodeName = parser.getName();
-            if (nodeName.equals("category")) {
+            if (nodeName.equals(TAG_CATEGORIES)) {
                 sa = resources.obtainAttributes(attrs,
                         com.android.internal.R.styleable.IntentCategory);
                 String cat = sa.getString(com.android.internal.R.styleable.IntentCategory_name);
@@ -7358,11 +7363,11 @@
                 }
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (nodeName.equals("extra")) {
+            } else if (nodeName.equals(TAG_EXTRA)) {
                 if (intent.mExtras == null) {
                     intent.mExtras = new Bundle();
                 }
-                resources.parseBundleExtra("extra", attrs, intent.mExtras);
+                resources.parseBundleExtra(TAG_EXTRA, attrs, intent.mExtras);
                 XmlUtils.skipCurrentTag(parser);
 
             } else {
@@ -7373,6 +7378,76 @@
         return intent;
     }
 
+    /** @hide */
+    public void saveToXml(XmlSerializer out) throws IOException {
+        if (mAction != null) {
+            out.attribute(null, ATTR_ACTION, mAction);
+        }
+        if (mData != null) {
+            out.attribute(null, ATTR_DATA, mData.toString());
+        }
+        if (mType != null) {
+            out.attribute(null, ATTR_TYPE, mType);
+        }
+        if (mComponent != null) {
+            out.attribute(null, ATTR_COMPONENT, mComponent.flattenToShortString());
+        }
+        out.attribute(null, ATTR_FLAGS, Integer.toHexString(getFlags()));
+
+        if (mCategories != null) {
+            out.startTag(null, TAG_CATEGORIES);
+            for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
+                out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
+            }
+        }
+    }
+
+    /** @hide */
+    public static Intent restoreFromXml(XmlPullParser in) throws IOException,
+            XmlPullParserException {
+        Intent intent = new Intent();
+        final int outerDepth = in.getDepth();
+
+        int attrCount = in.getAttributeCount();
+        for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
+            final String attrName = in.getAttributeName(attrNdx);
+            final String attrValue = in.getAttributeValue(attrNdx);
+            if (ATTR_ACTION.equals(attrName)) {
+                intent.setAction(attrValue);
+            } else if (ATTR_DATA.equals(attrName)) {
+                intent.setData(Uri.parse(attrValue));
+            } else if (ATTR_TYPE.equals(attrName)) {
+                intent.setType(attrValue);
+            } else if (ATTR_COMPONENT.equals(attrName)) {
+                intent.setComponent(ComponentName.unflattenFromString(attrValue));
+            } else if (ATTR_FLAGS.equals(attrName)) {
+                intent.setFlags(Integer.valueOf(attrValue, 16));
+            } else {
+                Log.e("Intent", "restoreFromXml: unknown attribute=" + attrName);
+            }
+        }
+
+        int event;
+        String name;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                name = in.getName();
+                if (TAG_CATEGORIES.equals(name)) {
+                    attrCount = in.getAttributeCount();
+                    for (int attrNdx = attrCount - 1; attrNdx >= 0; --attrNdx) {
+                        intent.addCategory(in.getAttributeValue(attrNdx));
+                    }
+                } else {
+                    Log.w("Intent", "restoreFromXml: unknown name=" + name);
+                    XmlUtils.skipCurrentTag(in);
+                }
+            }
+        }
+
+        return intent;
+    }
+
     /**
      * Normalize a MIME data type.
      *
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a78f8e2..3737638 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -31,11 +31,11 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Trace;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.TypedValue;
 import android.util.LongSparseArray;
 
@@ -104,10 +104,10 @@
     // These are protected by mAccessLock.
     private final Object mAccessLock = new Object();
     private final Configuration mTmpConfig = new Configuration();
-    private final ThemedCaches<ConstantState> mDrawableCache =
-            new ThemedCaches<ConstantState>();
-    private final ThemedCaches<ConstantState> mColorDrawableCache =
-            new ThemedCaches<ConstantState>();
+    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
+            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
+            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
     private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache =
             new LongSparseArray<WeakReference<ColorStateList>>();
 
@@ -1261,18 +1261,17 @@
          * any of the style's attributes are already defined in the theme, the
          * current values in the theme will be overwritten.
          * 
-         * @param resid The resource ID of a style resource from which to
+         * @param resId The resource ID of a style resource from which to
          *              obtain attribute values.
          * @param force If true, values in the style resource will always be
          *              used in the theme; otherwise, they will only be used
          *              if not already defined in the theme.
          */
-        public void applyStyle(int resid, boolean force) {
-            AssetManager.applyThemeStyle(mTheme, resid, force);
+        public void applyStyle(int resId, boolean force) {
+            AssetManager.applyThemeStyle(mTheme, resId, force);
 
-            // TODO: In very rare cases, we may end up with a hybrid theme
-            // that can't map to a single theme ID.
-            mThemeResId = resid;
+            mThemeResId = resId;
+            mKey += Integer.toHexString(resId) + (force ? "! " : " ");
         }
 
         /**
@@ -1288,6 +1287,7 @@
             AssetManager.copyTheme(mTheme, other.mTheme);
 
             mThemeResId = other.mThemeResId;
+            mKey = other.mKey;
         }
 
         /**
@@ -1577,6 +1577,9 @@
         /** Resource identifier for the theme. */
         private int mThemeResId = 0;
 
+        /** Unique key for the series of styles applied to this theme. */
+        private String mKey = "";
+
         // Needed by layoutlib.
         /*package*/ long getNativeTheme() {
             return mTheme;
@@ -1585,6 +1588,10 @@
         /*package*/ int getAppliedStyleResId() {
             return mThemeResId;
         }
+
+        /*package*/ String getKey() {
+            return mKey;
+        }
     }
 
     /**
@@ -1740,7 +1747,8 @@
     }
 
     private void clearDrawableCachesLocked(
-            ThemedCaches<ConstantState> caches, int configChanges) {
+            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+            int configChanges) {
         final int N = caches.size();
         for (int i = 0; i < N; i++) {
             clearDrawableCacheLocked(caches.valueAt(i), configChanges);
@@ -1763,7 +1771,7 @@
                             configChanges, cs.getChangingConfigurations())) {
                         if (DEBUG_CONFIG) {
                             Log.d(TAG, "FLUSHING #0x"
-                                    + Long.toHexString(mDrawableCache.keyAt(i))
+                                    + Long.toHexString(cache.keyAt(i))
                                     + " / " + cs + " with changes: 0x"
                                     + Integer.toHexString(cs.getChangingConfigurations()));
                         }
@@ -2205,7 +2213,7 @@
         }
 
         final boolean isColorDrawable;
-        final ThemedCaches<ConstantState> caches;
+        final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
         final long key;
         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                 && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
@@ -2258,7 +2266,8 @@
     }
 
     private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable,
-            ThemedCaches<ConstantState> caches, long key, Drawable dr) {
+            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+            long key, Drawable dr) {
         final ConstantState cs = dr.getConstantState();
         if (cs == null) {
             return;
@@ -2287,8 +2296,12 @@
             }
         } else {
             synchronized (mAccessLock) {
-                final LongSparseArray<WeakReference<ConstantState>> themedCache;
-                themedCache = caches.getOrCreate(theme == null ? 0 : theme.mThemeResId);
+                final String themeKey = theme == null ? "" : theme.mKey;
+                LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
+                if (themedCache == null) {
+                    themedCache = new LongSparseArray<WeakReference<ConstantState>>(1);
+                    caches.put(themeKey, themedCache);
+                }
                 themedCache.put(key, new WeakReference<ConstantState>(cs));
             }
         }
@@ -2347,7 +2360,9 @@
         return dr;
     }
 
-    private Drawable getCachedDrawable(ThemedCaches<ConstantState> caches, long key, Theme theme) {
+    private Drawable getCachedDrawable(
+            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+            long key, Theme theme) {
         synchronized (mAccessLock) {
             final int themeKey = theme != null ? theme.mThemeResId : 0;
             final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
@@ -2584,21 +2599,4 @@
         updateConfiguration(null, null);
         mAssets.ensureStringBlocks();
     }
-
-    static class ThemedCaches<T> extends SparseArray<LongSparseArray<WeakReference<T>>> {
-        /**
-         * Returns the cache of drawables styled for the specified theme.
-         * <p>
-         * Drawables that have themeable attributes but were loaded without
-         * specifying a theme are cached at themeResId = 0.
-         */
-        public LongSparseArray<WeakReference<T>> getOrCreate(int themeResId) {
-            LongSparseArray<WeakReference<T>> result = get(themeResId);
-            if (result == null) {
-                result = new LongSparseArray<WeakReference<T>>(1);
-                put(themeResId, result);
-            }
-            return result;
-        }
-    }
 }
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 8391209..7738d2d 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -508,14 +508,62 @@
         }
 
         /**
-         * This method is called when an image capture has completed and the
-         * result metadata is available.
+         * This method is called when an image capture makes partial forward progress; some
+         * (but not all) results from an image capture are available.
+         *
+         * <p>The result provided here will contain some subset of the fields of
+         * a full result. Multiple {@link #onCaptureProgressed} calls may happen per
+         * capture; a given result field will only be present in one partial
+         * capture at most. The final {@link #onCaptureCompleted} call will always
+         * contain all the fields (in particular, the union of all the fields of all
+         * the partial results composing the total result).</p>
+         *
+         * <p>For each request, some result data might be available earlier than others. The typical
+         * delay between each partial result (per request) is a single frame interval.
+         * For performance-oriented use-cases, applications should query the metadata they need
+         * to make forward progress from the partial results and avoid waiting for the completed
+         * result.</p>
+         *
+         * <p>Each request will generate at least {@code 1} partial results, and at most
+         * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
+         *
+         * <p>Depending on the request settings, the number of partial results per request
+         * will vary, although typically the partial count could be the same as long as the
+         * camera device subsystems enabled stay the same.</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 output metadata from the capture, including the
+         * @param partialResult The partial output metadata from the capture, which
+         * includes a subset of the {@link TotalCaptureResult} fields.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureProgressed(CameraDevice camera,
+                CaptureRequest request, CaptureResult partialResult) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has fully completed and all the
+         * result metadata is available.
+         *
+         * <p>This callback will always fire after the last {@link #onCaptureProgressed};
+         * in other words, no more partial results will be delivered once the completed result
+         * is available.</p>
+         *
+         * <p>For performance-intensive use-cases where latency is a factor, consider
+         * using {@link #onCaptureProgressed} instead.</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 total output metadata from the capture, including the
          * final capture parameters and the state of the camera system during
          * capture.
          *
@@ -525,7 +573,7 @@
          * @see #setRepeatingBurst
          */
         public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
+                CaptureRequest request, TotalCaptureResult result) {
             // default empty implementation
         }
 
@@ -563,24 +611,57 @@
          * when a capture sequence finishes and all {@link CaptureResult}
          * or {@link CaptureFailure} for it have been returned via this listener.
          *
+         * <p>In total, there will be at least one result/failure returned by this listener
+         * before this callback is invoked. If the capture sequence is aborted before any
+         * requests have been processed, {@link #onCaptureSequenceAborted} is invoked instead.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
          * @param camera
          *            The CameraDevice sending the callback.
          * @param sequenceId
          *            A sequence ID returned by the {@link #capture} family of functions.
-         * @param lastFrameNumber
+         * @param frameNumber
          *            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()
+         * @see #onCaptureSequenceAborted
          */
         public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, int lastFrameNumber) {
+                int sequenceId, long frameNumber) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called independently of the others in CaptureListener,
+         * when a capture sequence aborts before any {@link CaptureResult}
+         * or {@link CaptureFailure} for it have been returned via this listener.
+         *
+         * <p>Due to the asynchronous nature of the camera device, not all submitted captures
+         * are immediately processed. It is possible to clear out the pending requests
+         * by a variety of operations such as {@link CameraDevice#stopRepeating} or
+         * {@link CameraDevice#flush}. When such an event happens,
+         * {@link #onCaptureSequenceCompleted} will not be called.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param sequenceId
+         *            A sequence ID returned by the {@link #capture} family of functions.
+         *
+         * @see CaptureResult#getFrameNumber()
+         * @see CaptureFailure#getFrameNumber()
+         * @see CaptureResult#getSequenceId()
+         * @see CaptureFailure#getSequenceId()
+         * @see #onCaptureSequenceCompleted
+         */
+        public void onCaptureSequenceAborted(CameraDevice camera,
+                int sequenceId) {
             // default empty implementation
         }
     }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 164e683..cea68d2 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -16,7 +16,9 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.CaptureResult.Key;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
 import android.util.Rational;
 
 import java.util.Collections;
@@ -35,18 +37,109 @@
  * @see CameraDevice
  * @see CameraManager
  */
-public final class CameraCharacteristics extends CameraMetadata {
+public final class CameraCharacteristics extends CameraMetadata<CameraCharacteristics.Key<?>> {
+
+    /**
+     * A {@code Key} is used to do camera characteristics field lookups with
+     * {@link CameraCharacteristics#get}.
+     *
+     * <p>For example, to get the stream configuration map:
+     * <code><pre>
+     * StreamConfigurationMap map = cameraCharacteristics.get(
+     *      CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+     * </pre></code>
+     * </p>
+     *
+     * <p>To enumerate over all possible keys for {@link CameraCharacteristics}, see
+     * {@link CameraCharacteristics#getKeys()}.</p>
+     *
+     * @see CameraCharacteristics#get
+     * @see CameraCharacteristics#getKeys()
+     */
+    public static final class Key<T> {
+        private final CameraMetadataNative.Key<T> mKey;
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
+            mKey = new CameraMetadataNative.Key<T>(name,  type);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, TypeReference<T> typeReference) {
+            mKey = new CameraMetadataNative.Key<T>(name,  typeReference);
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public String getName() {
+            return mKey.getName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mKey.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+        }
+
+        /**
+         * Visible for CameraMetadataNative implementation only; do not use.
+         *
+         * TODO: Make this private or remove it altogether.
+         *
+         * @hide
+         */
+        public CameraMetadataNative.Key<T> getNativeKey() {
+            return mKey;
+        }
+
+        @SuppressWarnings({
+                "unused", "unchecked"
+        })
+        private Key(CameraMetadataNative.Key<?> nativeKey) {
+            mKey = (CameraMetadataNative.Key<T>) nativeKey;
+        }
+    }
 
     private final CameraMetadataNative mProperties;
-    private List<Key<?>> mAvailableRequestKeys;
-    private List<Key<?>> mAvailableResultKeys;
+    private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
+    private List<CaptureResult.Key<?>> mAvailableResultKeys;
 
     /**
      * Takes ownership of the passed-in properties object
      * @hide
      */
     public CameraCharacteristics(CameraMetadataNative properties) {
-        mProperties = properties;
+        mProperties = CameraMetadataNative.move(properties);
     }
 
     /**
@@ -57,12 +150,55 @@
         return new CameraMetadataNative(mProperties);
     }
 
-    @Override
+    /**
+     * Get a camera characteristics field value.
+     *
+     * <p>The field definitions can be
+     * found in {@link CameraCharacteristics}.</p>
+     *
+     * <p>Querying the value for the same key more than once will return a value
+     * which is equal to the previous queried value.</p>
+     *
+     * @throws IllegalArgumentException if the key was not valid
+     *
+     * @param key The characteristics field to read.
+     * @return The value of that key, or {@code null} if the field is not set.
+     */
     public <T> T get(Key<T> key) {
         return mProperties.get(key);
     }
 
     /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getProtected(Key<?> key) {
+        return (T) mProperties.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<Key<?>> getKeyClass() {
+        Object thisClass = Key.class;
+        return (Class<Key<?>>)thisClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Key<?>> getKeys() {
+        // Force the javadoc for this function to show up on the CameraCharacteristics page
+        return super.getKeys();
+    }
+
+    /**
      * Returns the list of keys supported by this {@link CameraDevice} for querying
      * with a {@link CaptureRequest}.
      *
@@ -76,9 +212,14 @@
      *
      * @return List of keys supported by this CameraDevice for CaptureRequests.
      */
-    public List<Key<?>> getAvailableCaptureRequestKeys() {
+    @SuppressWarnings({"unchecked"})
+    public List<CaptureRequest.Key<?>> getAvailableCaptureRequestKeys() {
         if (mAvailableRequestKeys == null) {
-            mAvailableRequestKeys = getAvailableKeyList(CaptureRequest.class);
+            Object crKey = CaptureRequest.Key.class;
+            Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+            mAvailableRequestKeys = Collections.unmodifiableList(
+                    getAvailableKeyList(CaptureRequest.class, crKeyTyped));
         }
         return mAvailableRequestKeys;
     }
@@ -97,9 +238,14 @@
      *
      * @return List of keys supported by this CameraDevice for CaptureResults.
      */
-    public List<Key<?>> getAvailableCaptureResultKeys() {
+    @SuppressWarnings({"unchecked"})
+    public List<CaptureResult.Key<?>> getAvailableCaptureResultKeys() {
         if (mAvailableResultKeys == null) {
-            mAvailableResultKeys = getAvailableKeyList(CaptureResult.class);
+            Object crKey = CaptureResult.Key.class;
+            Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+
+            mAvailableResultKeys = Collections.unmodifiableList(
+                    getAvailableKeyList(CaptureResult.class, crKeyTyped));
         }
         return mAvailableResultKeys;
     }
@@ -113,12 +259,14 @@
      * <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
      *
      * @param metadataClass The subclass of CameraMetadata that you want to get the keys for.
+     * @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class
      *
      * @return List of keys supported by this CameraDevice for metadataClass.
      *
      * @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
      */
-    private <T extends CameraMetadata> List<Key<?>> getAvailableKeyList(Class<T> metadataClass) {
+    private <TKey> List<TKey>
+    getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass) {
 
         if (metadataClass.equals(CameraMetadata.class)) {
             throw new AssertionError(
@@ -128,7 +276,9 @@
                     "metadataClass must be a subclass of CameraMetadata");
         }
 
-        return Collections.unmodifiableList(getKeysStatic(metadataClass, /*instance*/null));
+        List<TKey> staticKeyList = CameraCharacteristics.<TKey>getKeysStatic(
+                metadataClass, keyClass, /*instance*/null);
+        return Collections.unmodifiableList(staticKeyList);
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -146,8 +296,8 @@
      * valid anti-banding modes that the application may request
      * for this camera device; they must include AUTO.</p>
      */
-    public static final Key<byte[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES =
-            new Key<byte[]>("android.control.aeAvailableAntibandingModes", byte[].class);
+    public static final Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES =
+            new Key<int[]>("android.control.aeAvailableAntibandingModes", int[].class);
 
     /**
      * <p>The set of auto-exposure modes that are supported by this
@@ -165,15 +315,15 @@
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      */
-    public static final Key<byte[]> CONTROL_AE_AVAILABLE_MODES =
-            new Key<byte[]>("android.control.aeAvailableModes", byte[].class);
+    public static final Key<int[]> CONTROL_AE_AVAILABLE_MODES =
+            new Key<int[]>("android.control.aeAvailableModes", int[].class);
 
     /**
      * <p>List of frame rate ranges supported by the
      * AE algorithm/hardware</p>
      */
-    public static final Key<int[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES =
-            new Key<int[]>("android.control.aeAvailableTargetFpsRanges", int[].class);
+    public static final Key<android.util.Range<Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES =
+            new Key<android.util.Range<Integer>[]>("android.control.aeAvailableTargetFpsRanges", new TypeReference<android.util.Range<Integer>[]>() {{ }});
 
     /**
      * <p>Maximum and minimum exposure compensation
@@ -182,8 +332,8 @@
      *
      * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
      */
-    public static final Key<int[]> CONTROL_AE_COMPENSATION_RANGE =
-            new Key<int[]>("android.control.aeCompensationRange", int[].class);
+    public static final Key<android.util.Range<Integer>> CONTROL_AE_COMPENSATION_RANGE =
+            new Key<android.util.Range<Integer>>("android.control.aeCompensationRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Smallest step by which exposure compensation
@@ -205,8 +355,8 @@
      * @see CaptureRequest#CONTROL_AF_MODE
      * @see CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
      */
-    public static final Key<byte[]> CONTROL_AF_AVAILABLE_MODES =
-            new Key<byte[]>("android.control.afAvailableModes", byte[].class);
+    public static final Key<int[]> CONTROL_AF_AVAILABLE_MODES =
+            new Key<int[]>("android.control.afAvailableModes", int[].class);
 
     /**
      * <p>List containing the subset of color effects
@@ -224,8 +374,8 @@
      * @see CaptureRequest#CONTROL_EFFECT_MODE
      * @see CaptureRequest#CONTROL_MODE
      */
-    public static final Key<byte[]> CONTROL_AVAILABLE_EFFECTS =
-            new Key<byte[]>("android.control.availableEffects", byte[].class);
+    public static final Key<int[]> CONTROL_AVAILABLE_EFFECTS =
+            new Key<int[]>("android.control.availableEffects", int[].class);
 
     /**
      * <p>List containing a subset of scene modes
@@ -238,15 +388,15 @@
      *
      * @see CaptureRequest#CONTROL_SCENE_MODE
      */
-    public static final Key<byte[]> CONTROL_AVAILABLE_SCENE_MODES =
-            new Key<byte[]>("android.control.availableSceneModes", byte[].class);
+    public static final Key<int[]> CONTROL_AVAILABLE_SCENE_MODES =
+            new Key<int[]>("android.control.availableSceneModes", int[].class);
 
     /**
      * <p>List of video stabilization modes that can
      * be supported</p>
      */
-    public static final Key<byte[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES =
-            new Key<byte[]>("android.control.availableVideoStabilizationModes", byte[].class);
+    public static final Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES =
+            new Key<int[]>("android.control.availableVideoStabilizationModes", int[].class);
 
     /**
      * <p>The set of auto-white-balance modes ({@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode})
@@ -264,8 +414,8 @@
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
      * @see CaptureRequest#CONTROL_AWB_MODE
      */
-    public static final Key<byte[]> CONTROL_AWB_AVAILABLE_MODES =
-            new Key<byte[]>("android.control.awbAvailableModes", byte[].class);
+    public static final Key<int[]> CONTROL_AWB_AVAILABLE_MODES =
+            new Key<int[]>("android.control.awbAvailableModes", int[].class);
 
     /**
      * <p>List of the maximum number of regions that can be used for metering in
@@ -277,19 +427,53 @@
      * @see CaptureRequest#CONTROL_AE_REGIONS
      * @see CaptureRequest#CONTROL_AF_REGIONS
      * @see CaptureRequest#CONTROL_AWB_REGIONS
+     * @hide
      */
     public static final Key<int[]> CONTROL_MAX_REGIONS =
             new Key<int[]>("android.control.maxRegions", int[].class);
 
     /**
+     * <p>List of the maximum number of regions that can be used for metering in
+     * auto-exposure (AE);
+     * this corresponds to the the maximum number of elements in
+     * {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_REGIONS
+     */
+    public static final Key<Integer> CONTROL_MAX_REGIONS_AE =
+            new Key<Integer>("android.control.maxRegionsAe", int.class);
+
+    /**
+     * <p>List of the maximum number of regions that can be used for metering in
+     * auto-white balance (AWB);
+     * this corresponds to the the maximum number of elements in
+     * {@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
+     */
+    public static final Key<Integer> CONTROL_MAX_REGIONS_AWB =
+            new Key<Integer>("android.control.maxRegionsAwb", int.class);
+
+    /**
+     * <p>List of the maximum number of regions that can be used for metering in
+     * auto-focus (AF);
+     * this corresponds to the the maximum number of elements in
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     */
+    public static final Key<Integer> CONTROL_MAX_REGIONS_AF =
+            new Key<Integer>("android.control.maxRegionsAf", int.class);
+
+    /**
      * <p>The set of edge enhancement modes supported by this camera device.</p>
      * <p>This tag lists the valid modes for {@link CaptureRequest#EDGE_MODE android.edge.mode}.</p>
      * <p>Full-capability camera devices must always support OFF and FAST.</p>
      *
      * @see CaptureRequest#EDGE_MODE
      */
-    public static final Key<byte[]> EDGE_AVAILABLE_EDGE_MODES =
-            new Key<byte[]>("android.edge.availableEdgeModes", byte[].class);
+    public static final Key<int[]> EDGE_AVAILABLE_EDGE_MODES =
+            new Key<int[]>("android.edge.availableEdgeModes", int[].class);
 
     /**
      * <p>Whether this camera device has a
@@ -308,8 +492,8 @@
      *
      * @see CaptureRequest#HOT_PIXEL_MODE
      */
-    public static final Key<byte[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES =
-            new Key<byte[]>("android.hotPixel.availableHotPixelModes", byte[].class);
+    public static final Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES =
+            new Key<int[]>("android.hotPixel.availableHotPixelModes", int[].class);
 
     /**
      * <p>Supported resolutions for the JPEG thumbnail</p>
@@ -376,8 +560,8 @@
      *
      * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
      */
-    public static final Key<byte[]> LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION =
-            new Key<byte[]>("android.lens.info.availableOpticalStabilization", byte[].class);
+    public static final Key<int[]> LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION =
+            new Key<int[]>("android.lens.info.availableOpticalStabilization", int[].class);
 
     /**
      * <p>Optional. Hyperfocal distance for this lens.</p>
@@ -403,6 +587,7 @@
      * <p>Dimensions of lens shading map.</p>
      * <p>The map should be on the order of 30-40 rows and columns, and
      * must be smaller than 64x64.</p>
+     * @hide
      */
     public static final Key<android.util.Size> LENS_INFO_SHADING_MAP_SIZE =
             new Key<android.util.Size>("android.lens.info.shadingMapSize", android.util.Size.class);
@@ -441,8 +626,8 @@
      *
      * @see CaptureRequest#NOISE_REDUCTION_MODE
      */
-    public static final Key<byte[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES =
-            new Key<byte[]>("android.noiseReduction.availableNoiseReductionModes", byte[].class);
+    public static final Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES =
+            new Key<int[]>("android.noiseReduction.availableNoiseReductionModes", int[].class);
 
     /**
      * <p>If set to 1, the HAL will always split result
@@ -471,7 +656,7 @@
      * number is 3, and max JPEG stream number is 2, then this tuple should be <code>(1, 3, 2)</code>.</p>
      * <p>This lists the upper bound of the number of output streams supported by
      * the camera device. Using more streams simultaneously may require more hardware and
-     * CPU resources that will consume more power. The image format for a output stream can
+     * CPU resources that will consume more power. The image format for an output stream can
      * be any supported format provided by android.scaler.availableStreamConfigurations.
      * The formats defined in android.scaler.availableStreamConfigurations can be catergorized
      * into the 3 stream types as below:</p>
@@ -482,11 +667,79 @@
      * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
      * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
      * </ul>
+     * @hide
      */
     public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS =
             new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
 
     /**
+     * <p>The maximum numbers of different types of output streams
+     * that can be configured and used simultaneously by a camera device
+     * for any <code>RAW</code> formats.</p>
+     * <p>This value contains the max number of output simultaneous
+     * streams from the raw sensor.</p>
+     * <p>This lists the upper bound of the number of output streams supported by
+     * the camera device. Using more streams simultaneously may require more hardware and
+     * CPU resources that will consume more power. The image format for this kind of an output stream can
+     * be any <code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
+     * <p>In particular, a <code>RAW</code> format is typically one of:</p>
+     * <ul>
+     * <li>ImageFormat#RAW_SENSOR</li>
+     * <li>Opaque <code>RAW</code></li>
+     * </ul>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_RAW =
+            new Key<Integer>("android.request.maxNumOutputRaw", int.class);
+
+    /**
+     * <p>The maximum numbers of different types of output streams
+     * that can be configured and used simultaneously by a camera device
+     * for any processed (but not-stalling) formats.</p>
+     * <p>This value contains the max number of output simultaneous
+     * streams for any processed (but not-stalling) formats.</p>
+     * <p>This lists the upper bound of the number of output streams supported by
+     * the camera device. Using more streams simultaneously may require more hardware and
+     * CPU resources that will consume more power. The image format for this kind of an output stream can
+     * be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
+     * <p>Processed (but not-stalling) is defined as any non-RAW format without a stall duration.
+     * Typically:</p>
+     * <ul>
+     * <li>ImageFormat#YUV_420_888</li>
+     * <li>ImageFormat#NV21</li>
+     * <li>ImageFormat#YV12</li>
+     * <li>Implementation-defined formats, i.e. StreamConfiguration#isOutputSupportedFor(Class)</li>
+     * </ul>
+     * <p>For full guarantees, query StreamConfigurationMap#getOutputStallDuration with
+     * a processed format -- it will return 0 for a non-stalling stream.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_PROC =
+            new Key<Integer>("android.request.maxNumOutputProc", int.class);
+
+    /**
+     * <p>The maximum numbers of different types of output streams
+     * that can be configured and used simultaneously by a camera device
+     * for any processed (and stalling) formats.</p>
+     * <p>This value contains the max number of output simultaneous
+     * streams for any processed (but not-stalling) formats.</p>
+     * <p>This lists the upper bound of the number of output streams supported by
+     * the camera device. Using more streams simultaneously may require more hardware and
+     * CPU resources that will consume more power. The image format for this kind of an output stream can
+     * be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
+     * <p>A processed and stalling format is defined as any non-RAW format with a stallDurations &gt; 0.
+     * Typically only the <code>JPEG</code> format (ImageFormat#JPEG)</p>
+     * <p>For full guarantees, query StreamConfigurationMap#getOutputStallDuration with
+     * a processed format -- it will return a non-0 value for a stalling stream.</p>
+     *
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+     */
+    public static final Key<Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING =
+            new Key<Integer>("android.request.maxNumOutputProcStalling", int.class);
+
+    /**
      * <p>The maximum numbers of any type of input streams
      * that can be configured and used simultaneously by a camera device.</p>
      * <p>When set to 0, it means no input stream is supported.</p>
@@ -557,7 +810,6 @@
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL devices:</p>
      * <ul>
      * <li>MANUAL_SENSOR</li>
-     * <li>ZSL</li>
      * </ul>
      * <p>Other capabilities may be available on either FULL or LIMITED
      * devices, but the app. should query this field to be sure.</p>
@@ -566,7 +818,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
      * @see #REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
-     * @see #REQUEST_AVAILABLE_CAPABILITIES_GCAM
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_ZSL
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG
      */
@@ -600,7 +852,7 @@
      * value.</p>
      * <p>The following keys may return <code>null</code> unless they are enabled:</p>
      * <ul>
-     * <li>{@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} (non-null iff {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON)</li>
+     * <li>android.statistics.lensShadingMap (non-null iff {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON)</li>
      * </ul>
      * <p>(Those sometimes-null keys should nevertheless be listed here
      * if they are available.)</p>
@@ -611,7 +863,6 @@
      * <p>TODO: This should be used by #getAvailableCaptureResultKeys.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @hide
      */
@@ -1079,8 +1330,8 @@
     /**
      * <p>Range of valid sensitivities</p>
      */
-    public static final Key<int[]> SENSOR_INFO_SENSITIVITY_RANGE =
-            new Key<int[]>("android.sensor.info.sensitivityRange", int[].class);
+    public static final Key<android.util.Range<Integer>> SENSOR_INFO_SENSITIVITY_RANGE =
+            new Key<android.util.Range<Integer>>("android.sensor.info.sensitivityRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Arrangement of color filters on sensor;
@@ -1101,8 +1352,8 @@
      *
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
-    public static final Key<long[]> SENSOR_INFO_EXPOSURE_TIME_RANGE =
-            new Key<long[]>("android.sensor.info.exposureTimeRange", long[].class);
+    public static final Key<android.util.Range<Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE =
+            new Key<android.util.Range<Long>>("android.sensor.info.exposureTimeRange", new TypeReference<android.util.Range<Long>>() {{ }});
 
     /**
      * <p>Maximum possible frame duration (minimum frame
@@ -1126,8 +1377,8 @@
      * array</p>
      * <p>Needed for FOV calculation for old API</p>
      */
-    public static final Key<float[]> SENSOR_INFO_PHYSICAL_SIZE =
-            new Key<float[]>("android.sensor.info.physicalSize", float[].class);
+    public static final Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE =
+            new Key<android.util.SizeF>("android.sensor.info.physicalSize", android.util.SizeF.class);
 
     /**
      * <p>Dimensions of full pixel array, possibly
@@ -1233,8 +1484,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
      */
-    public static final Key<Rational[]> SENSOR_CALIBRATION_TRANSFORM1 =
-            new Key<Rational[]>("android.sensor.calibrationTransform1", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM1 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.calibrationTransform1", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A per-device calibration transform matrix that maps from the
@@ -1254,8 +1505,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
      */
-    public static final Key<Rational[]> SENSOR_CALIBRATION_TRANSFORM2 =
-            new Key<Rational[]>("android.sensor.calibrationTransform2", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM2 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.calibrationTransform2", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms color values from CIE XYZ color space to
@@ -1276,8 +1527,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
      */
-    public static final Key<Rational[]> SENSOR_COLOR_TRANSFORM1 =
-            new Key<Rational[]>("android.sensor.colorTransform1", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_COLOR_TRANSFORM1 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.colorTransform1", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms color values from CIE XYZ color space to
@@ -1300,8 +1551,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
      */
-    public static final Key<Rational[]> SENSOR_COLOR_TRANSFORM2 =
-            new Key<Rational[]>("android.sensor.colorTransform2", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_COLOR_TRANSFORM2 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.colorTransform2", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms white balanced camera colors from the reference
@@ -1320,8 +1571,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1
      */
-    public static final Key<Rational[]> SENSOR_FORWARD_MATRIX1 =
-            new Key<Rational[]>("android.sensor.forwardMatrix1", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX1 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.forwardMatrix1", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A matrix that transforms white balanced camera colors from the reference
@@ -1342,21 +1593,8 @@
      *
      * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2
      */
-    public static final Key<Rational[]> SENSOR_FORWARD_MATRIX2 =
-            new Key<Rational[]>("android.sensor.forwardMatrix2", Rational[].class);
-
-    /**
-     * <p>Gain factor from electrons to raw units when
-     * ISO=100</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     * <p><b>Full capability</b> -
-     * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
-     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
-     *
-     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
-     */
-    public static final Key<Rational> SENSOR_BASE_GAIN_FACTOR =
-            new Key<Rational>("android.sensor.baseGainFactor", Rational.class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX2 =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.sensor.forwardMatrix2", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>A fixed black level offset for each of the color filter arrangement
@@ -1423,8 +1661,8 @@
      * android.statistics.faceIds and
      * android.statistics.faceLandmarks outputs.</p>
      */
-    public static final Key<byte[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES =
-            new Key<byte[]>("android.statistics.info.availableFaceDetectModes", byte[].class);
+    public static final Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES =
+            new Key<int[]>("android.statistics.info.availableFaceDetectModes", int[].class);
 
     /**
      * <p>Maximum number of simultaneously detectable
@@ -1447,19 +1685,16 @@
 
     /**
      * <p>Maximum number of supported points in the
-     * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed}, or
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, or {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.</p>
+     * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>If the actual number of points provided by the application (in
-     * android.tonemap.curve*)  is less than max, the camera device will
+     * {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*)  is less than max, the camera device will
      * resample the curve to its internal representation, using linear
      * interpolation.</p>
      * <p>The output curves in the result metadata may have a different number
      * of points than the input curves, and will represent the actual
      * hardware curves used as closely as possible when linearly interpolated.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      */
     public static final Key<Integer> TONEMAP_MAX_CURVE_POINTS =
             new Key<Integer>("android.tonemap.maxCurvePoints", int.class);
@@ -1472,8 +1707,8 @@
      *
      * @see CaptureRequest#TONEMAP_MODE
      */
-    public static final Key<byte[]> TONEMAP_AVAILABLE_TONE_MAP_MODES =
-            new Key<byte[]>("android.tonemap.availableToneMapModes", byte[].class);
+    public static final Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES =
+            new Key<int[]>("android.tonemap.availableToneMapModes", int[].class);
 
     /**
      * <p>A list of camera LEDs that are available on this system.</p>
@@ -1489,15 +1724,16 @@
      * <p>A FULL device has the most support possible and will enable the
      * widest range of use cases such as:</p>
      * <ul>
-     * <li>30 FPS at maximum resolution (== sensor resolution)</li>
-     * <li>Per frame control</li>
-     * <li>Manual sensor control</li>
-     * <li>Zero Shutter Lag (ZSL)</li>
+     * <li>30fps at maximum resolution (== sensor resolution) is preferred, more than 20fps is required.</li>
+     * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
+     * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li>
+     * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_POST_PROCESSING)</li>
      * </ul>
      * <p>A LIMITED device may have some or none of the above characteristics.
      * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
      *
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      */
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 77640d1..6f5099b 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -766,14 +766,62 @@
         }
 
         /**
-         * This method is called when an image capture has completed and the
-         * result metadata is available.
+         * This method is called when an image capture makes partial forward progress; some
+         * (but not all) results from an image capture are available.
+         *
+         * <p>The result provided here will contain some subset of the fields of
+         * a full result. Multiple {@link #onCaptureProgressed} calls may happen per
+         * capture; a given result field will only be present in one partial
+         * capture at most. The final {@link #onCaptureCompleted} call will always
+         * contain all the fields (in particular, the union of all the fields of all
+         * the partial results composing the total result).</p>
+         *
+         * <p>For each request, some result data might be available earlier than others. The typical
+         * delay between each partial result (per request) is a single frame interval.
+         * For performance-oriented use-cases, applications should query the metadata they need
+         * to make forward progress from the partial results and avoid waiting for the completed
+         * result.</p>
+         *
+         * <p>Each request will generate at least {@code 1} partial results, and at most
+         * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
+         *
+         * <p>Depending on the request settings, the number of partial results per request
+         * will vary, although typically the partial count could be the same as long as the
+         * camera device subsystems enabled stay the same.</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 output metadata from the capture, including the
+         * @param partialResult The partial output metadata from the capture, which
+         * includes a subset of the {@link TotalCaptureResult} fields.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureProgressed(CameraDevice camera,
+                CaptureRequest request, CaptureResult partialResult) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has fully completed and all the
+         * result metadata is available.
+         *
+         * <p>This callback will always fire after the last {@link #onCaptureProgressed};
+         * in other words, no more partial results will be delivered once the completed result
+         * is available.</p>
+         *
+         * <p>For performance-intensive use-cases where latency is a factor, consider
+         * using {@link #onCaptureProgressed} instead.</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 total output metadata from the capture, including the
          * final capture parameters and the state of the camera system during
          * capture.
          *
@@ -783,7 +831,7 @@
          * @see #setRepeatingBurst
          */
         public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
+                CaptureRequest request, TotalCaptureResult result) {
             // default empty implementation
         }
 
@@ -796,6 +844,10 @@
          * the capture may have been pushed to their respective output
          * streams.</p>
          *
+         * <p>Some partial results may have been delivered before the capture fails;
+         * however after this callback fires, no more partial results will be delivered by
+         * {@link #onCaptureProgressed}.</p>
+         *
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera
@@ -821,24 +873,57 @@
          * when a capture sequence finishes and all {@link CaptureResult}
          * or {@link CaptureFailure} for it have been returned via this listener.
          *
+         * <p>In total, there will be at least one result/failure returned by this listener
+         * before this callback is invoked. If the capture sequence is aborted before any
+         * requests have been processed, {@link #onCaptureSequenceAborted} is invoked instead.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
          * @param camera
          *            The CameraDevice sending the callback.
          * @param sequenceId
          *            A sequence ID returned by the {@link #capture} family of functions.
-         * @param lastFrameNumber
+         * @param frameNumber
          *            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()
+         * @see #onCaptureSequenceAborted
          */
         public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, int lastFrameNumber) {
+                int sequenceId, long frameNumber) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called independently of the others in CaptureListener,
+         * when a capture sequence aborts before any {@link CaptureResult}
+         * or {@link CaptureFailure} for it have been returned via this listener.
+         *
+         * <p>Due to the asynchronous nature of the camera device, not all submitted captures
+         * are immediately processed. It is possible to clear out the pending requests
+         * by a variety of operations such as {@link CameraDevice#stopRepeating} or
+         * {@link CameraDevice#flush}. When such an event happens,
+         * {@link #onCaptureSequenceCompleted} will not be called.</p>
+         *
+         * <p>The default implementation does nothing.</p>
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param sequenceId
+         *            A sequence ID returned by the {@link #capture} family of functions.
+         *
+         * @see CaptureResult#getFrameNumber()
+         * @see CaptureFailure#getFrameNumber()
+         * @see CaptureResult#getSequenceId()
+         * @see CaptureFailure#getSequenceId()
+         * @see #onCaptureSequenceCompleted
+         */
+        public void onCaptureSequenceAborted(CameraDevice camera,
+                int sequenceId) {
             // default empty implementation
         }
     }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 03b342c..83db056 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -220,6 +220,7 @@
     private CameraDevice openCameraDeviceUserAsync(String cameraId,
             CameraDevice.StateListener listener, Handler handler)
             throws CameraAccessException {
+        CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
         CameraDevice device = null;
         try {
 
@@ -231,7 +232,8 @@
                         new android.hardware.camera2.impl.CameraDevice(
                                 cameraId,
                                 listener,
-                                handler);
+                                handler,
+                                characteristics);
 
                 BinderHolder holder = new BinderHolder();
 
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 5455189..b3e165e 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,8 +16,7 @@
 
 package android.hardware.camera2;
 
-import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -36,7 +35,7 @@
  *
  * <p>
  * All instances of CameraMetadata are immutable. The list of keys with {@link #getKeys()}
- * never changes, nor do the values returned by any key with {@link #get} throughout
+ * never changes, nor do the values returned by any key with {@code #get} throughout
  * the lifetime of the object.
  * </p>
  *
@@ -44,7 +43,10 @@
  * @see CameraManager
  * @see CameraCharacteristics
  **/
-public abstract class CameraMetadata {
+public abstract class CameraMetadata<TKey> {
+
+    private static final String TAG = "CameraMetadataAb";
+    private static final boolean VERBOSE = false;
 
     /**
      * Set a camera metadata field to a value. The field definitions can be
@@ -74,8 +76,15 @@
      *
      * @param key The metadata field to read.
      * @return The value of that key, or {@code null} if the field is not set.
+     *
+     * @hide
      */
-    public abstract <T> T get(Key<T> key);
+     protected abstract <T> T getProtected(TKey key);
+
+     /**
+      * @hide
+      */
+     protected abstract Class<TKey> getKeyClass();
 
     /**
      * Returns a list of the keys contained in this map.
@@ -83,14 +92,16 @@
      * <p>The list returned is not modifiable, so any attempts to modify it will throw
      * a {@code UnsupportedOperationException}.</p>
      *
-     * <p>All values retrieved by a key from this list with {@link #get} are guaranteed to be
+     * <p>All values retrieved by a key from this list with {@code #get} are guaranteed to be
      * non-{@code null}. Each key is only listed once in the list. The order of the keys
      * is undefined.</p>
      *
      * @return List of the keys contained in this map.
      */
-    public List<Key<?>> getKeys() {
-        return Collections.unmodifiableList(getKeysStatic(this.getClass(), this));
+    @SuppressWarnings("unchecked")
+    public List<TKey> getKeys() {
+        Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass();
+        return Collections.unmodifiableList(getKeysStatic(thisClass, getKeyClass(), this));
     }
 
     /**
@@ -101,24 +112,31 @@
      * Optionally, if {@code instance} is not null, then filter out any keys with null values.
      * </p>
      */
-    /*package*/ static ArrayList<Key<?>> getKeysStatic(Class<? extends CameraMetadata> type,
-            CameraMetadata instance) {
-        ArrayList<Key<?>> keyList = new ArrayList<Key<?>>();
+     /*package*/ @SuppressWarnings("unchecked")
+    static <TKey> ArrayList<TKey> getKeysStatic(
+             Class<?> type, Class<TKey> keyClass,
+             CameraMetadata<TKey> instance) {
+
+        if (VERBOSE) Log.v(TAG, "getKeysStatic for " + type);
+
+        ArrayList<TKey> keyList = new ArrayList<TKey>();
 
         Field[] fields = type.getDeclaredFields();
         for (Field field : fields) {
             // Filter for Keys that are public
-            if (field.getType().isAssignableFrom(Key.class) &&
+            if (field.getType().isAssignableFrom(keyClass) &&
                     (field.getModifiers() & Modifier.PUBLIC) != 0) {
-                Key<?> key;
+
+                TKey key;
                 try {
-                    key = (Key<?>) field.get(instance);
+                    key = (TKey) field.get(instance);
                 } catch (IllegalAccessException e) {
                     throw new AssertionError("Can't get IllegalAccessException", e);
                 } catch (IllegalArgumentException e) {
                     throw new AssertionError("Can't get IllegalArgumentException", e);
                 }
-                if (instance == null || instance.get(key) != null) {
+
+                if (instance == null || instance.getProtected(key) != null) {
                     keyList.add(key);
                 }
             }
@@ -127,113 +145,6 @@
         return keyList;
     }
 
-    // TODO: make final or abstract
-    public static class Key<T> {
-
-        private boolean mHasTag;
-        private int mTag;
-        private final Class<T> mType;
-        private final TypeReference<T> mTypeReference;
-        private final String mName;
-
-        /**
-         * @hide
-         */
-        public Key(String name, Class<T> type) {
-            if (name == null) {
-                throw new NullPointerException("Key needs a valid name");
-            } else if (type == null) {
-                throw new NullPointerException("Type needs to be non-null");
-            }
-            mName = name;
-            mType = type;
-            mTypeReference = TypeReference.createSpecializedTypeReference(type);
-        }
-
-        /**
-         * @hide
-         */
-        @SuppressWarnings("unchecked")
-        public Key(String name, TypeReference<T> typeReference) {
-            if (name == null) {
-                throw new NullPointerException("Key needs a valid name");
-            } else if (typeReference == null) {
-                throw new NullPointerException("TypeReference needs to be non-null");
-            }
-            mName = name;
-            mType = (Class<T>)typeReference.getRawType();
-            mTypeReference = typeReference;
-        }
-
-        public final String getName() {
-            return mName;
-        }
-
-        @Override
-        public final int hashCode() {
-            return mName.hashCode() ^ mTypeReference.hashCode();
-        }
-
-        @Override
-        public final boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-
-            if (!(o instanceof Key)) {
-                return false;
-            }
-
-            Key<?> lhs = (Key<?>)o;
-            return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
-        }
-
-        /**
-         * <p>
-         * Get the tag corresponding to this key. This enables insertion into the
-         * native metadata.
-         * </p>
-         *
-         * <p>This value is looked up the first time, and cached subsequently.</p>
-         *
-         * @return The tag numeric value corresponding to the string
-         *
-         * @hide
-         */
-        public final int getTag() {
-            if (!mHasTag) {
-                mTag = CameraMetadataNative.getTag(mName);
-                mHasTag = true;
-            }
-            return mTag;
-        }
-
-        /**
-         * Get the raw class backing the type {@code T} for this key.
-         *
-         * <p>The distinction is only important if {@code T} is a generic, e.g.
-         * {@code Range<Integer>} since the nested type will be erased.</p>
-         *
-         * @hide
-         */
-        public final Class<T> getType() {
-            // TODO: remove this; other places should use #getTypeReference() instead
-            return mType;
-        }
-
-        /**
-         * Get the type reference backing the type {@code T} for this key.
-         *
-         * <p>The distinction is only important if {@code T} is a generic, e.g.
-         * {@code Range<Integer>} since the nested type will be retained.</p>
-         *
-         * @hide
-         */
-        public final TypeReference<T> getTypeReference() {
-            return mTypeReference;
-        }
-    }
-
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * The enum values below this point are generated from metadata
      * definitions in /system/media/camera/docs. Do not modify by hand or
@@ -325,9 +236,17 @@
 
     /**
      * <p>The camera device can be manually controlled (3A algorithms such
-     * as auto exposure, and auto focus can be
-     * bypassed), this includes but is not limited to:</p>
+     * as auto exposure, and auto focus can be bypassed).
+     * The camera device supports basic manual control of the sensor image
+     * acquisition related stages. This means the following controls are
+     * guaranteed to be supported:</p>
      * <ul>
+     * <li>Manual frame duration control<ul>
+     * <li>{@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
+     * <li>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</li>
+     * </ul>
+     * </li>
      * <li>Manual exposure control<ul>
      * <li>{@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</li>
      * <li>{@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
@@ -336,7 +255,6 @@
      * <li>Manual sensitivity control<ul>
      * <li>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</li>
      * <li>{@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}</li>
-     * <li>{@link CameraCharacteristics#SENSOR_BASE_GAIN_FACTOR android.sensor.baseGainFactor}</li>
      * </ul>
      * </li>
      * <li>Manual lens control<ul>
@@ -355,11 +273,15 @@
      * <p>If any of the above 3A algorithms are enabled, then the camera
      * device will accurately report the values applied by 3A in the
      * result.</p>
+     * <p>A given camera device may also support additional manual sensor controls,
+     * but this capability only covers the above list of controls.</p>
      *
      * @see CaptureRequest#BLACK_LEVEL_LOCK
-     * @see CameraCharacteristics#SENSOR_BASE_GAIN_FACTOR
+     * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+     * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
+     * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
      * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
      * @see CaptureRequest#SENSOR_SENSITIVITY
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
@@ -367,12 +289,12 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2;
 
     /**
-     * <p>TODO: This should be @hide</p>
+     * <p>The camera device post-processing stages can be manually controlled.
+     * The camera device supports basic manual control of the image post-processing
+     * stages. This means the following controls are guaranteed to be supported:</p>
      * <ul>
      * <li>Manual tonemap control<ul>
-     * <li>{@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}</li>
-     * <li>{@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}</li>
-     * <li>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed}</li>
+     * <li>{@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}</li>
      * <li>{@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}</li>
      * <li>{@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</li>
      * </ul>
@@ -383,8 +305,8 @@
      * </ul>
      * </li>
      * <li>Lens shading map information<ul>
-     * <li>{@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}</li>
-     * <li>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}</li>
+     * <li>android.statistics.lensShadingMap</li>
+     * <li>android.lens.info.shadingMapSize</li>
      * </ul>
      * </li>
      * </ul>
@@ -392,19 +314,17 @@
      * will accurately report the values applied by AWB in the result.</p>
      * <p>The camera device will also support everything in MANUAL_SENSOR
      * except manual lens control and manual flash control.</p>
+     * <p>A given camera device may also support additional post-processing
+     * controls, but this capability only covers the above list of controls.</p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
-     * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
-    public static final int REQUEST_AVAILABLE_CAPABILITIES_GCAM = 3;
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 3;
 
     /**
      * <p>The camera device supports the Zero Shutter Lag use case.</p>
@@ -1381,8 +1301,7 @@
 
     /**
      * <p>If the flash is available and charged, fire flash
-     * for this capture based on android.flash.firingPower and
-     * android.flash.firingTime.</p>
+     * for this capture.</p>
      * @see CaptureRequest#FLASH_MODE
      */
     public static final int FLASH_MODE_SINGLE = 1;
@@ -1640,17 +1559,14 @@
 
     /**
      * <p>Use the tone mapping curve specified in
-     * the android.tonemap.curve* entries.</p>
+     * the {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}* entries.</p>
      * <p>All color enhancement and tonemapping must be disabled, except
      * for applying the tonemapping curve specified by
-     * {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed}, {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}, or
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}.</p>
+     * {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>Must not slow down frame rate relative to raw
      * sensor output.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CaptureRequest#TONEMAP_MODE
      */
     public static final int TONEMAP_MODE_CONTRAST_CURVE = 0;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 8e0f2ae..6aa24e6 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -16,7 +16,9 @@
 
 package android.hardware.camera2;
 
+import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.TypeReference;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Rational;
@@ -25,6 +27,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
 
 
@@ -58,7 +61,98 @@
  * @see CameraDevice#setRepeatingRequest
  * @see CameraDevice#createCaptureRequest
  */
-public final class CaptureRequest extends CameraMetadata implements Parcelable {
+public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
+        implements Parcelable {
+
+    /**
+     * A {@code Key} is used to do capture request field lookups with
+     * {@link CaptureResult#get} or to set fields with
+     * {@link CaptureRequest.Builder#set(Key, Object)}.
+     *
+     * <p>For example, to set the crop rectangle for the next capture:
+     * <code><pre>
+     * Rect cropRectangle = new Rect(0, 0, 640, 480);
+     * captureRequestBuilder.set(SCALER_CROP_REGION, cropRectangle);
+     * </pre></code>
+     * </p>
+     *
+     * <p>To enumerate over all possible keys for {@link CaptureResult}, see
+     * {@link CameraCharacteristics#getAvailableCaptureResultKeys}.</p>
+     *
+     * @see CaptureResult#get
+     * @see CameraCharacteristics#getAvailableCaptureResultKeys
+     */
+    public final static class Key<T> {
+        private final CameraMetadataNative.Key<T> mKey;
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
+            mKey = new CameraMetadataNative.Key<T>(name, type);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, TypeReference<T> typeReference) {
+            mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public String getName() {
+            return mKey.getName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mKey.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+        }
+
+        /**
+         * Visible for CameraMetadataNative implementation only; do not use.
+         *
+         * TODO: Make this private or remove it altogether.
+         *
+         * @hide
+         */
+        public CameraMetadataNative.Key<T> getNativeKey() {
+            return mKey;
+        }
+
+        @SuppressWarnings({ "unchecked" })
+        /*package*/ Key(CameraMetadataNative.Key<?> nativeKey) {
+            mKey = (CameraMetadataNative.Key<T>) nativeKey;
+        }
+    }
 
     private final HashSet<Surface> mSurfaceSet;
     private final CameraMetadataNative mSettings;
@@ -93,17 +187,58 @@
      * Used by the Builder to create a mutable CaptureRequest.
      */
     private CaptureRequest(CameraMetadataNative settings) {
-        mSettings = settings;
+        mSettings = CameraMetadataNative.move(settings);
         mSurfaceSet = new HashSet<Surface>();
     }
 
-    @SuppressWarnings("unchecked")
-    @Override
+    /**
+     * Get a capture request field value.
+     *
+     * <p>The field definitions can be found in {@link CaptureRequest}.</p>
+     *
+     * <p>Querying the value for the same key more than once will return a value
+     * which is equal to the previous queried value.</p>
+     *
+     * @throws IllegalArgumentException if the key was not valid
+     *
+     * @param key The result field to read.
+     * @return The value of that key, or {@code null} if the field is not set.
+     */
     public <T> T get(Key<T> key) {
         return mSettings.get(key);
     }
 
     /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getProtected(Key<?> key) {
+        return (T) mSettings.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<Key<?>> getKeyClass() {
+        Object thisClass = Key.class;
+        return (Class<Key<?>>)thisClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Key<?>> getKeys() {
+        // Force the javadoc for this function to show up on the CaptureRequest page
+        return super.getKeys();
+    }
+
+    /**
      * Retrieve the tag for this request, if any.
      *
      * <p>This tag is not used for anything by the camera device, but can be
@@ -402,30 +537,24 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<Rational[]> COLOR_CORRECTION_TRANSFORM =
-            new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.colorCorrection.transform", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>Gains applying to Bayer raw color channels for
      * white-balance.</p>
-     * <p>The 4-channel white-balance gains are defined in
-     * the order of <code>[R G_even G_odd B]</code>, where <code>G_even</code> is the gain
-     * for green pixels on even rows of the output, and <code>G_odd</code>
-     * is the gain for green pixels on the odd rows. if a HAL
-     * does not support a separate gain for even/odd green channels,
-     * it should use the <code>G_even</code> value, and write <code>G_odd</code> equal to
-     * <code>G_even</code> in the output result metadata.</p>
-     * <p>This array is either set by the camera device when the request
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or
-     * directly by the application in the request when the
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is TRANSFORM_MATRIX.</p>
-     * <p>The output should be the gains actually applied by the camera device to
-     * the current frame.</p>
+     * <p>These per-channel gains are either set by the camera device
+     * when the request {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not
+     * TRANSFORM_MATRIX, or directly by the application in the
+     * request when the {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is
+     * TRANSFORM_MATRIX.</p>
+     * <p>The gains in the result metadata are the gains actually
+     * applied by the camera device to the current frame.</p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<float[]> COLOR_CORRECTION_GAINS =
-            new Key<float[]>("android.colorCorrection.gains", float[].class);
+    public static final Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS =
+            new Key<android.hardware.camera2.params.RggbChannelVector>("android.colorCorrection.gains", android.hardware.camera2.params.RggbChannelVector.class);
 
     /**
      * <p>The desired setting for the camera device's auto-exposure
@@ -558,9 +687,6 @@
     /**
      * <p>List of areas to use for
      * metering.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
@@ -576,8 +702,8 @@
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AE_REGIONS =
-            new Key<int[]>("android.control.aeRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.aeRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Range over which fps can be adjusted to
@@ -587,8 +713,8 @@
      *
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
-    public static final Key<int[]> CONTROL_AE_TARGET_FPS_RANGE =
-            new Key<int[]>("android.control.aeTargetFpsRange", int[].class);
+    public static final Key<android.util.Range<Integer>> CONTROL_AE_TARGET_FPS_RANGE =
+            new Key<android.util.Range<Integer>>("android.control.aeTargetFpsRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Whether the camera device will trigger a precapture
@@ -633,26 +759,23 @@
     /**
      * <p>List of areas to use for focus
      * estimation.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
      * bottom-right pixel in the active pixel array. The weight
      * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific focus area
-     * needs to be used by the camera device. If the focusing region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture
-     * result metadata, the camera device will ignore the sections outside
-     * the region and output the used sections in the result metadata.</p>
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * the camera device will ignore the sections outside the region and output the
+     * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AF_REGIONS =
-            new Key<int[]>("android.control.afRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.afRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Whether the camera device will trigger autofocus for this request.</p>
@@ -719,27 +842,23 @@
     /**
      * <p>List of areas to use for illuminant
      * estimation.</p>
-     * <p>Only used in AUTO mode.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
      * bottom-right pixel in the active pixel array. The weight
      * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific auto-white balance (AWB) area
-     * needs to be used by the camera device. If the AWB region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
      * the camera device will ignore the sections outside the region and output the
      * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AWB_REGIONS =
-            new Key<int[]>("android.control.awbRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.awbRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Information to the camera device 3A (auto-exposure,
@@ -933,8 +1052,15 @@
             new Key<Integer>("android.hotPixel.mode", int.class);
 
     /**
+     * <p>A location object to use when generating image GPS metadata.</p>
+     */
+    public static final Key<android.location.Location> JPEG_GPS_LOCATION =
+            new Key<android.location.Location>("android.jpeg.gpsLocation", android.location.Location.class);
+
+    /**
      * <p>GPS coordinates to include in output JPEG
      * EXIF</p>
+     * @hide
      */
     public static final Key<double[]> JPEG_GPS_COORDINATES =
             new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
@@ -942,6 +1068,7 @@
     /**
      * <p>32 characters describing GPS algorithm to
      * include in EXIF</p>
+     * @hide
      */
     public static final Key<String> JPEG_GPS_PROCESSING_METHOD =
             new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
@@ -949,6 +1076,7 @@
     /**
      * <p>Time GPS fix was made to include in
      * EXIF</p>
+     * @hide
      */
     public static final Key<Long> JPEG_GPS_TIMESTAMP =
             new Key<Long>("android.jpeg.gpsTimestamp", long.class);
@@ -981,6 +1109,12 @@
      * but the captured JPEG will still be a valid image.</p>
      * <p>When a jpeg image capture is issued, the thumbnail size selected should have
      * the same aspect ratio as the jpeg image.</p>
+     * <p>If the thumbnail image aspect ratio differs from the JPEG primary image aspect
+     * ratio, the camera device creates the thumbnail by cropping it from the primary image.
+     * For example, if the primary image has 4:3 aspect ratio, the thumbnail image has
+     * 16:9 aspect ratio, the primary image will be cropped vertically (letterbox) to
+     * generate the thumbnail image. The thumbnail image will always have a smaller Field
+     * Of View (FOV) than the primary image when aspect ratios differ.</p>
      */
     public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE =
             new Key<android.util.Size>("android.jpeg.thumbnailSize", android.util.Size.class);
@@ -1297,8 +1431,8 @@
      * <p>When set to OFF mode, no lens shading correction will be applied by the
      * camera device, and an identity lens shading map data will be provided
      * if <code>{@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON</code>. For example, for lens
-     * shading map with size specified as <code>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize} = [ 4, 3 ]</code>,
-     * the output {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} for this case will be an identity map
+     * shading map with size specified as <code>android.lens.info.shadingMapSize = [ 4, 3 ]</code>,
+     * the output android.statistics.lensShadingMap for this case will be an identity map
      * shown below:</p>
      * <pre><code>[ 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
      * 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
@@ -1310,8 +1444,8 @@
      * <p>When set to other modes, lens shading correction will be applied by the
      * 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}; the returned shading map data will be the one
+     * lens shading map data in android.statistics.lensShadingMap, with size specified
+     * by 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
@@ -1321,8 +1455,6 @@
      *
      * @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
      * @see #SHADING_MODE_OFF
      * @see #SHADING_MODE_FAST
@@ -1363,10 +1495,8 @@
      * <p>Whether the camera device will output the lens
      * shading map in output result metadata.</p>
      * <p>When set to ON,
-     * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} must be provided in
+     * android.statistics.lensShadingMap must be provided in
      * the output result metadata.</p>
-     *
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
      */
@@ -1377,10 +1507,10 @@
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_BLUE =
             new Key<float[]>("android.tonemap.curveBlue", float[].class);
@@ -1389,10 +1519,10 @@
      * <p>Tonemapping / contrast / gamma curve for the green
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_GREEN =
             new Key<float[]>("android.tonemap.curveGreen", float[].class);
@@ -1402,7 +1532,7 @@
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
      * <p>Each channel's curve is defined by an array of control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} =
+     * <pre><code>android.tonemap.curveRed =
      * [ P0in, P0out, P1in, P1out, P2in, P2out, P3in, P3out, ..., PNin, PNout ]
      * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
      * <p>These are sorted in order of increasing <code>Pin</code>; it is always
@@ -1418,15 +1548,15 @@
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
      * <p>Linear mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 0, 1.0, 1.0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
      * </code></pre>
      * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
      * <p>Invert mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 1.0, 1.0, 0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
      * </code></pre>
      * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
      * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
      * 0.2667, 0.5484, 0.3333, 0.6069, 0.4000, 0.6594, 0.4667, 0.7072,
      * 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
@@ -1434,7 +1564,7 @@
      * </code></pre>
      * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
      * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
      * 0.2667, 0.5532, 0.3333, 0.6125, 0.4000, 0.6652, 0.4667, 0.7130,
      * 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
@@ -1442,14 +1572,67 @@
      * </code></pre>
      * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_RED =
             new Key<float[]>("android.tonemap.curveRed", float[].class);
 
     /**
+     * <p>Tonemapping / contrast / gamma curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}
+     * is CONTRAST_CURVE.</p>
+     * <p>The tonemapCurve consist of three curves for each of red, green, and blue
+     * channels respectively. The following example uses the red channel as an
+     * example. The same logic applies to green and blue channel.
+     * Each channel's curve is defined by an array of control points:</p>
+     * <pre><code>curveRed =
+     * [ P0(in, out), P1(in, out), P2(in, out), P3(in, out), ..., PN(in, out) ]
+     * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
+     * <p>These are sorted in order of increasing <code>Pin</code>; it is always
+     * guaranteed that input values 0.0 and 1.0 are included in the list to
+     * define a complete mapping. For input values between control points,
+     * the camera device must linearly interpolate between the control
+     * points.</p>
+     * <p>Each curve can have an independent number of points, and the number
+     * of points can be less than max (that is, the request doesn't have to
+     * always provide a curve with number of points equivalent to
+     * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>A few examples, and their corresponding graphical mappings; these
+     * only specify the red channel and the precision is limited to 4
+     * digits, for conciseness.</p>
+     * <p>Linear mapping:</p>
+     * <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
+     * </code></pre>
+     * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+     * <p>Invert mapping:</p>
+     * <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
+     * </code></pre>
+     * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+     * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
+     * (0.2667, 0.5484), (0.3333, 0.6069), (0.4000, 0.6594), (0.4667, 0.7072),
+     * (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
+     * (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+     * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
+     * (0.2667, 0.5532), (0.3333, 0.6125), (0.4000, 0.6652), (0.4667, 0.7130),
+     * (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
+     * (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+     *
+     * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    public static final Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE =
+            new Key<android.hardware.camera2.params.TonemapCurve>("android.tonemap.curve", android.hardware.camera2.params.TonemapCurve.class);
+
+    /**
      * <p>High-level global contrast/gamma/tonemapping control.</p>
      * <p>When switching to an application-defined contrast curve by setting
      * {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} to CONTRAST_CURVE, the curve is defined
@@ -1465,8 +1648,7 @@
      * <p>This must be set to a valid mode in
      * {@link CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES android.tonemap.availableToneMapModes}.</p>
      * <p>When using either FAST or HIGH_QUALITY, the camera device will
-     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed},
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
+     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.
      * 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 CONTRAST_CURVE with the camera device's
@@ -1474,9 +1656,7 @@
      * roughly the same.</p>
      *
      * @see CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CaptureRequest#TONEMAP_MODE
      * @see #TONEMAP_MODE_CONTRAST_CURVE
      * @see #TONEMAP_MODE_FAST
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e0ddd57..42020eb 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -17,13 +17,16 @@
 package android.hardware.camera2;
 
 import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.params.Face;
+import android.hardware.camera2.utils.TypeReference;
+import android.util.Log;
 import android.util.Rational;
 
+import java.util.List;
+
 /**
- * <p>The results of a single image capture from the image sensor.</p>
+ * <p>The subset of the results of a single image capture from the image sensor.</p>
  *
- * <p>Contains the final configuration for the capture hardware (sensor, lens,
+ * <p>Contains a subset of the final configuration for the capture hardware (sensor, lens,
  * flash), the processing pipeline, the control algorithms, and the output
  * buffers.</p>
  *
@@ -33,10 +36,106 @@
  * capture. The result also includes additional metadata about the state of the
  * camera device during the capture.</p>
  *
- * <p>{@link CameraCharacteristics} objects are immutable.</p>
+ * <p>Not all properties returned by {@link CameraCharacteristics#getAvailableCaptureResultKeys()}
+ * are necessarily available. Some results are {@link CaptureResult partial} and will
+ * not have every key set. Only {@link TotalCaptureResult total} results are guaranteed to have
+ * every key available that was enabled by the request.</p>
+ *
+ * <p>{@link CaptureResult} objects are immutable.</p>
  *
  */
-public final class CaptureResult extends CameraMetadata {
+public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
+
+    private static final String TAG = "CaptureResult";
+    private static final boolean VERBOSE = false;
+
+    /**
+     * A {@code Key} is used to do capture result field lookups with
+     * {@link CaptureResult#get}.
+     *
+     * <p>For example, to get the timestamp corresponding to the exposure of the first row:
+     * <code><pre>
+     * long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP);
+     * </pre></code>
+     * </p>
+     *
+     * <p>To enumerate over all possible keys for {@link CaptureResult}, see
+     * {@link CameraCharacteristics#getAvailableCaptureResultKeys}.</p>
+     *
+     * @see CaptureResult#get
+     * @see CameraCharacteristics#getAvailableCaptureResultKeys
+     */
+    public final static class Key<T> {
+        private final CameraMetadataNative.Key<T> mKey;
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
+            mKey = new CameraMetadataNative.Key<T>(name, type);
+        }
+
+        /**
+         * Visible for testing and vendor extensions only.
+         *
+         * @hide
+         */
+        public Key(String name, TypeReference<T> typeReference) {
+            mKey = new CameraMetadataNative.Key<T>(name, typeReference);
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public String getName() {
+            return mKey.getName();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mKey.hashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof Key && ((Key<T>)o).mKey.equals(mKey);
+        }
+
+        /**
+         * Visible for CameraMetadataNative implementation only; do not use.
+         *
+         * TODO: Make this private or remove it altogether.
+         *
+         * @hide
+         */
+        public CameraMetadataNative.Key<T> getNativeKey() {
+            return mKey;
+        }
+
+        @SuppressWarnings({ "unchecked" })
+        /*package*/ Key(CameraMetadataNative.Key<?> nativeKey) {
+            mKey = (CameraMetadataNative.Key<T>) nativeKey;
+        }
+    }
 
     private final CameraMetadataNative mResults;
     private final CaptureRequest mRequest;
@@ -55,7 +154,10 @@
             throw new IllegalArgumentException("parent was null");
         }
 
-        mResults = results;
+        mResults = CameraMetadataNative.move(results);
+        if (mResults.isEmpty()) {
+            throw new AssertionError("Results must not be empty");
+        }
         mRequest = parent;
         mSequenceId = sequenceId;
     }
@@ -68,26 +170,103 @@
         return new CameraMetadataNative(mResults);
     }
 
-    @Override
+    /**
+     * Creates a request-less result.
+     *
+     * <p><strong>For testing only.</strong></p>
+     * @hide
+     */
+    public CaptureResult(CameraMetadataNative results, int sequenceId) {
+        if (results == null) {
+            throw new IllegalArgumentException("results was null");
+        }
+
+        mResults = CameraMetadataNative.move(results);
+        if (mResults.isEmpty()) {
+            throw new AssertionError("Results must not be empty");
+        }
+
+        mRequest = null;
+        mSequenceId = sequenceId;
+    }
+
+    /**
+     * Get a capture result field value.
+     *
+     * <p>The field definitions can be found in {@link CaptureResult}.</p>
+     *
+     * <p>Querying the value for the same key more than once will return a value
+     * which is equal to the previous queried value.</p>
+     *
+     * @throws IllegalArgumentException if the key was not valid
+     *
+     * @param key The result field to read.
+     * @return The value of that key, or {@code null} if the field is not set.
+     */
     public <T> T get(Key<T> key) {
-        return mResults.get(key);
+        T value = mResults.get(key);
+        if (VERBOSE) Log.v(TAG, "#get for Key = " + key.getName() + ", returned value = " + value);
+        return value;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected <T> T getProtected(Key<?> key) {
+        return (T) mResults.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Class<Key<?>> getKeyClass() {
+        Object thisClass = Key.class;
+        return (Class<Key<?>>)thisClass;
+    }
+
+    /**
+     * Dumps the native metadata contents to logcat.
+     *
+     * <p>Visibility for testing/debugging only. The results will not
+     * include any synthesized keys, as they are invisible to the native layer.</p>
+     *
+     * @hide
+     */
+    public void dumpToLog() {
+        mResults.dumpToLog();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<Key<?>> getKeys() {
+        // Force the javadoc for this function to show up on the CaptureResult page
+        return super.getKeys();
     }
 
     /**
      * Get the request associated with this result.
      *
-     * <p>Whenever a request is successfully captured, with
-     * {@link CameraDevice.CaptureListener#onCaptureCompleted},
-     * the {@code result}'s {@code getRequest()} will return that {@code request}.
+     * <p>Whenever a request has been fully or partially captured, with
+     * {@link CameraDevice.CaptureListener#onCaptureCompleted} or
+     * {@link CameraDevice.CaptureListener#onCaptureProgressed}, the {@code result}'s
+     * {@code getRequest()} will return that {@code request}.
      * </p>
      *
-     * <p>In particular,
+     * <p>For example,
      * <code><pre>cameraDevice.capture(someRequest, new CaptureListener() {
      *     {@literal @}Override
      *     void onCaptureCompleted(CaptureRequest myRequest, CaptureResult myResult) {
      *         assert(myResult.getRequest.equals(myRequest) == true);
      *     }
-     * };
+     * }, null);
      * </code></pre>
      * </p>
      *
@@ -110,6 +289,7 @@
      * @return int frame number
      */
     public int getFrameNumber() {
+        // TODO: @hide REQUEST_FRAME_COUNT
         return get(REQUEST_FRAME_COUNT);
     }
 
@@ -123,6 +303,7 @@
      * @return int The ID for the sequence of requests that this capture result is a part of
      *
      * @see CameraDevice.CaptureListener#onCaptureSequenceCompleted
+     * @see CameraDevice.CaptureListener#onCaptureSequenceAborted
      */
     public int getSequenceId() {
         return mSequenceId;
@@ -202,30 +383,24 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<Rational[]> COLOR_CORRECTION_TRANSFORM =
-            new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+    public static final Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM =
+            new Key<android.hardware.camera2.params.ColorSpaceTransform>("android.colorCorrection.transform", android.hardware.camera2.params.ColorSpaceTransform.class);
 
     /**
      * <p>Gains applying to Bayer raw color channels for
      * white-balance.</p>
-     * <p>The 4-channel white-balance gains are defined in
-     * the order of <code>[R G_even G_odd B]</code>, where <code>G_even</code> is the gain
-     * for green pixels on even rows of the output, and <code>G_odd</code>
-     * is the gain for green pixels on the odd rows. if a HAL
-     * does not support a separate gain for even/odd green channels,
-     * it should use the <code>G_even</code> value, and write <code>G_odd</code> equal to
-     * <code>G_even</code> in the output result metadata.</p>
-     * <p>This array is either set by the camera device when the request
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or
-     * directly by the application in the request when the
-     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is TRANSFORM_MATRIX.</p>
-     * <p>The output should be the gains actually applied by the camera device to
-     * the current frame.</p>
+     * <p>These per-channel gains are either set by the camera device
+     * when the request {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not
+     * TRANSFORM_MATRIX, or directly by the application in the
+     * request when the {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is
+     * TRANSFORM_MATRIX.</p>
+     * <p>The gains in the result metadata are the gains actually
+     * applied by the camera device to the current frame.</p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      */
-    public static final Key<float[]> COLOR_CORRECTION_GAINS =
-            new Key<float[]>("android.colorCorrection.gains", float[].class);
+    public static final Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS =
+            new Key<android.hardware.camera2.params.RggbChannelVector>("android.colorCorrection.gains", android.hardware.camera2.params.RggbChannelVector.class);
 
     /**
      * <p>The desired setting for the camera device's auto-exposure
@@ -358,9 +533,6 @@
     /**
      * <p>List of areas to use for
      * metering.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
@@ -376,8 +548,8 @@
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AE_REGIONS =
-            new Key<int[]>("android.control.aeRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.aeRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Range over which fps can be adjusted to
@@ -387,8 +559,8 @@
      *
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      */
-    public static final Key<int[]> CONTROL_AE_TARGET_FPS_RANGE =
-            new Key<int[]>("android.control.aeTargetFpsRange", int[].class);
+    public static final Key<android.util.Range<Integer>> CONTROL_AE_TARGET_FPS_RANGE =
+            new Key<android.util.Range<Integer>>("android.control.aeTargetFpsRange", new TypeReference<android.util.Range<Integer>>() {{ }});
 
     /**
      * <p>Whether the camera device will trigger a precapture
@@ -631,26 +803,23 @@
     /**
      * <p>List of areas to use for focus
      * estimation.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
      * bottom-right pixel in the active pixel array. The weight
      * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific focus area
-     * needs to be used by the camera device. If the focusing region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture
-     * result metadata, the camera device will ignore the sections outside
-     * the region and output the used sections in the result metadata.</p>
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * the camera device will ignore the sections outside the region and output the
+     * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AF_REGIONS =
-            new Key<int[]>("android.control.afRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.afRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Whether the camera device will trigger autofocus for this request.</p>
@@ -1114,27 +1283,23 @@
     /**
      * <p>List of areas to use for illuminant
      * estimation.</p>
-     * <p>Only used in AUTO mode.</p>
-     * <p>Each area is a rectangle plus weight: xmin, ymin,
-     * xmax, ymax, weight. The rectangle is defined to be inclusive of the
-     * specified coordinates.</p>
      * <p>The coordinate system is based on the active pixel array,
      * with (0,0) being the top-left pixel in the active pixel array, and
      * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
      * bottom-right pixel in the active pixel array. The weight
      * should be nonnegative.</p>
-     * <p>If all regions have 0 weight, then no specific auto-white balance (AWB) area
-     * needs to be used by the camera device. If the AWB region is
-     * outside the the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
+     * <p>If all regions have 0 weight, then no specific metering area
+     * needs to be used by the camera device. If the metering region is
+     * outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in capture result metadata,
      * the camera device will ignore the sections outside the region and output the
      * used sections in the result metadata.</p>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
-    public static final Key<int[]> CONTROL_AWB_REGIONS =
-            new Key<int[]>("android.control.awbRegions", int[].class);
+    public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
+            new Key<android.hardware.camera2.params.MeteringRectangle[]>("android.control.awbRegions", android.hardware.camera2.params.MeteringRectangle[].class);
 
     /**
      * <p>Information to the camera device 3A (auto-exposure,
@@ -1475,8 +1640,15 @@
             new Key<Integer>("android.hotPixel.mode", int.class);
 
     /**
+     * <p>A location object to use when generating image GPS metadata.</p>
+     */
+    public static final Key<android.location.Location> JPEG_GPS_LOCATION =
+            new Key<android.location.Location>("android.jpeg.gpsLocation", android.location.Location.class);
+
+    /**
      * <p>GPS coordinates to include in output JPEG
      * EXIF</p>
+     * @hide
      */
     public static final Key<double[]> JPEG_GPS_COORDINATES =
             new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
@@ -1484,6 +1656,7 @@
     /**
      * <p>32 characters describing GPS algorithm to
      * include in EXIF</p>
+     * @hide
      */
     public static final Key<String> JPEG_GPS_PROCESSING_METHOD =
             new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
@@ -1491,6 +1664,7 @@
     /**
      * <p>Time GPS fix was made to include in
      * EXIF</p>
+     * @hide
      */
     public static final Key<Long> JPEG_GPS_TIMESTAMP =
             new Key<Long>("android.jpeg.gpsTimestamp", long.class);
@@ -1523,6 +1697,12 @@
      * but the captured JPEG will still be a valid image.</p>
      * <p>When a jpeg image capture is issued, the thumbnail size selected should have
      * the same aspect ratio as the jpeg image.</p>
+     * <p>If the thumbnail image aspect ratio differs from the JPEG primary image aspect
+     * ratio, the camera device creates the thumbnail by cropping it from the primary image.
+     * For example, if the primary image has 4:3 aspect ratio, the thumbnail image has
+     * 16:9 aspect ratio, the primary image will be cropped vertically (letterbox) to
+     * generate the thumbnail image. The thumbnail image will always have a smaller Field
+     * Of View (FOV) than the primary image when aspect ratios differ.</p>
      */
     public static final Key<android.util.Size> JPEG_THUMBNAIL_SIZE =
             new Key<android.util.Size>("android.jpeg.thumbnailSize", android.util.Size.class);
@@ -1612,8 +1792,8 @@
      * <p>If variable focus not supported, can still report
      * fixed depth of field range</p>
      */
-    public static final Key<float[]> LENS_FOCUS_RANGE =
-            new Key<float[]>("android.lens.focusRange", float[].class);
+    public static final Key<android.util.Range<Float>> LENS_FOCUS_RANGE =
+            new Key<android.util.Range<Float>>("android.lens.focusRange", new TypeReference<android.util.Range<Float>>() {{ }});
 
     /**
      * <p>Sets whether the camera device uses optical image stabilization (OIS)
@@ -1888,21 +2068,6 @@
             new Key<Long>("android.sensor.timestamp", long.class);
 
     /**
-     * <p>The temperature of the sensor, sampled at the time
-     * exposure began for this frame.</p>
-     * <p>The thermal diode being queried should be inside the sensor PCB, or
-     * somewhere close to it.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     * <p><b>Full capability</b> -
-     * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
-     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
-     *
-     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
-     */
-    public static final Key<Float> SENSOR_TEMPERATURE =
-            new Key<Float>("android.sensor.temperature", float.class);
-
-    /**
      * <p>The estimated camera neutral color in the native sensor colorspace at
      * the time of capture.</p>
      * <p>This value gives the neutral color point encoded as an RGB value in the
@@ -1995,8 +2160,8 @@
      * <p>When set to OFF mode, no lens shading correction will be applied by the
      * camera device, and an identity lens shading map data will be provided
      * if <code>{@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} == ON</code>. For example, for lens
-     * shading map with size specified as <code>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize} = [ 4, 3 ]</code>,
-     * the output {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} for this case will be an identity map
+     * shading map with size specified as <code>android.lens.info.shadingMapSize = [ 4, 3 ]</code>,
+     * the output android.statistics.lensShadingMap for this case will be an identity map
      * shown below:</p>
      * <pre><code>[ 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
      * 1.0, 1.0, 1.0, 1.0,  1.0, 1.0, 1.0, 1.0,
@@ -2008,8 +2173,8 @@
      * <p>When set to other modes, lens shading correction will be applied by the
      * 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}; the returned shading map data will be the one
+     * lens shading map data in android.statistics.lensShadingMap, with size specified
+     * by 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
@@ -2019,8 +2184,6 @@
      *
      * @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
      * @see #SHADING_MODE_OFF
      * @see #SHADING_MODE_FAST
@@ -2110,13 +2273,12 @@
      * The map is assumed to be bilinearly interpolated between the sample points.</p>
      * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
      * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
-     * The shading map is stored in a fully interleaved format, and its size
-     * is provided in the camera static metadata by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+     * The shading map is stored in a fully interleaved format.</p>
      * <p>The shading map should have on the order of 30-40 rows and columns,
      * and must be smaller than 64x64.</p>
      * <p>As an example, given a very small map defined as:</p>
-     * <pre><code>{@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize} = [ 4, 3 ]
-     * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} =
+     * <pre><code>width,height = [ 4, 3 ]
+     * values =
      * [ 1.3, 1.2, 1.15, 1.2,  1.2, 1.2, 1.15, 1.2,
      * 1.1, 1.2, 1.2, 1.2,  1.3, 1.2, 1.3, 1.3,
      * 1.2, 1.2, 1.25, 1.1,  1.1, 1.1, 1.1, 1.0,
@@ -2135,8 +2297,54 @@
      * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
-     * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
+     */
+    public static final Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP =
+            new Key<android.hardware.camera2.params.LensShadingMap>("android.statistics.lensShadingCorrectionMap", android.hardware.camera2.params.LensShadingMap.class);
+
+    /**
+     * <p>The shading map is a low-resolution floating-point map
+     * that lists the coefficients used to correct for vignetting, for each
+     * Bayer color channel.</p>
+     * <p>The least shaded section of the image should have a gain factor
+     * of 1; all other sections should have gains above 1.</p>
+     * <p>When {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} = TRANSFORM_MATRIX, the map
+     * must take into account the colorCorrection settings.</p>
+     * <p>The shading map is for the entire active pixel array, and is not
+     * affected by the crop region specified in the request. Each shading map
+     * entry is the value of the shading compensation map over a specific
+     * pixel on the sensor.  Specifically, with a (N x M) resolution shading
+     * map, and an active pixel array size (W x H), shading map entry
+     * (x,y) ϵ (0 ... N-1, 0 ... M-1) is the value of the shading map at
+     * pixel ( ((W-1)/(N-1)) * x, ((H-1)/(M-1)) * y) for the four color channels.
+     * The map is assumed to be bilinearly interpolated between the sample points.</p>
+     * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
+     * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
+     * The shading map is stored in a fully interleaved format, and its size
+     * is provided in the camera static metadata by android.lens.info.shadingMapSize.</p>
+     * <p>The shading map should have on the order of 30-40 rows and columns,
+     * and must be smaller than 64x64.</p>
+     * <p>As an example, given a very small map defined as:</p>
+     * <pre><code>android.lens.info.shadingMapSize = [ 4, 3 ]
+     * android.statistics.lensShadingMap =
+     * [ 1.3, 1.2, 1.15, 1.2,  1.2, 1.2, 1.15, 1.2,
+     * 1.1, 1.2, 1.2, 1.2,  1.3, 1.2, 1.3, 1.3,
+     * 1.2, 1.2, 1.25, 1.1,  1.1, 1.1, 1.1, 1.0,
+     * 1.0, 1.0, 1.0, 1.0,  1.2, 1.3, 1.25, 1.2,
+     * 1.3, 1.2, 1.2, 1.3,   1.2, 1.15, 1.1, 1.2,
+     * 1.2, 1.1, 1.0, 1.2,  1.3, 1.15, 1.2, 1.3 ]
+     * </code></pre>
+     * <p>The low-resolution scaling map images for each channel are
+     * (displayed using nearest-neighbor interpolation):</p>
+     * <p><img alt="Red lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
+     * <img alt="Green (even rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
+     * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
+     * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
+     * <p>As a visualization only, inverting the full-color map to recover an
+     * image of a gray wall (using bicubic interpolation for visual quality) as captured by the sensor gives:</p>
+     * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     * @hide
      */
     public static final Key<float[]> STATISTICS_LENS_SHADING_MAP =
             new Key<float[]>("android.statistics.lensShadingMap", float[].class);
@@ -2236,17 +2444,15 @@
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      */
-    public static final Key<int[]> STATISTICS_HOT_PIXEL_MAP =
-            new Key<int[]>("android.statistics.hotPixelMap", int[].class);
+    public static final Key<android.graphics.Point[]> STATISTICS_HOT_PIXEL_MAP =
+            new Key<android.graphics.Point[]>("android.statistics.hotPixelMap", android.graphics.Point[].class);
 
     /**
      * <p>Whether the camera device will output the lens
      * shading map in output result metadata.</p>
      * <p>When set to ON,
-     * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} must be provided in
+     * android.statistics.lensShadingMap must be provided in
      * the output result metadata.</p>
-     *
-     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
      */
@@ -2257,10 +2463,10 @@
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_BLUE =
             new Key<float[]>("android.tonemap.curveBlue", float[].class);
@@ -2269,10 +2475,10 @@
      * <p>Tonemapping / contrast / gamma curve for the green
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
-     * <p>See {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} for more details.</p>
+     * <p>See android.tonemap.curveRed for more details.</p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_GREEN =
             new Key<float[]>("android.tonemap.curveGreen", float[].class);
@@ -2282,7 +2488,7 @@
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
      * <p>Each channel's curve is defined by an array of control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} =
+     * <pre><code>android.tonemap.curveRed =
      * [ P0in, P0out, P1in, P1out, P2in, P2out, P3in, P3out, ..., PNin, PNout ]
      * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
      * <p>These are sorted in order of increasing <code>Pin</code>; it is always
@@ -2298,15 +2504,15 @@
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
      * <p>Linear mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 0, 1.0, 1.0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
      * </code></pre>
      * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
      * <p>Invert mapping:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [ 0, 1.0, 1.0, 0 ]
+     * <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
      * </code></pre>
      * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
      * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
      * 0.2667, 0.5484, 0.3333, 0.6069, 0.4000, 0.6594, 0.4667, 0.7072,
      * 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
@@ -2314,7 +2520,7 @@
      * </code></pre>
      * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
      * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
-     * <pre><code>{@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed} = [
+     * <pre><code>android.tonemap.curveRed = [
      * 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
      * 0.2667, 0.5532, 0.3333, 0.6125, 0.4000, 0.6652, 0.4667, 0.7130,
      * 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
@@ -2322,14 +2528,67 @@
      * </code></pre>
      * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
      *
-     * @see CaptureRequest#TONEMAP_CURVE_RED
      * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
      * @see CaptureRequest#TONEMAP_MODE
+     * @hide
      */
     public static final Key<float[]> TONEMAP_CURVE_RED =
             new Key<float[]>("android.tonemap.curveRed", float[].class);
 
     /**
+     * <p>Tonemapping / contrast / gamma curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}
+     * is CONTRAST_CURVE.</p>
+     * <p>The tonemapCurve consist of three curves for each of red, green, and blue
+     * channels respectively. The following example uses the red channel as an
+     * example. The same logic applies to green and blue channel.
+     * Each channel's curve is defined by an array of control points:</p>
+     * <pre><code>curveRed =
+     * [ P0(in, out), P1(in, out), P2(in, out), P3(in, out), ..., PN(in, out) ]
+     * 2 &lt;= N &lt;= {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</code></pre>
+     * <p>These are sorted in order of increasing <code>Pin</code>; it is always
+     * guaranteed that input values 0.0 and 1.0 are included in the list to
+     * define a complete mapping. For input values between control points,
+     * the camera device must linearly interpolate between the control
+     * points.</p>
+     * <p>Each curve can have an independent number of points, and the number
+     * of points can be less than max (that is, the request doesn't have to
+     * always provide a curve with number of points equivalent to
+     * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>A few examples, and their corresponding graphical mappings; these
+     * only specify the red channel and the precision is limited to 4
+     * digits, for conciseness.</p>
+     * <p>Linear mapping:</p>
+     * <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
+     * </code></pre>
+     * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+     * <p>Invert mapping:</p>
+     * <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
+     * </code></pre>
+     * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+     * <p>Gamma 1/2.2 mapping, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
+     * (0.2667, 0.5484), (0.3333, 0.6069), (0.4000, 0.6594), (0.4667, 0.7072),
+     * (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
+     * (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+     * <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
+     * <pre><code>curveRed = [
+     * (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
+     * (0.2667, 0.5532), (0.3333, 0.6125), (0.4000, 0.6652), (0.4667, 0.7130),
+     * (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
+     * (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
+     * </code></pre>
+     * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+     *
+     * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    public static final Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE =
+            new Key<android.hardware.camera2.params.TonemapCurve>("android.tonemap.curve", android.hardware.camera2.params.TonemapCurve.class);
+
+    /**
      * <p>High-level global contrast/gamma/tonemapping control.</p>
      * <p>When switching to an application-defined contrast curve by setting
      * {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} to CONTRAST_CURVE, the curve is defined
@@ -2345,8 +2604,7 @@
      * <p>This must be set to a valid mode in
      * {@link CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES android.tonemap.availableToneMapModes}.</p>
      * <p>When using either FAST or HIGH_QUALITY, the camera device will
-     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE_RED android.tonemap.curveRed},
-     * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
+     * emit its own tonemap curve in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.
      * 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 CONTRAST_CURVE with the camera device's
@@ -2354,9 +2612,7 @@
      * roughly the same.</p>
      *
      * @see CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES
-     * @see CaptureRequest#TONEMAP_CURVE_BLUE
-     * @see CaptureRequest#TONEMAP_CURVE_GREEN
-     * @see CaptureRequest#TONEMAP_CURVE_RED
+     * @see CaptureRequest#TONEMAP_CURVE
      * @see CaptureRequest#TONEMAP_MODE
      * @see #TONEMAP_MODE_CONTRAST_CURVE
      * @see #TONEMAP_MODE_FAST
diff --git a/media/java/android/media/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
similarity index 98%
rename from media/java/android/media/DngCreator.java
rename to core/java/android/hardware/camera2/DngCreator.java
index 76c6d46..54568ed 100644
--- a/media/java/android/media/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.hardware.camera2;
 
 import android.graphics.Bitmap;
 import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.location.Location;
+import android.media.ExifInterface;
+import android.media.Image;
 import android.util.Size;
 
 import java.io.IOException;
@@ -266,6 +266,7 @@
      * </p>
      *
      * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+     * @param size the {@link Size} of the image to write, in pixels.
      * @param pixels an {@link java.nio.ByteBuffer} of pixel data to write.
      * @param offset the offset of the raw image in bytes.  This indicates how many bytes will
      *               be skipped in the input before any pixel data is read.
@@ -362,7 +363,6 @@
                                                             long offset) throws IOException;
 
     static {
-        System.loadLibrary("media_jni");
         nativeClassInit();
     }
 }
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
new file mode 100644
index 0000000..2647959
--- /dev/null
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -0,0 +1,84 @@
+/*
+ * 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.hardware.camera2.impl.CameraMetadataNative;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>The total assembled results of a single image capture from the image sensor.</p>
+ *
+ * <p>Contains the final configuration for the capture hardware (sensor, lens,
+ * flash), the processing pipeline, the control algorithms, and the output
+ * buffers.</p>
+ *
+ * <p>A {@code TotalCaptureResult} is produced by a {@link CameraDevice} after processing a
+ * {@link CaptureRequest}. All properties listed for capture requests can also
+ * be queried on the capture result, to determine the final values used for
+ * capture. The result also includes additional metadata about the state of the
+ * camera device during the capture.</p>
+ *
+ * <p>All properties returned by {@link CameraCharacteristics#getAvailableCaptureResultKeys()}
+ * are available (that is {@link CaptureResult#get} will return non-{@code null}, if and only if
+ * that key that was enabled by the request. A few keys such as
+ * {@link CaptureResult#STATISTICS_FACES} are disabled by default unless enabled with a switch (such
+ * as {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE}). Refer to each key documentation on
+ * a case-by-case basis.</p>
+ *
+ * <p>{@link TotalCaptureResult} objects are immutable.</p>
+ *
+ * @see CameraDevice.CaptureListener#onCaptureCompleted
+ */
+public final class TotalCaptureResult extends CaptureResult {
+
+    /**
+     * Takes ownership of the passed-in properties object
+     * @hide
+     */
+    public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent, int sequenceId) {
+        super(results, parent, sequenceId);
+    }
+
+    /**
+     * Creates a request-less result.
+     *
+     * <p><strong>For testing only.</strong></p>
+     * @hide
+     */
+    public TotalCaptureResult(CameraMetadataNative results, int sequenceId) {
+        super(results, sequenceId);
+    }
+
+    /**
+     * Get the read-only list of partial results that compose this total result.
+     *
+     * <p>The list is returned is unmodifiable; attempting to modify it will result in a
+     * {@code UnsupportedOperationException} being thrown.</p>
+     *
+     * <p>The list size will be inclusive between {@code 1} and
+     * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order
+     * of when {@link CameraDevice.CaptureListener#onCaptureProgressed} was invoked.</p>
+     *
+     * @return unmodifiable list of partial results
+     */
+    public List<CaptureResult> getPartialResults() {
+        // TODO
+        return Collections.unmodifiableList(null);
+    }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index b082a70..9a4c531 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -20,10 +20,13 @@
 
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.LongParcelable;
@@ -72,6 +75,7 @@
     private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
 
     private final String mCameraId;
+    private final CameraCharacteristics mCharacteristics;
 
     /**
      * A list tracking request and its expected last frame.
@@ -150,13 +154,15 @@
         }
     };
 
-    public CameraDevice(String cameraId, StateListener listener, Handler handler) {
+    public CameraDevice(String cameraId, StateListener listener, Handler handler,
+                        CameraCharacteristics characteristics) {
         if (cameraId == null || listener == null || handler == null) {
             throw new IllegalArgumentException("Null argument given");
         }
         mCameraId = cameraId;
         mDeviceListener = listener;
         mDeviceHandler = handler;
+        mCharacteristics = characteristics;
 
         final int MAX_TAG_LEN = 23;
         String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -359,7 +365,7 @@
                             holder.getListener().onCaptureSequenceCompleted(
                                     CameraDevice.this,
                                     requestId,
-                                    (int)lastFrameNumber);
+                                    lastFrameNumber);
                         }
                     }
                 };
@@ -717,7 +723,7 @@
                                 holder.getListener().onCaptureSequenceCompleted(
                                     CameraDevice.this,
                                     requestId,
-                                    (int)lastFrameNumber);
+                                    lastFrameNumber);
                             }
                         }
                     };
@@ -850,11 +856,18 @@
         @Override
         public void onResultReceived(CameraMetadataNative result,
                 CaptureResultExtras resultExtras) throws RemoteException {
+
             int requestId = resultExtras.getRequestId();
             if (DEBUG) {
                 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
                         + requestId);
             }
+
+
+            // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
+            result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+                    getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+
             final CaptureListenerHolder holder;
             synchronized (mLock) {
                 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
@@ -888,12 +901,15 @@
             }
 
             final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-            final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
+
 
             Runnable resultDispatch = null;
 
             // Either send a partial result or the final capture completed result
             if (quirkIsPartialResult) {
+                final CaptureResult resultAsCapture =
+                        new CaptureResult(result, request, requestId);
+
                 // Partial result
                 resultDispatch = new Runnable() {
                     @Override
@@ -907,6 +923,9 @@
                     }
                 };
             } else {
+                final TotalCaptureResult resultAsCapture =
+                        new TotalCaptureResult(result, request, requestId);
+
                 // Final capture result
                 resultDispatch = new Runnable() {
                     @Override
@@ -958,4 +977,8 @@
             return (mRemoteDevice == null);
         }
     }
+
+    private CameraCharacteristics getCharacteristics() {
+        return mCharacteristics;
+    }
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 27cfd38..dc0c652 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -20,7 +20,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.marshal.Marshaler;
 import android.hardware.camera2.marshal.MarshalQueryable;
@@ -43,28 +43,210 @@
 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
 import android.hardware.camera2.marshal.impl.MarshalQueryableString;
 import android.hardware.camera2.params.Face;
+import android.hardware.camera2.params.LensShadingMap;
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.TonemapCurve;
+import android.hardware.camera2.utils.TypeReference;
+import android.location.Location;
+import android.location.LocationManager;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
 
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * Implementation of camera metadata marshal/unmarshal across Binder to
  * the camera service
  */
-public class CameraMetadataNative extends CameraMetadata implements Parcelable {
+public class CameraMetadataNative implements Parcelable {
+
+    public static class Key<T> {
+        private boolean mHasTag;
+        private int mTag;
+        private final Class<T> mType;
+        private final TypeReference<T> mTypeReference;
+        private final String mName;
+
+        /**
+         * Visible for testing only.
+         *
+         * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
+         * for application code or vendor-extended keys.</p>
+         */
+        public Key(String name, Class<T> type) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (type == null) {
+                throw new NullPointerException("Type needs to be non-null");
+            }
+            mName = name;
+            mType = type;
+            mTypeReference = TypeReference.createSpecializedTypeReference(type);
+        }
+
+        /**
+         * Visible for testing only.
+         *
+         * <p>Use the CameraCharacteristics.Key, CaptureResult.Key, or CaptureRequest.Key
+         * for application code or vendor-extended keys.</p>
+         */
+        @SuppressWarnings("unchecked")
+        public Key(String name, TypeReference<T> typeReference) {
+            if (name == null) {
+                throw new NullPointerException("Key needs a valid name");
+            } else if (typeReference == null) {
+                throw new NullPointerException("TypeReference needs to be non-null");
+            }
+            mName = name;
+            mType = (Class<T>)typeReference.getRawType();
+            mTypeReference = typeReference;
+        }
+
+        /**
+         * Return a camelCase, period separated name formatted like:
+         * {@code "root.section[.subsections].name"}.
+         *
+         * <p>Built-in keys exposed by the Android SDK are always prefixed with {@code "android."};
+         * keys that are device/platform-specific are prefixed with {@code "com."}.</p>
+         *
+         * <p>For example, {@code CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP} would
+         * have a name of {@code "android.scaler.streamConfigurationMap"}; whereas a device
+         * specific key might look like {@code "com.google.nexus.data.private"}.</p>
+         *
+         * @return String representation of the key name
+         */
+        public final String getName() {
+            return mName;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public final int hashCode() {
+            return mName.hashCode() ^ mTypeReference.hashCode();
+        }
+
+        /**
+         * Compare this key against other native keys, request keys, result keys, and
+         * characteristics keys.
+         *
+         * <p>Two keys are considered equal if their name and type reference are equal.</p>
+         *
+         * <p>Note that the equality against non-native keys is one-way. A native key may be equal
+         * to a result key; but that same result key will not be equal to a native key.</p>
+         */
+        @SuppressWarnings("rawtypes")
+        @Override
+        public final boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            Key<?> lhs;
+
+            if (o instanceof CaptureResult.Key) {
+                lhs = ((CaptureResult.Key)o).getNativeKey();
+            } else if (o instanceof CaptureRequest.Key) {
+                lhs = ((CaptureRequest.Key)o).getNativeKey();
+            } else if (o instanceof CameraCharacteristics.Key) {
+                lhs = ((CameraCharacteristics.Key)o).getNativeKey();
+            } else if ((o instanceof Key)) {
+                lhs = (Key<?>)o;
+            } else {
+                return false;
+            }
+
+            return mName.equals(lhs.mName) && mTypeReference.equals(lhs.mTypeReference);
+        }
+
+        /**
+         * <p>
+         * Get the tag corresponding to this key. This enables insertion into the
+         * native metadata.
+         * </p>
+         *
+         * <p>This value is looked up the first time, and cached subsequently.</p>
+         *
+         * @return The tag numeric value corresponding to the string
+         */
+        public final int getTag() {
+            if (!mHasTag) {
+                mTag = CameraMetadataNative.getTag(mName);
+                mHasTag = true;
+            }
+            return mTag;
+        }
+
+        /**
+         * Get the raw class backing the type {@code T} for this key.
+         *
+         * <p>The distinction is only important if {@code T} is a generic, e.g.
+         * {@code Range<Integer>} since the nested type will be erased.</p>
+         */
+        public final Class<T> getType() {
+            // TODO: remove this; other places should use #getTypeReference() instead
+            return mType;
+        }
+
+        /**
+         * Get the type reference backing the type {@code T} for this key.
+         *
+         * <p>The distinction is only important if {@code T} is a generic, e.g.
+         * {@code Range<Integer>} since the nested type will be retained.</p>
+         */
+        public final TypeReference<T> getTypeReference() {
+            return mTypeReference;
+        }
+    }
 
     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
     public static final int NATIVE_JPEG_FORMAT = 0x21;
 
+    private static final String CELLID_PROCESS = "CELLID";
+    private static final String GPS_PROCESS = "GPS";
+
+    private static String translateLocationProviderToProcess(final String provider) {
+        if (provider == null) {
+            return null;
+        }
+        switch(provider) {
+            case LocationManager.GPS_PROVIDER:
+                return GPS_PROCESS;
+            case LocationManager.NETWORK_PROVIDER:
+                return CELLID_PROCESS;
+            default:
+                return null;
+        }
+    }
+
+    private static String translateProcessToLocationProvider(final String process) {
+        if (process == null) {
+            return null;
+        }
+        switch(process) {
+            case GPS_PROCESS:
+                return LocationManager.GPS_PROVIDER;
+            case CELLID_PROCESS:
+                return LocationManager.NETWORK_PROVIDER;
+            default:
+                return null;
+        }
+    }
+
     public CameraMetadataNative() {
         super();
         mMetadataPtr = nativeAllocate();
@@ -84,6 +266,20 @@
         }
     }
 
+    /**
+     * Move the contents from {@code other} into a new camera metadata instance.</p>
+     *
+     * <p>After this call, {@code other} will become empty.</p>
+     *
+     * @param other the previous metadata instance which will get pilfered
+     * @return a new metadata instance with the values from {@code other} moved into it
+     */
+    public static CameraMetadataNative move(CameraMetadataNative other) {
+        CameraMetadataNative newObject = new CameraMetadataNative();
+        newObject.swap(other);
+        return newObject;
+    }
+
     public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
             new Parcelable.Creator<CameraMetadataNative>() {
         @Override
@@ -109,11 +305,39 @@
         nativeWriteToParcel(dest);
     }
 
-    @Override
+    /**
+     * @hide
+     */
+    public <T> T get(CameraCharacteristics.Key<T> key) {
+        return get(key.getNativeKey());
+    }
+
+    /**
+     * @hide
+     */
+    public <T> T get(CaptureResult.Key<T> key) {
+        return get(key.getNativeKey());
+    }
+
+    /**
+     * @hide
+     */
+    public <T> T get(CaptureRequest.Key<T> key) {
+        return get(key.getNativeKey());
+    }
+
+    /**
+     * Look-up a metadata field value by its key.
+     *
+     * @param key a non-{@code null} key instance
+     * @return the field corresponding to the {@code key}, or {@code null} if no value was set
+     */
     public <T> T get(Key<T> key) {
-        T value = getOverride(key);
-        if (value != null) {
-            return value;
+        Preconditions.checkNotNull(key, "key must not be null");
+
+        Pair<T, Boolean> override = getOverride(key);
+        if (override.second) {
+            return override.first;
         }
 
         return getBase(key);
@@ -152,6 +376,18 @@
         setBase(key, value);
     }
 
+    public <T> void set(CaptureRequest.Key<T> key, T value) {
+        set(key.getNativeKey(), value);
+    }
+
+    public <T> void set(CaptureResult.Key<T> key, T value) {
+        set(key.getNativeKey(), value);
+    }
+
+    public <T> void set(CameraCharacteristics.Key<T> key, T value) {
+        set(key.getNativeKey(), value);
+    }
+
     // Keep up-to-date with camera_metadata.h
     /**
      * @hide
@@ -188,6 +424,18 @@
         mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
     }
 
+    private <T> T getBase(CameraCharacteristics.Key<T> key) {
+        return getBase(key.getNativeKey());
+    }
+
+    private <T> T getBase(CaptureResult.Key<T> key) {
+        return getBase(key.getNativeKey());
+    }
+
+    private <T> T getBase(CaptureRequest.Key<T> key) {
+        return getBase(key.getNativeKey());
+    }
+
     private <T> T getBase(Key<T> key) {
         int tag = key.getTag();
         byte[] values = readValues(tag);
@@ -199,23 +447,44 @@
         ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
         return marshaler.unmarshal(buffer);
     }
-
     // Need overwrite some metadata that has different definitions between native
     // and managed sides.
     @SuppressWarnings("unchecked")
-    private <T> T getOverride(Key<T> key) {
+    private <T> Pair<T, Boolean> getOverride(Key<T> key) {
+        T value = null;
+        boolean override = true;
+
         if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
-            return (T) getAvailableFormats();
+            value = (T) getAvailableFormats();
         } else if (key.equals(CaptureResult.STATISTICS_FACES)) {
-            return (T) getFaces();
+            value = (T) getFaces();
         } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
-            return (T) getFaceRectangles();
+            value = (T) getFaceRectangles();
         } else if (key.equals(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)) {
-            return (T) getStreamConfigurationMap();
+            value = (T) getStreamConfigurationMap();
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
+            value = (T) getMaxRegions(key);
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
+            value = (T) getMaxRegions(key);
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
+            value = (T) getMaxRegions(key);
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
+            value = (T) getMaxNumOutputs(key);
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
+            value = (T) getMaxNumOutputs(key);
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
+            value = (T) getMaxNumOutputs(key);
+        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
+            value = (T) getTonemapCurve();
+        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
+            value = (T) getGpsLocation();
+        } else if (key.equals(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP)) {
+            value = (T) getLensShadingMap();
+        } else {
+            override = false;
         }
 
-        // For other keys, get() falls back to getBase()
-        return null;
+        return Pair.create(value, override);
     }
 
     private int[] getAvailableFormats() {
@@ -331,6 +600,62 @@
         return fixedFaceRectangles;
     }
 
+    private LensShadingMap getLensShadingMap() {
+        float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+        if (lsmArray == null) {
+            Log.w(TAG, "getLensShadingMap - Lens shading map was null.");
+            return null;
+        }
+        Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+        LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
+        return map;
+    }
+
+    private Location getGpsLocation() {
+        String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
+        Location l = new Location(translateProcessToLocationProvider(processingMethod));
+
+        double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
+        Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
+
+        if (timeStamp != null) {
+            l.setTime(timeStamp);
+        } else {
+            Log.w(TAG, "getGpsLocation - No timestamp for GPS location.");
+        }
+
+        if (coords != null) {
+            l.setLatitude(coords[0]);
+            l.setLongitude(coords[1]);
+            l.setAltitude(coords[2]);
+        } else {
+            Log.w(TAG, "getGpsLocation - No coordinates for GPS location");
+        }
+
+        return l;
+    }
+
+    private boolean setGpsLocation(Location l) {
+        if (l == null) {
+            return false;
+        }
+
+        double[] coords = { l.getLatitude(), l.getLongitude(), l.getAltitude() };
+        String processMethod = translateLocationProviderToProcess(l.getProvider());
+        long timestamp = l.getTime();
+
+        set(CaptureRequest.JPEG_GPS_TIMESTAMP, timestamp);
+        set(CaptureRequest.JPEG_GPS_COORDINATES, coords);
+
+        if (processMethod == null) {
+            Log.w(TAG, "setGpsLocation - No process method, Location is not from a GPS or NETWORK" +
+                    "provider");
+        } else {
+            setBase(CaptureRequest.JPEG_GPS_PROCESSING_METHOD, processMethod);
+        }
+        return true;
+    }
+
     private StreamConfigurationMap getStreamConfigurationMap() {
         StreamConfiguration[] configurations = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -342,6 +667,75 @@
         return new StreamConfigurationMap(configurations, minFrameDurations, stallDurations);
     }
 
+    private <T> Integer getMaxRegions(Key<T> key) {
+        final int AE = 0;
+        final int AWB = 1;
+        final int AF = 2;
+
+        // The order of the elements is: (AE, AWB, AF)
+        int[] maxRegions = getBase(CameraCharacteristics.CONTROL_MAX_REGIONS);
+
+        if (maxRegions == null) {
+            return null;
+        }
+
+        if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
+            return maxRegions[AE];
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
+            return maxRegions[AWB];
+        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
+            return maxRegions[AF];
+        } else {
+            throw new AssertionError("Invalid key " + key);
+        }
+    }
+
+    private <T> Integer getMaxNumOutputs(Key<T> key) {
+        final int RAW = 0;
+        final int PROC = 1;
+        final int PROC_STALLING = 2;
+
+        // The order of the elements is: (raw, proc+nonstalling, proc+stalling)
+        int[] maxNumOutputs = getBase(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS);
+
+        if (maxNumOutputs == null) {
+            return null;
+        }
+
+        if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
+            return maxNumOutputs[RAW];
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
+            return maxNumOutputs[PROC];
+        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
+            return maxNumOutputs[PROC_STALLING];
+        } else {
+            throw new AssertionError("Invalid key " + key);
+        }
+    }
+
+    private <T> TonemapCurve getTonemapCurve() {
+        float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
+        float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
+        float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
+        if (red == null || green == null || blue == null) {
+            return null;
+        }
+        TonemapCurve tc = new TonemapCurve(red, green, blue);
+        return tc;
+    }
+
+    private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
+        setBase(key.getNativeKey(), value);
+    }
+
+    private <T> void setBase(CaptureResult.Key<T> key, T value) {
+        setBase(key.getNativeKey(), value);
+    }
+
+    private <T> void setBase(CaptureRequest.Key<T> key, T value) {
+        setBase(key.getNativeKey(), value);
+    }
+
     private <T> void setBase(Key<T> key, T value) {
         int tag = key.getTag();
 
@@ -369,8 +763,11 @@
             return setAvailableFormats((int[]) value);
         } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
             return setFaceRectangles((Rect[]) value);
+        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
+            return setTonemapCurve((TonemapCurve) value);
+        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
+            return setGpsLocation((Location) value);
         }
-
         // For other keys, set() falls back to setBase().
         return false;
     }
@@ -424,6 +821,24 @@
         return true;
     }
 
+    private <T> boolean setTonemapCurve(TonemapCurve tc) {
+        if (tc == null) {
+            return false;
+        }
+
+        float[][] curve = new float[3][];
+        for (int i = TonemapCurve.CHANNEL_RED; i <= TonemapCurve.CHANNEL_BLUE; i++) {
+            int pointCount = tc.getPointCount(i);
+            curve[i] = new float[pointCount * TonemapCurve.POINT_SIZE];
+            tc.copyColorCurve(i, curve[i], 0);
+        }
+        setBase(CaptureRequest.TONEMAP_CURVE_RED, curve[0]);
+        setBase(CaptureRequest.TONEMAP_CURVE_GREEN, curve[1]);
+        setBase(CaptureRequest.TONEMAP_CURVE_BLUE, curve[2]);
+
+        return true;
+    }
+
     private long mMetadataPtr; // native CameraMetadata*
 
     private native long nativeAllocate();
@@ -440,6 +855,7 @@
 
     private native synchronized byte[] nativeReadValues(int tag);
     private native synchronized void nativeWriteValues(int tag, byte[] src);
+    private native synchronized void nativeDump() throws IOException; // dump to ALOGD
 
     private static native int nativeGetTagFromKey(String keyName)
             throws IllegalArgumentException;
@@ -531,6 +947,22 @@
         return nativeReadValues(tag);
     }
 
+    /**
+     * Dumps the native metadata contents to logcat.
+     *
+     * <p>Visibility for testing/debugging only. The results will not
+     * include any synthesized keys, as they are invisible to the native layer.</p>
+     *
+     * @hide
+     */
+    public void dumpToLog() {
+        try {
+            nativeDump();
+        } catch (IOException e) {
+            Log.wtf(TAG, "Dump logging failed", e);
+        }
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -599,5 +1031,4 @@
         nativeClassInit();
         registerAllMarshalers();
     }
-
 }
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 71adf8b..22ff9c6 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -93,7 +93,7 @@
      * {@link CameraDeviceStateListener#onConfiguring()} will be called.
      * </p>
      *
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setConfiguring() {
         doStateTransition(STATE_CONFIGURING);
@@ -108,7 +108,7 @@
      * {@link CameraDeviceStateListener#onIdle()} will be called.
      * </p>
      *
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setIdle() {
         doStateTransition(STATE_IDLE);
@@ -124,7 +124,7 @@
      * </p>
      *
      * @param request A {@link RequestHolder} containing the request for the current capture.
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setCaptureStart(final RequestHolder request) {
         mCurrentRequest = request;
@@ -144,7 +144,7 @@
      *
      * @param request the {@link RequestHolder} request that created this result.
      * @param result the {@link CameraMetadataNative} result to set.
-     * @returns {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
+     * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
      */
     public synchronized int setCaptureResult(final RequestHolder request,
                                              final CameraMetadataNative result) {
diff --git a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
index fa8c8ea..b4289db2 100644
--- a/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
+++ b/core/java/android/hardware/camera2/params/ColorSpaceTransform.java
@@ -139,8 +139,8 @@
             throw new IllegalArgumentException("row out of range");
         }
 
-        int numerator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_NUMERATOR];
-        int denominator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_DENOMINATOR];
+        int numerator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_NUMERATOR];
+        int denominator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_DENOMINATOR];
 
         return new Rational(numerator, denominator);
     }
@@ -162,7 +162,7 @@
     public void copyElements(Rational[] destination, int offset) {
         checkArgumentNonnegative(offset, "offset must not be negative");
         checkNotNull(destination, "destination must not be null");
-        if (destination.length + offset < COUNT) {
+        if (destination.length - offset < COUNT) {
             throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
         }
 
@@ -197,7 +197,7 @@
     public void copyElements(int[] destination, int offset) {
         checkArgumentNonnegative(offset, "offset must not be negative");
         checkNotNull(destination, "destination must not be null");
-        if (destination.length + offset < COUNT_INT) {
+        if (destination.length - offset < COUNT_INT) {
             throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
         }
 
diff --git a/core/java/android/hardware/camera2/params/LensShadingMap.java b/core/java/android/hardware/camera2/params/LensShadingMap.java
index b328f578..9bbc33a 100644
--- a/core/java/android/hardware/camera2/params/LensShadingMap.java
+++ b/core/java/android/hardware/camera2/params/LensShadingMap.java
@@ -28,7 +28,7 @@
 /**
  * Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
  *
- * @see CameraCharacteristics#LENS_SHADING_MAP
+ * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP
  */
 public final class LensShadingMap {
 
@@ -62,12 +62,12 @@
     public LensShadingMap(final float[] elements, final int rows, final int columns) {
 
         mRows = checkArgumentPositive(rows, "rows must be positive");
-        mColumns = checkArgumentPositive(rows, "columns must be positive");
+        mColumns = checkArgumentPositive(columns, "columns must be positive");
         mElements = checkNotNull(elements, "elements must not be null");
 
         if (elements.length != getGainFactorCount()) {
             throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
-                    " length");
+                    " length, received " + elements.length);
         }
 
         // Every element must be finite and >= 1.0f
@@ -242,4 +242,4 @@
     private final int mRows;
     private final int mColumns;
     private final float[] mElements;
-};
+}
diff --git a/core/java/android/hardware/camera2/params/MeteringRectangle.java b/core/java/android/hardware/camera2/params/MeteringRectangle.java
index a26c57d..a7a3b59 100644
--- a/core/java/android/hardware/camera2/params/MeteringRectangle.java
+++ b/core/java/android/hardware/camera2/params/MeteringRectangle.java
@@ -57,7 +57,7 @@
      * @param height height >= 0
      * @param meteringWeight weight >= 0
      *
-     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws IllegalArgumentException if any of the parameters were negative
      */
     public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
         mX = checkArgumentNonnegative(x, "x must be nonnegative");
@@ -74,7 +74,7 @@
      * @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
      * @param meteringWeight weight >= 0
      *
-     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws IllegalArgumentException if any of the parameters were negative
      * @throws NullPointerException if any of the arguments were null
      */
     public MeteringRectangle(Point xy, Size dimensions, int meteringWeight) {
@@ -94,7 +94,7 @@
      * @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
      * @param meteringWeight weight >= 0
      *
-     * @throws IllegalArgumentException if any of the parameters were non-negative
+     * @throws IllegalArgumentException if any of the parameters were negative
      * @throws NullPointerException if any of the arguments were null
      */
     public MeteringRectangle(Rect rect, int meteringWeight) {
@@ -210,7 +210,7 @@
                 && mY == other.mY
                 && mWidth == other.mWidth
                 && mHeight == other.mHeight
-                && mWidth == other.mWidth);
+                && mWeight == other.mWeight);
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4bccaf1..3417de1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -39,6 +39,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -679,7 +680,7 @@
         mInflater = (LayoutInflater)getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
-                false);
+                WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
         if (mHardwareAccelerated) {
             mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
         }
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index a9bace1..38a65c5 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -37,6 +37,8 @@
     final Callback mCallback;
     final KeyEvent.Callback mKeyEventCallback;
     final KeyEvent.DispatcherState mDispatcherState;
+    final int mWindowType;
+    final int mGravity;
     final boolean mTakesFocus;
     private final Rect mBounds = new Rect();
 
@@ -64,12 +66,14 @@
      */
     public SoftInputWindow(Context context, String name, int theme, Callback callback,
             KeyEvent.Callback keyEventCallback, KeyEvent.DispatcherState dispatcherState,
-            boolean takesFocus) {
+            int windowType, int gravity, boolean takesFocus) {
         super(context, theme);
         mName = name;
         mCallback = callback;
         mKeyEventCallback = keyEventCallback;
         mDispatcherState = dispatcherState;
+        mWindowType = windowType;
+        mGravity = gravity;
         mTakesFocus = takesFocus;
         initDockWindow();
     }
@@ -97,47 +101,6 @@
     }
 
     /**
-     * Get the size of the DockWindow.
-     * 
-     * @return If the DockWindow sticks to the top or bottom of the screen, the
-     *         return value is the height of the DockWindow, and its width is
-     *         equal to the width of the screen; If the DockWindow sticks to the
-     *         left or right of the screen, the return value is the width of the
-     *         DockWindow, and its height is equal to the height of the screen.
-     */
-    public int getSize() {
-        WindowManager.LayoutParams lp = getWindow().getAttributes();
-
-        if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
-            return lp.height;
-        } else {
-            return lp.width;
-        }
-    }
-
-    /**
-     * Set the size of the DockWindow.
-     * 
-     * @param size If the DockWindow sticks to the top or bottom of the screen,
-     *        <var>size</var> is the height of the DockWindow, and its width is
-     *        equal to the width of the screen; If the DockWindow sticks to the
-     *        left or right of the screen, <var>size</var> is the width of the
-     *        DockWindow, and its height is equal to the height of the screen.
-     */
-    public void setSize(int size) {
-        WindowManager.LayoutParams lp = getWindow().getAttributes();
-
-        if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
-            lp.width = -1;
-            lp.height = size;
-        } else {
-            lp.width = size;
-            lp.height = -1;
-        }
-        getWindow().setAttributes(lp);
-    }
-
-    /**
      * Set which boundary of the screen the DockWindow sticks to.
      * 
      * @param gravity The boundary of the screen to stick. See {#link
@@ -147,18 +110,18 @@
      */
     public void setGravity(int gravity) {
         WindowManager.LayoutParams lp = getWindow().getAttributes();
-
-        boolean oldIsVertical = (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM);
-
         lp.gravity = gravity;
+        updateWidthHeight(lp);
+        getWindow().setAttributes(lp);
+    }
 
-        boolean newIsVertical = (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM);
-
-        if (oldIsVertical != newIsVertical) {
-            int tmp = lp.width;
-            lp.width = lp.height;
-            lp.height = tmp;
-            getWindow().setAttributes(lp);
+    private void updateWidthHeight(WindowManager.LayoutParams lp) {
+        if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        } else {
+            lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.height = WindowManager.LayoutParams.MATCH_PARENT;
         }
     }
 
@@ -201,14 +164,11 @@
     private void initDockWindow() {
         WindowManager.LayoutParams lp = getWindow().getAttributes();
 
-        lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+        lp.type = mWindowType;
         lp.setTitle(mName);
 
-        lp.gravity = Gravity.BOTTOM;
-        lp.width = -1;
-        // Let the input method window's orientation follow sensor based rotation
-        // Turn this off for now, it is very problematic.
-        //lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
+        lp.gravity = mGravity;
+        updateWidthHeight(lp);
 
         getWindow().setAttributes(lp);
 
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/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/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index c2b06a2..1c18ba5 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -257,31 +257,43 @@
     }
 
     /**
-     * called to go through our list of requests and see if we're
-     * good enough to try connecting.
+     * Called to go through our list of requests and see if we're
+     * good enough to try connecting, or if we have gotten worse and
+     * need to disconnect.
      *
-     * Only does connects - we disconnect when requested via
+     * Once we are registered, does nothing: we disconnect when requested via
      * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection
      * between modules (bearer or ConnectivityService dies) or more commonly
      * when the NetworkInfo reports to ConnectivityService it is disconnected.
      */
     private void evalScores() {
-        if (mConnectionRequested) {
-            if (VDBG) log("evalScores - already trying - size=" + mNetworkRequests.size());
-            // already trying
-            return;
-        }
-        if (VDBG) log("evalScores!");
-        for (int i=0; i < mNetworkRequests.size(); i++) {
-            int score = mNetworkRequests.valueAt(i).score;
-            if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore);
-            if (score < mNetworkScore) {
-                // have a request that has a lower scored network servicing it
-                // (or no network) than we could provide, so lets connect!
-                mConnectionRequested = true;
-                connect();
+        synchronized(mLockObj) {
+            if (mRegistered) {
+                if (VDBG) log("evalScores - already connected - size=" + mNetworkRequests.size());
+                // already trying
                 return;
             }
+            if (VDBG) log("evalScores!");
+            for (int i=0; i < mNetworkRequests.size(); i++) {
+                int score = mNetworkRequests.valueAt(i).score;
+                if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore);
+                if (score < mNetworkScore) {
+                    // have a request that has a lower scored network servicing it
+                    // (or no network) than we could provide, so let's connect!
+                    mConnectionRequested = true;
+                    connect();
+                    return;
+                }
+            }
+            // Our score is not high enough to satisfy any current request.
+            // This can happen if our score goes down after a connection is
+            // requested but before we actually connect. In this case, disconnect
+            // rather than continue trying - there's no point connecting if we know
+            // we'll just be torn down as soon as we do.
+            if (mConnectionRequested) {
+                mConnectionRequested = false;
+                disconnect();
+            }
         }
     }
 
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/BatteryStats.java b/core/java/android/os/BatteryStats.java
index af45fa0..bc57b33 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2443,8 +2443,10 @@
             pw.print(prefix); pw.print("    Capacity: ");
                     printmAh(pw, helper.getPowerProfile().getBatteryCapacity());
                     pw.print(", Computed drain: "); printmAh(pw, helper.getComputedPower());
-                    pw.print(", Min drain: "); printmAh(pw, helper.getMinDrainedPower());
-                    pw.print(", Max drain: "); printmAh(pw, helper.getMaxDrainedPower());
+                    pw.print(", actual drain: "); printmAh(pw, helper.getMinDrainedPower());
+                    if (helper.getMinDrainedPower() != helper.getMaxDrainedPower()) {
+                        pw.print("-"); printmAh(pw, helper.getMaxDrainedPower());
+                    }
                     pw.println();
             for (int i=0; i<sippers.size(); i++) {
                 BatterySipper bs = sippers.get(i);
@@ -3301,7 +3303,8 @@
             if (rec.time >= histStart) {
                 if (histStart >= 0 && !printed) {
                     if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
-                            || rec.cmd == HistoryItem.CMD_RESET) {
+                            || rec.cmd == HistoryItem.CMD_RESET
+                            || rec.cmd == HistoryItem.CMD_START) {
                         printed = true;
                         hprinter.printNextItem(pw, rec, baseTime, checkin,
                                 (flags&DUMP_VERBOSE) != 0);
@@ -3351,7 +3354,10 @@
                 }
                 hprinter.printNextItem(pw, rec, baseTime, checkin,
                         (flags&DUMP_VERBOSE) != 0);
-            } else if (rec.eventCode != HistoryItem.EVENT_NONE) {
+            } else if (false && rec.eventCode != HistoryItem.EVENT_NONE) {
+                // This is an attempt to aggregate the previous state and generate
+                // fake events to reflect that state at the point where we start
+                // printing real events.  It doesn't really work right, so is turned off.
                 if (tracker == null) {
                     tracker = new HistoryEventTracker();
                 }
diff --git a/core/java/android/os/CommonBundle.java b/core/java/android/os/CommonBundle.java
index e11f170..c1b202c 100644
--- a/core/java/android/os/CommonBundle.java
+++ b/core/java/android/os/CommonBundle.java
@@ -18,11 +18,10 @@
 
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 
 import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -304,6 +303,16 @@
     }
 
     /**
+     * Inserts all mappings from the given Map into this CommonBundle.
+     *
+     * @param map a Map
+     */
+    void putAll(Map map) {
+        unparcel();
+        mMap.putAll(map);
+    }
+
+    /**
      * Returns a Set containing the Strings used as keys in this Bundle.
      *
      * @return a Set of String keys
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index c2cd3be..cd8d515 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -17,7 +17,14 @@
 package android.os;
 
 import android.util.ArrayMap;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -25,7 +32,8 @@
  * restored.
  *
  */
-public final class PersistableBundle extends CommonBundle {
+public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
+    private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
     public static final PersistableBundle EMPTY;
     static final Parcel EMPTY_PARCEL;
 
@@ -88,6 +96,38 @@
     }
 
     /**
+     * Constructs a PersistableBundle containing the mappings passed in.
+     *
+     * @param map a Map containing only those items that can be persisted.
+     * @throws IllegalArgumentException if any element of #map cannot be persisted.
+     */
+    private PersistableBundle(Map<String, Object> map) {
+        super();
+
+        // First stuff everything in.
+        putAll(map);
+
+        // Now verify each item throwing an exception if there is a violation.
+        Set<String> keys = map.keySet();
+        Iterator<String> iterator = keys.iterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next();
+            Object value = map.get(key);
+            if (value instanceof Map) {
+                // Fix up any Maps by replacing them with PersistableBundles.
+                putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value));
+            } else if (!(value instanceof Integer) && !(value instanceof Long) &&
+                    !(value instanceof Double) && !(value instanceof String) &&
+                    !(value instanceof int[]) && !(value instanceof long[]) &&
+                    !(value instanceof double[]) && !(value instanceof String[]) &&
+                    !(value instanceof PersistableBundle) && (value != null)) {
+                throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key +
+                        " value=" + value);
+            }
+        }
+    }
+
+    /**
      * Make a PersistableBundle for a single key/value pair.
      *
      * @hide
@@ -206,6 +246,7 @@
      *
      * @param bundle a PersistableBundle
      */
+    @Override
     public void putAll(PersistableBundle bundle) {
         super.putAll(bundle);
     }
@@ -323,6 +364,7 @@
      * @param key a String, or null
      * @param value a Bundle object, or null
      */
+    @Override
     public void putPersistableBundle(String key, PersistableBundle value) {
         super.putPersistableBundle(key, value);
     }
@@ -539,6 +581,57 @@
         super.readFromParcelInner(parcel);
     }
 
+    /** @hide */
+    @Override
+    public void writeUnknownObject(Object v, String name, XmlSerializer out)
+            throws XmlPullParserException, IOException {
+        if (v instanceof PersistableBundle) {
+            out.startTag(null, TAG_PERSISTABLEMAP);
+            out.attribute(null, "name", name);
+            ((PersistableBundle) v).saveToXml(out);
+            out.endTag(null, TAG_PERSISTABLEMAP);
+        } else {
+            throw new XmlPullParserException("Unknown Object o=" + v);
+        }
+    }
+
+    /** @hide */
+    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        unparcel();
+        XmlUtils.writeMapXml(mMap, out, this);
+    }
+
+    /** @hide */
+    static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
+        @Override
+        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+                throws XmlPullParserException, IOException {
+            if (TAG_PERSISTABLEMAP.equals(tag)) {
+                return restoreFromXml(in);
+            }
+            throw new XmlPullParserException("Unknown tag=" + tag);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
+            XmlPullParserException {
+        final int outerDepth = in.getDepth();
+        final String startTag = in.getName();
+        final String[] tagName = new String[1];
+        int event;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                return new PersistableBundle((Map<String, Object>)
+                        XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback()));
+            }
+        }
+        return EMPTY;
+    }
+
     @Override
     synchronized public String toString() {
         if (mParcelledData != null) {
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index c1d4d4c..cb0f142 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -74,6 +74,7 @@
      * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
      *        For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
      *        {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+     * @hide
      */
     public void vibrate(long milliseconds, int streamHint) {
         vibrate(Process.myUid(), mPackageName, milliseconds, streamHint);
@@ -125,6 +126,7 @@
      * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
      *        For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
      *        {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+     * @hide
      */
     public void vibrate(long[] pattern, int repeat, int streamHint) {
         vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4963991..68b91cb 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -58,24 +58,6 @@
  * argument of {@link android.content.Context#STORAGE_SERVICE}.
  */
 public class StorageManager {
-
-    /// Consts to match the password types in cryptfs.h
-    /** Master key is encrypted with a password.
-     */
-    public static final int CRYPT_TYPE_PASSWORD = 0;
-
-    /** Master key is encrypted with the default password.
-     */
-    public static final int CRYPT_TYPE_DEFAULT = 1;
-
-    /** Master key is encrypted with a pattern.
-     */
-    public static final int CRYPT_TYPE_PATTERN = 2;
-
-    /** Master key is encrypted with a pin.
-     */
-    public static final int CRYPT_TYPE_PIN = 3;
-
     private static final String TAG = "StorageManager";
 
     private final ContentResolver mResolver;
@@ -663,4 +645,14 @@
         return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                 DEFAULT_FULL_THRESHOLD_BYTES);
     }
+
+    /// Consts to match the password types in cryptfs.h
+    /** @hide */
+    public static final int CRYPT_TYPE_PASSWORD = 0;
+    /** @hide */
+    public static final int CRYPT_TYPE_DEFAULT = 1;
+    /** @hide */
+    public static final int CRYPT_TYPE_PATTERN = 2;
+    /** @hide */
+    public static final int CRYPT_TYPE_PIN = 3;
 }
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index d2d6ade..5e005d0 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -37,20 +37,22 @@
  * @hide
  */
 public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
+    private static final String TAG = "SeekBarVolumizer";
 
     public interface Callback {
         void onSampleStarting(SeekBarVolumizer sbv);
     }
 
-    private Context mContext;
-    private Handler mHandler;
+    private final Context mContext;
+    private final Handler mHandler;
     private final Callback mCallback;
+    private final Uri mDefaultUri;
+    private final AudioManager mAudioManager;
+    private final int mStreamType;
+    private final int mMaxStreamVolume;
 
-    private AudioManager mAudioManager;
-    private int mStreamType;
     private int mOriginalStreamVolume;
     private Ringtone mRingtone;
-
     private int mLastProgress = -1;
     private SeekBar mSeekBar;
     private int mVolumeBeforeMute = -1;
@@ -58,9 +60,10 @@
     private static final int MSG_SET_STREAM_VOLUME = 0;
     private static final int MSG_START_SAMPLE = 1;
     private static final int MSG_STOP_SAMPLE = 2;
+    private static final int MSG_INIT_SAMPLE = 3;
     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
 
-    private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
+    private ContentObserver mVolumeObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange) {
             super.onChange(selfChange);
@@ -71,27 +74,17 @@
         }
     };
 
-    public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType, Uri defaultUri,
+    public SeekBarVolumizer(Context context, int streamType, Uri defaultUri,
             Callback callback) {
         mContext = context;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         mStreamType = streamType;
-        mSeekBar = seekBar;
-
-        HandlerThread thread = new HandlerThread(VolumePreference.TAG + ".CallbackHandler");
+        mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
+        HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
         thread.start();
         mHandler = new Handler(thread.getLooper(), this);
         mCallback = callback;
-
-        initSeekBar(seekBar, defaultUri);
-    }
-
-    private void initSeekBar(SeekBar seekBar, Uri defaultUri) {
-        seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType));
         mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
-        seekBar.setProgress(mOriginalStreamVolume);
-        seekBar.setOnSeekBarChangeListener(this);
-
         mContext.getContentResolver().registerContentObserver(
                 System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
                 false, mVolumeObserver);
@@ -105,12 +98,16 @@
                 defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
             }
         }
+        mDefaultUri = defaultUri;
+        mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
+    }
 
-        mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
-
-        if (mRingtone != null) {
-            mRingtone.setStreamType(mStreamType);
-        }
+    public void setSeekBar(SeekBar seekBar) {
+        mSeekBar = seekBar;
+        mSeekBar.setOnSeekBarChangeListener(null);
+        mSeekBar.setMax(mMaxStreamVolume);
+        mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
+        mSeekBar.setOnSeekBarChangeListener(this);
     }
 
     @Override
@@ -125,12 +122,22 @@
             case MSG_STOP_SAMPLE:
                 onStopSample();
                 break;
+            case MSG_INIT_SAMPLE:
+                onInitSample();
+                break;
             default:
-                Log.e(VolumePreference.TAG, "invalid SeekBarVolumizer message: "+msg.what);
+                Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
         }
         return true;
     }
 
+    private void onInitSample() {
+        mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);
+        if (mRingtone != null) {
+            mRingtone.setStreamType(mStreamType);
+        }
+    }
+
     private void postStartSample() {
         mHandler.removeMessages(MSG_START_SAMPLE);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 171e5c3..df9e10e 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -66,7 +66,8 @@
         super.onBindDialogView(view);
 
         final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
-        mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType, null, this);
+        mSeekBarVolumizer = new SeekBarVolumizer(getContext(), mStreamType, null, this);
+        mSeekBarVolumizer.setSeekBar(seekBar);
 
         getPreferenceManager().registerOnActivityStopListener(this);
 
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 5ffffb5..0d90a16 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -74,7 +74,7 @@
      *
      * @hide
      */
-    public static final String PARAM_BROWSABLE_ONLY = "browable_only";
+    public static final String PARAM_BROWSABLE_ONLY = "browsable_only";
 
     /**
      * Builds a URI that points to a specific channel.
@@ -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/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index a83544d..cd357b7 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.inputmethodservice.SoftInputWindow;
 import android.os.Binder;
@@ -32,6 +33,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -262,14 +264,14 @@
      */
     public static final class Insets {
         /**
-         * This is the top part of the UI that is the main content.  It is
+         * This is the part of the UI that is the main content.  It is
          * used to determine the basic space needed, to resize/pan the
          * application behind.  It is assumed that this inset does not
          * change very much, since any change will cause a full resize/pan
          * of the application behind.  This value is relative to the top edge
          * of the input method window.
          */
-        public int contentTopInsets;
+        public final Rect contentInsets = new Rect();
 
         /**
          * This is the region of the UI that is touchable.  It is used when
@@ -311,7 +313,8 @@
             new ViewTreeObserver.OnComputeInternalInsetsListener() {
         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
             onComputeInsets(mTmpInsets);
-            info.contentInsets.top = info.visibleInsets.top = mTmpInsets.contentTopInsets;
+            info.contentInsets.set(mTmpInsets.contentInsets);
+            info.visibleInsets.set(mTmpInsets.contentInsets);
             info.touchableRegion.set(mTmpInsets.touchableRegion);
             info.setTouchableInsets(mTmpInsets.touchableInsets);
         }
@@ -428,6 +431,8 @@
             throw new IllegalStateException("Can't call before onCreate()");
         }
         try {
+            intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             int res = mSystemService.startVoiceActivity(mToken, intent,
                     intent.resolveType(mContext.getContentResolver()));
             Instrumentation.checkStartActivityResult(res, intent);
@@ -460,7 +465,8 @@
         mInflater = (LayoutInflater)mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
-                mCallbacks, this, mDispatcherState, true);
+                mCallbacks, this, mDispatcherState,
+                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.TOP, true);
         mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
         initViews();
         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
@@ -517,7 +523,10 @@
         int[] loc = mTmpLocation;
         View decor = getWindow().getWindow().getDecorView();
         decor.getLocationInWindow(loc);
-        outInsets.contentTopInsets = loc[1];
+        outInsets.contentInsets.top = 0;
+        outInsets.contentInsets.left = 0;
+        outInsets.contentInsets.right = 0;
+        outInsets.contentInsets.bottom = 0;
         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
         outInsets.touchableRegion.setEmpty();
     }
diff --git a/core/java/android/tv/ITvInputHardware.aidl b/core/java/android/tv/ITvInputHardware.aidl
new file mode 100644
index 0000000..7250453
--- /dev/null
+++ b/core/java/android/tv/ITvInputHardware.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.tv;
+
+import android.tv.TvStreamConfig;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+/**
+ * TvInputService representing a physical port should connect to HAL through this interface.
+ * Framework will take care of communication among system services including TvInputManagerService,
+ * HdmiControlService, AudioService, etc.
+ *
+ * @hide
+ */
+interface ITvInputHardware {
+    /**
+     * Make the input render on the surface according to the config. In case of HDMI, this will
+     * trigger CEC commands for adjusting active HDMI source. Returns true on success.
+     */
+    boolean setSurface(in Surface surface, in TvStreamConfig config);
+    /**
+     * Set volume for this stream via AudioGain. (TBD)
+     */
+    void setVolume(float volume);
+
+    /**
+     * Dispatch key event to HDMI service. The events would be automatically converted to
+     * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
+     */
+    boolean dispatchKeyEventToHdmi(in KeyEvent event);
+}
diff --git a/core/java/android/tv/ITvInputHardwareCallback.aidl b/core/java/android/tv/ITvInputHardwareCallback.aidl
new file mode 100644
index 0000000..83041be
--- /dev/null
+++ b/core/java/android/tv/ITvInputHardwareCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.tv;
+
+import android.tv.TvStreamConfig;
+
+/**
+ * @hide
+ */
+oneway interface ITvInputHardwareCallback {
+    void onReleased();
+    void onStreamConfigChanged(in TvStreamConfig[] configs);
+}
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index b756aba..c6f8d79 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -19,7 +19,10 @@
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
 import android.tv.ITvInputClient;
+import android.tv.TvInputHardwareInfo;
 import android.tv.TvInputInfo;
 import android.view.Surface;
 
@@ -46,4 +49,10 @@
             int userId);
     void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
     void removeOverlayView(in IBinder sessionToken, int userId);
+
+    // For TV input hardware binding
+    List<TvInputHardwareInfo> getHardwareList();
+    ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
+            int userId);
+    void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
 }
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/core/java/android/tv/TvInputHardwareInfo.aidl
new file mode 100644
index 0000000..484ab60
--- /dev/null
+++ b/core/java/android/tv/TvInputHardwareInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+parcelable TvInputHardwareInfo;
diff --git a/core/java/android/tv/TvInputHardwareInfo.java b/core/java/android/tv/TvInputHardwareInfo.java
new file mode 100644
index 0000000..b0dc58e
--- /dev/null
+++ b/core/java/android/tv/TvInputHardwareInfo.java
@@ -0,0 +1,93 @@
+/*
+ * 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.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Simple container for information about TV input hardware.
+ * Not for third-party developers.
+ *
+ * @hide
+ */
+public final class TvInputHardwareInfo implements Parcelable {
+    static final String TAG = "TvInputHardwareInfo";
+
+    // Match hardware/libhardware/include/hardware/tv_input.h
+    public static final int TV_INPUT_TYPE_HDMI           = 1;
+    public static final int TV_INPUT_TYPE_BUILT_IN_TUNER = 2;
+    public static final int TV_INPUT_TYPE_PASSTHROUGH    = 3;
+
+    public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
+            new Parcelable.Creator<TvInputHardwareInfo>() {
+        @Override
+        public TvInputHardwareInfo createFromParcel(Parcel source) {
+            try {
+                TvInputHardwareInfo info = new TvInputHardwareInfo();
+                info.readFromParcel(source);
+                return info;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating TvInputHardwareInfo from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public TvInputHardwareInfo[] newArray(int size) {
+            return new TvInputHardwareInfo[size];
+        }
+    };
+
+    private int mDeviceId;
+    private int mType;
+    // TODO: Add audio port & audio address for audio service.
+    // TODO: Add HDMI handle for HDMI service.
+
+    public TvInputHardwareInfo() { }
+
+    public TvInputHardwareInfo(int deviceId, int type) {
+        mDeviceId = deviceId;
+        mType = type;
+    }
+
+    public int getDeviceId() {
+        return mDeviceId;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    // Parcelable
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mDeviceId);
+        dest.writeInt(mType);
+    }
+
+    public void readFromParcel(Parcel source) {
+        mDeviceId = source.readInt();
+        mType = source.readInt();
+    }
+}
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
index 50462cc..217e4b7 100644
--- a/core/java/android/tv/TvInputInfo.java
+++ b/core/java/android/tv/TvInputInfo.java
@@ -39,7 +39,7 @@
     public TvInputInfo(ResolveInfo service) {
         mService = service;
         ServiceInfo si = service.serviceInfo;
-        mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
+        mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
     }
 
     /**
@@ -134,7 +134,7 @@
      * @return the generated input id for the given {@code name}.
      * @hide
      */
-    public static final String generateInputIdForComponenetName(ComponentName name) {
+    public static final String generateInputIdForComponentName(ComponentName name) {
         return name.flattenToShortString();
     }
 
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index eeb738d..cb0142f 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -69,7 +69,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mId = TvInputInfo.generateInputIdForComponenetName(
+        mId = TvInputInfo.generateInputIdForComponentName(
                 new ComponentName(getPackageName(), getClass().getName()));
     }
 
diff --git a/core/java/android/tv/TvStreamConfig.aidl b/core/java/android/tv/TvStreamConfig.aidl
new file mode 100644
index 0000000..4d0add4
--- /dev/null
+++ b/core/java/android/tv/TvStreamConfig.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+parcelable TvStreamConfig;
\ No newline at end of file
diff --git a/core/java/android/tv/TvStreamConfig.java b/core/java/android/tv/TvStreamConfig.java
new file mode 100644
index 0000000..03e63b1
--- /dev/null
+++ b/core/java/android/tv/TvStreamConfig.java
@@ -0,0 +1,157 @@
+/*
+ * 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.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TvStreamConfig implements Parcelable {
+    static final String TAG = TvStreamConfig.class.getSimpleName();
+
+    public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
+    public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
+
+    private int mStreamId;
+    private int mType;
+    // TODO: Revisit if max widht/height really make sense.
+    private int mMaxWidth;
+    private int mMaxHeight;
+    /**
+     * Generations are incremented once framework receives STREAM_CONFIGURATION_CHANGED event from
+     * HAL module. Framework should throw away outdated configurations and get new configurations
+     * via tv_input_device::get_stream_configurations().
+     */
+    private int mGeneration;
+
+    public static final Parcelable.Creator<TvStreamConfig> CREATOR =
+            new Parcelable.Creator<TvStreamConfig>() {
+        @Override
+        public TvStreamConfig createFromParcel(Parcel source) {
+            try {
+                return new Builder().
+                        streamId(source.readInt()).
+                        type(source.readInt()).
+                        maxWidth(source.readInt()).
+                        maxHeight(source.readInt()).
+                        generation(source.readInt()).build();
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public TvStreamConfig[] newArray(int size) {
+            return new TvStreamConfig[size];
+        }
+    };
+
+    private TvStreamConfig() {}
+
+    public int getStreamId() {
+        return mStreamId;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public int getMaxWidth() {
+        return mMaxWidth;
+    }
+
+    public int getMaxHeight() {
+        return mMaxHeight;
+    }
+
+    public int getGeneration() {
+        return mGeneration;
+    }
+
+    // Parcelable
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mStreamId);
+        dest.writeInt(mType);
+        dest.writeInt(mMaxWidth);
+        dest.writeInt(mMaxHeight);
+        dest.writeInt(mGeneration);
+    }
+
+    /**
+     * A helper class for creating a TvStreamConfig object.
+     */
+    public static final class Builder {
+        private Integer mStreamId;
+        private Integer mType;
+        private Integer mMaxWidth;
+        private Integer mMaxHeight;
+        private Integer mGeneration;
+
+        public Builder() {
+        }
+
+        public Builder streamId(int streamId) {
+            mStreamId = streamId;
+            return this;
+        }
+
+        public Builder type(int type) {
+            mType = type;
+            return this;
+        }
+
+        public Builder maxWidth(int maxWidth) {
+            mMaxWidth = maxWidth;
+            return this;
+        }
+
+        public Builder maxHeight(int maxHeight) {
+            mMaxHeight = maxHeight;
+            return this;
+        }
+
+        public Builder generation(int generation) {
+            mGeneration = generation;
+            return this;
+        }
+
+        public TvStreamConfig build() {
+            if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
+                    || mGeneration == null) {
+                throw new UnsupportedOperationException();
+            }
+
+            TvStreamConfig config = new TvStreamConfig();
+            config.mStreamId = mStreamId;
+            config.mType = mType;
+            config.mMaxWidth = mMaxWidth;
+            config.mMaxHeight = mMaxHeight;
+            config.mGeneration = mGeneration;
+            return config;
+        }
+    }
+}
\ No newline at end of file
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/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 6dd7c00..64a4c41 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -61,6 +61,7 @@
 
 import com.google.android.gles_jni.EGLImpl;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -106,7 +107,6 @@
 
     private static final String[] VISUALIZERS = {
             PROFILE_PROPERTY_VISUALIZE_BARS,
-            PROFILE_PROPERTY_VISUALIZE_LINES
     };
 
     private static final String[] OVERDRAW = {
@@ -674,7 +674,7 @@
             mProfilePaint = null;
 
             if (value) {
-                mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
+                mDebugDataProvider = new GraphDataProvider(graphType);
             } else {
                 mDebugDataProvider = null;
             }
@@ -742,7 +742,7 @@
     }
 
     @Override
-    void dumpGfxInfo(PrintWriter pw) {
+    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
         if (mProfileEnabled) {
             pw.printf("\n\tDraw\tProcess\tExecute\n");
 
@@ -763,11 +763,6 @@
         }
     }
 
-    @Override
-    long getFrameCount() {
-        return mFrameCount;
-    }
-
     /**
      * Indicates whether this renderer instance can track and update dirty regions.
      */
@@ -1446,7 +1441,18 @@
 
     private static native void nPrepareTree(long displayListPtr);
 
-    class DrawPerformanceDataProvider extends GraphDataProvider {
+    class GraphDataProvider {
+        /**
+         * Draws the graph as bars. Frame elements are stacked on top of
+         * each other.
+         */
+        public static final int GRAPH_TYPE_BARS = 0;
+        /**
+         * Draws the graph as lines. The number of series drawn corresponds
+         * to the number of elements.
+         */
+        public static final int GRAPH_TYPE_LINES = 1;
+
         private final int mGraphType;
 
         private int mVerticalUnit;
@@ -1454,11 +1460,10 @@
         private int mHorizontalMargin;
         private int mThresholdStroke;
 
-        DrawPerformanceDataProvider(int graphType) {
+        public GraphDataProvider(int graphType) {
             mGraphType = graphType;
         }
 
-        @Override
         void prepare(DisplayMetrics metrics) {
             final float density = metrics.density;
 
@@ -1468,64 +1473,52 @@
             mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
         }
 
-        @Override
         int getGraphType() {
             return mGraphType;
         }
 
-        @Override
         int getVerticalUnitSize() {
             return mVerticalUnit;
         }
 
-        @Override
         int getHorizontalUnitSize() {
             return mHorizontalUnit;
         }
 
-        @Override
         int getHorizontaUnitMargin() {
             return mHorizontalMargin;
         }
 
-        @Override
         float[] getData() {
             return mProfileData;
         }
 
-        @Override
         float getThreshold() {
             return 16;
         }
 
-        @Override
         int getFrameCount() {
             return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
         }
 
-        @Override
         int getElementCount() {
             return PROFILE_FRAME_DATA_COUNT;
         }
 
-        @Override
         int getCurrentFrame() {
             return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
         }
 
-        @Override
         void setupGraphPaint(Paint paint, int elementIndex) {
             paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
             if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
         }
 
-        @Override
         void setupThresholdPaint(Paint paint) {
             paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
             paint.setStrokeWidth(mThresholdStroke);
         }
 
-        @Override
         void setupCurrentFramePaint(Paint paint) {
             paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
             if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index a902ce7..60f8ee3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,13 +17,13 @@
 package android.view;
 
 import android.graphics.Bitmap;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.util.DisplayMetrics;
 import android.view.Surface.OutOfResourcesException;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -61,11 +61,9 @@
      * Possible values:
      * "true", to enable profiling
      * "visual_bars", to enable profiling and visualize the results on screen
-     * "visual_lines", to enable profiling and visualize the results on screen
      * "false", to disable profiling
      *
      * @see #PROFILE_PROPERTY_VISUALIZE_BARS
-     * @see #PROFILE_PROPERTY_VISUALIZE_LINES
      *
      * @hide
      */
@@ -80,14 +78,6 @@
     public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
 
     /**
-     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
-     * value, profiling data will be visualized on screen as a line chart.
-     *
-     * @hide
-     */
-    public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
-
-    /**
      * System property used to specify the number of frames to be used
      * when doing hardware rendering profiling.
      * The default value of this property is #PROFILE_MAX_FRAMES.
@@ -298,16 +288,8 @@
 
     /**
      * Outputs extra debugging information in the specified file descriptor.
-     * @param pw
      */
-    abstract void dumpGfxInfo(PrintWriter pw);
-
-    /**
-     * Outputs the total number of frames rendered (used for fps calculations)
-     *
-     * @return the number of frames rendered
-     */
-    abstract long getFrameCount();
+    abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd);
 
     /**
      * Loads system properties used by the renderer. This method is invoked
@@ -579,96 +561,8 @@
     abstract void fence();
 
     /**
-     * Describes a series of frames that should be drawn on screen as a graph.
-     * Each frame is composed of 1 or more elements.
+     * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
      */
-    abstract class GraphDataProvider {
-        /**
-         * Draws the graph as bars. Frame elements are stacked on top of
-         * each other.
-         */
-        public static final int GRAPH_TYPE_BARS = 0;
-        /**
-         * Draws the graph as lines. The number of series drawn corresponds
-         * to the number of elements.
-         */
-        public static final int GRAPH_TYPE_LINES = 1;
-
-        /**
-         * Returns the type of graph to render.
-         *
-         * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
-         */
-        abstract int getGraphType();
-
-        /**
-         * This method is invoked before the graph is drawn. This method
-         * can be used to compute sizes, etc.
-         *
-         * @param metrics The display metrics
-         */
-        abstract void prepare(DisplayMetrics metrics);
-
-        /**
-         * @return The size in pixels of a vertical unit.
-         */
-        abstract int getVerticalUnitSize();
-
-        /**
-         * @return The size in pixels of a horizontal unit.
-         */
-        abstract int getHorizontalUnitSize();
-
-        /**
-         * @return The size in pixels of the margin between horizontal units.
-         */
-        abstract int getHorizontaUnitMargin();
-
-        /**
-         * An optional threshold value.
-         *
-         * @return A value >= 0 to draw the threshold, a negative value
-         *         to ignore it.
-         */
-        abstract float getThreshold();
-
-        /**
-         * The data to draw in the graph. The number of elements in the
-         * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
-         * If a value is negative the following values will be ignored.
-         */
-        abstract float[] getData();
-
-        /**
-         * Returns the number of frames to render in the graph.
-         */
-        abstract int getFrameCount();
-
-        /**
-         * Returns the number of elements in each frame. This directly affects
-         * the number of series drawn in the graph.
-         */
-        abstract int getElementCount();
-
-        /**
-         * Returns the current frame, if any. If the returned value is negative
-         * the current frame is ignored.
-         */
-        abstract int getCurrentFrame();
-
-        /**
-         * Prepares the paint to draw the specified element (or series.)
-         */
-        abstract void setupGraphPaint(Paint paint, int elementIndex);
-
-        /**
-         * Prepares the paint to draw the threshold.
-         */
-        abstract void setupThresholdPaint(Paint paint);
-
-        /**
-         * Prepares the paint to draw the current frame indicator.
-         */
-        abstract void setupCurrentFramePaint(Paint paint);
+    public void notifyFramePending() {
     }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7d13399..af16185 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -79,7 +79,7 @@
     void removeWindowToken(IBinder token);
     void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges);
+            int configChanges, boolean voiceInteraction);
     void setAppGroupId(IBinder token, int groupId);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
@@ -120,6 +120,7 @@
     boolean isKeyguardSecure();
     boolean inKeyguardRestrictedInputMode();
     void dismissKeyguard();
+    void keyguardGoingAway();
 
     void closeSystemDialogs(String reason);
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index cf125bc..e63829e 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -850,6 +850,13 @@
         nOutput(mNativeRenderNode);
     }
 
+    /**
+     * Gets the size of the DisplayList for debug purposes.
+     */
+    public int getDebugSize() {
+        return nGetDebugSize(mNativeRenderNode);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Animations
     ///////////////////////////////////////////////////////////////////////////
@@ -941,6 +948,7 @@
     private static native float nGetPivotX(long renderNode);
     private static native float nGetPivotY(long renderNode);
     private static native void nOutput(long renderNode);
+    private static native int nGetDebugSize(long renderNode);
 
     ///////////////////////////////////////////////////////////////////////////
     // Animations
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 704d516..cac23a8 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -22,19 +22,19 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
 import android.util.TimeUtils;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
  * Hardware renderer that proxies the rendering to a render thread. Most calls
  * are currently synchronous.
- * TODO: Make draw() async.
- * TODO: Figure out how to share the DisplayList between two threads (global lock?)
  *
  * The UI thread can block on the RenderThread, but RenderThread must never
  * block on the UI thread.
@@ -62,11 +62,16 @@
     // Needs a ViewRoot invalidate
     private static final int SYNC_INVALIDATE_REQUIRED = 0x1;
 
+    private static final String[] VISUALIZERS = {
+        PROFILE_PROPERTY_VISUALIZE_BARS,
+    };
+
     private int mWidth, mHeight;
     private long mNativeProxy;
     private boolean mInitialized = false;
     private RenderNode mRootNode;
     private Choreographer mChoreographer;
+    private boolean mProfilingEnabled;
 
     ThreadedRenderer(boolean translucent) {
         AtlasInitializer.sInstance.init();
@@ -79,6 +84,8 @@
         // Setup timing
         mChoreographer = Choreographer.getInstance();
         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
+
+        loadSystemProperties();
     }
 
     @Override
@@ -117,7 +124,7 @@
     @Override
     void destroyHardwareResources(View view) {
         destroyResources(view);
-        // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+        nFlushCaches(mNativeProxy, GLES20Canvas.FLUSH_CACHES_LAYERS);
     }
 
     private static void destroyResources(View view) {
@@ -168,19 +175,33 @@
     }
 
     @Override
-    void dumpGfxInfo(PrintWriter pw) {
-        // TODO Auto-generated method stub
+    void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
+        pw.flush();
+        nDumpProfileInfo(mNativeProxy, fd);
     }
 
-    @Override
-    long getFrameCount() {
-        // TODO Auto-generated method stub
-        return 0;
+    private static int search(String[] values, String value) {
+        for (int i = 0; i < values.length; i++) {
+            if (values[i].equals(value)) return i;
+        }
+        return -1;
+    }
+
+    private static boolean checkIfProfilingRequested() {
+        String profiling = SystemProperties.get(HardwareRenderer.PROFILE_PROPERTY);
+        int graphType = search(VISUALIZERS, profiling);
+        return (graphType >= 0) || Boolean.parseBoolean(profiling);
     }
 
     @Override
     boolean loadSystemProperties() {
-        return nLoadSystemProperties(mNativeProxy);
+        boolean changed = nLoadSystemProperties(mNativeProxy);
+        boolean wantProfiling = checkIfProfilingRequested();
+        if (wantProfiling != mProfilingEnabled) {
+            mProfilingEnabled = wantProfiling;
+            changed = true;
+        }
+        return changed;
     }
 
     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
@@ -212,14 +233,24 @@
         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
         attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
 
+        long recordDuration = 0;
+        if (mProfilingEnabled) {
+            recordDuration = System.nanoTime();
+        }
+
         updateRootDisplayList(view, callbacks);
 
+        if (mProfilingEnabled) {
+            recordDuration = System.nanoTime() - recordDuration;
+        }
+
         attachInfo.mIgnoreDirtyState = false;
 
         if (dirty == null) {
             dirty = NULL_RECT;
         }
         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
+                recordDuration, view.getResources().getDisplayMetrics().density,
                 dirty.left, dirty.top, dirty.right, dirty.bottom);
         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
             attachInfo.mViewRootImpl.invalidate();
@@ -291,6 +322,11 @@
     }
 
     @Override
+    public void notifyFramePending() {
+        nNotifyFramePending(mNativeProxy);
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         try {
             nDeleteProxy(mNativeProxy);
@@ -351,7 +387,8 @@
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightX, float lightY, float lightZ, float lightRadius);
     private static native void nSetOpaque(long nativeProxy, boolean opaque);
-    private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
+    private static native int nSyncAndDrawFrame(long nativeProxy,
+            long frameTimeNanos, long recordDuration, float density,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
@@ -363,5 +400,10 @@
     private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
     private static native void nDestroyLayer(long nativeProxy, long layer);
 
+    private static native void nFlushCaches(long nativeProxy, int flushMode);
+
     private static native void nFence(long nativeProxy);
+    private static native void nNotifyFramePending(long nativeProxy);
+
+    private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0f21c1d..025cf69 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10687,8 +10687,8 @@
      * {@link Drawable#getOutline(Outline)}. Manually setting the Outline with this method allows
      * this behavior to be overridden.
      * <p>
-     * If the outline is empty or is null, shadows will be cast from the
-     * bounds of the View.
+     * If the outline is {@link Outline#isEmpty()} or is <code>null</code>,
+     * shadows will not be cast.
      * <p>
      * Only outlines that return true from {@link Outline#canClip()} may be used for clipping.
      *
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b821a3e..0f40ee7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -456,6 +456,10 @@
     // views during a transition when they otherwise would have become gone/invisible
     private ArrayList<View> mVisibilityChangingChildren;
 
+    // Temporary holder of presorted children, only used for
+    // input/software draw dispatch for correctly Z ordering.
+    private ArrayList<View> mPreSortedChildren;
+
     // Indicates how many of this container's child subtrees contain transient state
     @ViewDebug.ExportedProperty(category = "layout")
     private int mChildCountWithTransientState = 0;
@@ -1499,13 +1503,15 @@
             final float y = event.getY();
             final int childrenCount = mChildrenCount;
             if (childrenCount != 0) {
-                final boolean customChildOrder = isChildrenDrawingOrderEnabled();
+                final ArrayList<View> preorderedList = buildOrderedChildList();
+                final boolean customOrder = preorderedList == null
+                        && isChildrenDrawingOrderEnabled();
                 final View[] children = mChildren;
                 HoverTarget lastHoverTarget = null;
                 for (int i = childrenCount - 1; i >= 0; i--) {
-                    final int childIndex = customChildOrder
-                            ? getChildDrawingOrder(childrenCount, i) : i;
-                    final View child = children[childIndex];
+                    int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+                    final View child = (preorderedList == null)
+                            ? children[childIndex] : preorderedList.get(childIndex);
                     if (!canViewReceivePointerEvents(child)
                             || !isTransformedTouchPointInView(x, y, child, null)) {
                         continue;
@@ -1572,6 +1578,7 @@
                         break;
                     }
                 }
+                if (preorderedList != null) preorderedList.clear();
             }
         }
 
@@ -1778,23 +1785,28 @@
         // Send the event to the child under the pointer.
         final int childrenCount = mChildrenCount;
         if (childrenCount != 0) {
-            final View[] children = mChildren;
             final float x = event.getX();
             final float y = event.getY();
 
-            final boolean customOrder = isChildrenDrawingOrderEnabled();
+            final ArrayList<View> preorderedList = buildOrderedChildList();
+            final boolean customOrder = preorderedList == null
+                    && isChildrenDrawingOrderEnabled();
+            final View[] children = mChildren;
             for (int i = childrenCount - 1; i >= 0; i--) {
-                final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
-                final View child = children[childIndex];
+                int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+                final View child = (preorderedList == null)
+                        ? children[childIndex] : preorderedList.get(childIndex);
                 if (!canViewReceivePointerEvents(child)
                         || !isTransformedTouchPointInView(x, y, child, null)) {
                     continue;
                 }
 
                 if (dispatchTransformedGenericPointerEvent(event, child)) {
+                    if (preorderedList != null) preorderedList.clear();
                     return true;
                 }
             }
+            if (preorderedList != null) preorderedList.clear();
         }
 
         // No child handled the event.  Send it to this view group.
@@ -1910,13 +1922,15 @@
                         final float y = ev.getY(actionIndex);
                         // Find a child that can receive the event.
                         // Scan children from front to back.
+                        final ArrayList<View> preorderedList = buildOrderedChildList();
+                        final boolean customOrder = preorderedList == null
+                                && isChildrenDrawingOrderEnabled();
                         final View[] children = mChildren;
-
-                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                         for (int i = childrenCount - 1; i >= 0; i--) {
-                            final int childIndex = customOrder ?
-                                    getChildDrawingOrder(childrenCount, i) : i;
-                            final View child = children[childIndex];
+                            final int childIndex = customOrder
+                                    ? getChildDrawingOrder(childrenCount, i) : i;
+                            final View child = (preorderedList == null)
+                                    ? children[childIndex] : preorderedList.get(childIndex);
                             if (!canViewReceivePointerEvents(child)
                                     || !isTransformedTouchPointInView(x, y, child, null)) {
                                 continue;
@@ -1934,7 +1948,17 @@
                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                 // Child wants to receive touch within its bounds.
                                 mLastTouchDownTime = ev.getDownTime();
-                                mLastTouchDownIndex = childIndex;
+                                if (preorderedList != null) {
+                                    // childIndex points into presorted list, find original index
+                                    for (int j = 0; j < childrenCount; j++) {
+                                        if (children[childIndex] == mChildren[j]) {
+                                            mLastTouchDownIndex = j;
+                                            break;
+                                        }
+                                    }
+                                } else {
+                                    mLastTouchDownIndex = childIndex;
+                                }
                                 mLastTouchDownX = ev.getX();
                                 mLastTouchDownY = ev.getY();
                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
@@ -1942,6 +1966,7 @@
                                 break;
                             }
                         }
+                        if (preorderedList != null) preorderedList.clear();
                     }
 
                     if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -2928,7 +2953,7 @@
      */
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        final int count = mChildrenCount;
+        final int childrenCount = mChildrenCount;
         final View[] children = mChildren;
         int flags = mGroupFlags;
 
@@ -2936,15 +2961,15 @@
             final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
 
             final boolean buildCache = !isHardwareAccelerated();
-            for (int i = 0; i < count; i++) {
+            for (int i = 0; i < childrenCount; i++) {
                 final View child = children[i];
                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                     final LayoutParams params = child.getLayoutParams();
-                    attachLayoutAnimationParameters(child, params, i, count);
+                    attachLayoutAnimationParameters(child, params, i, childrenCount);
                     bindLayoutAnimation(child);
                     if (cache) {
                         child.setDrawingCacheEnabled(true);
-                        if (buildCache) {                        
+                        if (buildCache) {
                             child.buildDrawingCache(true);
                         }
                     }
@@ -2997,21 +3022,22 @@
         boolean more = false;
         final long drawingTime = getDrawingTime();
 
-        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
-            for (int i = 0; i < count; i++) {
-                final View child = children[i];
-                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
-                    more |= drawChild(canvas, child, drawingTime);
-                }
-            }
-        } else {
-            for (int i = 0; i < count; i++) {
-                final View child = children[getChildDrawingOrder(count, i)];
-                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
-                    more |= drawChild(canvas, child, drawingTime);
-                }
+
+        // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
+        // draw reordering internally
+        final ArrayList<View> preorderedList = canvas.isHardwareAccelerated()
+                ? null : buildOrderedChildList();
+        final boolean customOrder = preorderedList == null
+                && isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < childrenCount; i++) {
+            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
+            final View child = (preorderedList == null)
+                    ? children[childIndex] : preorderedList.get(childIndex);
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
+                more |= drawChild(canvas, child, drawingTime);
             }
         }
+        if (preorderedList != null) preorderedList.clear();
 
         // Draw any disappearing views that have animations
         if (mDisappearingChildren != null) {
@@ -3096,6 +3122,47 @@
         return i;
     }
 
+    private boolean hasChildWithZ() {
+        for (int i = 0; i < mChildrenCount; i++) {
+            if (mChildren[i].getZ() != 0) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
+     * sorted first by Z, then by child drawing order (if applicable).
+     *
+     * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
+     * children.
+     */
+    private ArrayList<View> buildOrderedChildList() {
+        final int count = mChildrenCount;
+        if (count <= 1 || !hasChildWithZ()) return null;
+
+        if (mPreSortedChildren == null) {
+            mPreSortedChildren = new ArrayList<View>(count);
+        } else {
+            mPreSortedChildren.ensureCapacity(count);
+        }
+
+        final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < mChildrenCount; i++) {
+            // add next child (in child order) to end of list
+            int childIndex = useCustomOrder ? getChildDrawingOrder(mChildrenCount, i) : i;
+            View nextChild = mChildren[childIndex];
+            float currentZ = nextChild.getZ();
+
+            // insert ahead of any Views with greater Z
+            int insertIndex = i;
+            while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
+                insertIndex--;
+            }
+            mPreSortedChildren.add(insertIndex, nextChild);
+        }
+        return mPreSortedChildren;
+    }
+
     private void notifyAnimationListener() {
         mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
         mGroupFlags |= FLAG_ANIMATION_DONE;
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 3104862..af1de78 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -253,9 +253,10 @@
     ViewPropertyAnimator(View view) {
         mView = view;
         view.ensureTransformationInfo();
-        if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
-            mRTBackend = new ViewPropertyAnimatorRT(view);
-        }
+        // TODO: Disabled because of b/15287046
+        //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+        //    mRTBackend = new ViewPropertyAnimatorRT(view);
+        //}
     }
 
     /**
@@ -1142,7 +1143,8 @@
                 // Shouldn't happen, but just to play it safe
                 return;
             }
-            boolean useRenderNodeProperties = mView.mRenderNode != null;
+
+            boolean hardwareAccelerated = mView.isHardwareAccelerated();
 
             // alpha requires slightly different treatment than the other (transform) properties.
             // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
@@ -1150,13 +1152,13 @@
             // We track what kinds of properties are set, and how alpha is handled when it is
             // set, and perform the invalidation steps appropriately.
             boolean alphaHandled = false;
-            if (!useRenderNodeProperties) {
+            if (!hardwareAccelerated) {
                 mView.invalidateParentCaches();
             }
             float fraction = animation.getAnimatedFraction();
             int propertyMask = propertyBundle.mPropertyMask;
             if ((propertyMask & TRANSFORM_MASK) != 0) {
-                mView.invalidateViewProperty(false, false);
+                mView.invalidateViewProperty(hardwareAccelerated, false);
             }
             ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
             if (valueList != null) {
@@ -1172,7 +1174,7 @@
                 }
             }
             if ((propertyMask & TRANSFORM_MASK) != 0) {
-                if (!useRenderNodeProperties) {
+                if (!hardwareAccelerated) {
                     mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
                 }
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 799a406..aa06d15 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -999,6 +999,17 @@
         }
     }
 
+    /**
+     * Notifies the HardwareRenderer that a new frame will be coming soon.
+     * Currently only {@link ThreadedRenderer} cares about this, and uses
+     * this knowledge to adjust the scheduling of off-thread animations
+     */
+    void notifyRendererOfFramePending() {
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.notifyFramePending();
+        }
+    }
+
     void scheduleTraversals() {
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
@@ -1006,6 +1017,7 @@
             mChoreographer.postCallback(
                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
             scheduleConsumeBatchedInput();
+            notifyRendererOfFramePending();
         }
     }
 
@@ -5317,7 +5329,7 @@
         RenderNode renderNode = view.mRenderNode;
         info[0]++;
         if (renderNode != null) {
-            info[1] += 0; /* TODO: Memory used by RenderNodes (properties + DisplayLists) */
+            info[1] += renderNode.getDebugSize();
         }
 
         if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 031ad80..4eecc6a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -218,7 +218,8 @@
             @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL"),
             @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, to = "TYPE_DISPLAY_OVERLAY"),
             @ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY, to = "TYPE_MAGNIFICATION_OVERLAY"),
-            @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION")
+            @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION"),
+            @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"),
         })
         public int type;
     
@@ -541,6 +542,12 @@
         public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
 
         /**
+         * Window type: Windows in the voice interaction layer.
+         * @hide
+         */
+        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 96c0ed2..b4779f4 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -430,7 +430,7 @@
                     HardwareRenderer renderer =
                             root.getView().mAttachInfo.mHardwareRenderer;
                     if (renderer != null) {
-                        renderer.dumpGfxInfo(pw);
+                        renderer.dumpGfxInfo(pw, fd);
                     }
                 }
 
@@ -447,11 +447,6 @@
                     String name = getWindowName(root);
                     pw.printf("  %s\n  %d views, %.2f kB of display lists",
                             name, info[0], info[1] / 1024.0f);
-                    HardwareRenderer renderer =
-                            root.getView().mAttachInfo.mHardwareRenderer;
-                    if (renderer != null) {
-                        pw.printf(", %d frames rendered", renderer.getFrameCount());
-                    }
                     pw.printf("\n\n");
 
                     viewsCount += info[0];
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 4fde1e4..d45d686 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -274,6 +274,11 @@
         public IApplicationToken getAppToken();
 
         /**
+         * Return true if this window is participating in voice interaction.
+         */
+        public boolean isVoiceInteraction();
+
+        /**
          * Return true if, at any point, the application token associated with 
          * this window has actually displayed any windows.  This is most useful 
          * with the "starting up" window to determine if any windows were 
@@ -603,8 +608,15 @@
      * Return whether the given window should forcibly hide everything
      * behind it.  Typically returns true for the keyguard.
      */
-    public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs);
-    
+    public boolean doesForceHide(WindowManager.LayoutParams attrs);
+
+
+    /**
+     * Return whether the given window can become one that passes doesForceHide() test.
+     * Typically returns true for the StatusBar.
+     */
+    public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs);
+
     /**
      * Determine if a window that is behind one that is force hiding
      * (as determined by {@link #doesForceHide}) should actually be hidden.
@@ -613,7 +625,7 @@
      * will conflict with what you set.
      */
     public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs);
-    
+
     /**
      * Called when the system would like to show a UI to indicate that an
      * application is starting.  You can use this to add a
@@ -1184,4 +1196,9 @@
      * @return True if the window is a top level one.
      */
     public boolean isTopLevelWindow(int windowType);
+
+    /**
+     * Notifies the keyguard to start fading out.
+     */
+    public void startKeyguardExitAnimation(long fadeoutDuration);
 }
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 51759c5..1fddf3e 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -544,6 +544,7 @@
 
     public void setMenuView(ActionMenuView menuView) {
         mMenuView = menuView;
+        menuView.initialize(mMenu);
     }
 
     private static class SavedState implements Parcelable {
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index 3975edf..a9a5eae 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -69,6 +69,7 @@
     /** @hide */
     public void setPresenter(ActionMenuPresenter presenter) {
         mPresenter = presenter;
+        mPresenter.setMenuView(this);
     }
 
     @Override
@@ -488,7 +489,7 @@
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mPresenter.dismissPopupMenus();
+        dismissPopupMenus();
     }
 
     /** @hide */
@@ -578,6 +579,56 @@
     }
 
     /**
+     * Returns the current menu or null if one has not yet been configured.
+     * @hide Internal use only for action bar integration
+     */
+    public MenuBuilder peekMenu() {
+        return mMenu;
+    }
+
+    /**
+     * Show the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be shown, false otherwise
+     */
+    public boolean showOverflowMenu() {
+        return mPresenter != null && mPresenter.showOverflowMenu();
+    }
+
+    /**
+     * Hide the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be hidden, false otherwise
+     */
+    public boolean hideOverflowMenu() {
+        return mPresenter != null && mPresenter.hideOverflowMenu();
+    }
+
+    /**
+     * Check whether the overflow menu is currently showing. This may not reflect
+     * a pending show operation in progress.
+     *
+     * @return true if the overflow menu is currently showing
+     */
+    public boolean isOverflowMenuShowing() {
+        return mPresenter != null && mPresenter.isOverflowMenuShowing();
+    }
+
+    /** @hide */
+    public boolean isOverflowMenuShowPending() {
+        return mPresenter != null && mPresenter.isOverflowMenuShowPending();
+    }
+
+    /**
+     * Dismiss any popups associated with this menu view.
+     */
+    public void dismissPopupMenus() {
+        if (mPresenter != null) {
+            mPresenter.dismissPopupMenus();
+        }
+    }
+
+    /**
      * @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public.
      */
     @Override
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index f903346..5033bee 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -18,13 +18,17 @@
 package android.widget;
 
 import android.annotation.NonNull;
+import android.app.ActionBar;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.Layout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.CollapsibleActionView;
 import android.view.Gravity;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -32,7 +36,15 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
+import android.view.Window;
 import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuPresenter;
+import com.android.internal.view.menu.MenuView;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ToolbarWidgetWrapper;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -80,14 +92,25 @@
  * layout is discouraged on API 21 devices and newer.</p>
  */
 public class Toolbar extends ViewGroup {
+    private static final String TAG = "Toolbar";
+
     private ActionMenuView mMenuView;
     private TextView mTitleTextView;
     private TextView mSubtitleTextView;
     private ImageButton mNavButtonView;
     private ImageView mLogoView;
 
+    private Drawable mCollapseIcon;
+    private ImageButton mCollapseButtonView;
+    View mExpandedActionView;
+
     private int mTitleTextAppearance;
     private int mSubtitleTextAppearance;
+    private int mNavButtonStyle;
+
+    private int mButtonGravity;
+
+    private int mMaxButtonHeight;
 
     private int mTitleMarginStart;
     private int mTitleMarginEnd;
@@ -117,6 +140,10 @@
                 }
             };
 
+    private ToolbarWidgetWrapper mWrapper;
+    private ActionMenuPresenter mOuterActionMenuPresenter;
+    private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
+
     public Toolbar(Context context) {
         this(context, null);
     }
@@ -137,7 +164,9 @@
 
         mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
         mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
+        mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0);
         mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
+        mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
         mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
                 a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
 
@@ -162,6 +191,8 @@
             mTitleMarginBottom = marginBottom;
         }
 
+        mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1);
+
         final int contentInsetStart =
                 a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart,
                         RtlSpacingHelper.UNDEFINED);
@@ -180,6 +211,8 @@
             mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
         }
 
+        mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
+
         final CharSequence title = a.getText(R.styleable.Toolbar_title);
         if (!TextUtils.isEmpty(title)) {
             setTitle(title);
@@ -211,6 +244,110 @@
         setLogo(getContext().getDrawable(resId));
     }
 
+    /** @hide */
+    public boolean canShowOverflowMenu() {
+        return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved();
+    }
+
+    /**
+     * Check whether the overflow menu is currently showing. This may not reflect
+     * a pending show operation in progress.
+     *
+     * @return true if the overflow menu is currently showing
+     */
+    public boolean isOverflowMenuShowing() {
+        return mMenuView != null && mMenuView.isOverflowMenuShowing();
+    }
+
+    /** @hide */
+    public boolean isOverflowMenuShowPending() {
+        return mMenuView != null && mMenuView.isOverflowMenuShowPending();
+    }
+
+    /**
+     * Show the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be shown, false otherwise
+     */
+    public boolean showOverflowMenu() {
+        return mMenuView != null && mMenuView.showOverflowMenu();
+    }
+
+    /**
+     * Hide the overflow items from the associated menu.
+     *
+     * @return true if the menu was able to be hidden, false otherwise
+     */
+    public boolean hideOverflowMenu() {
+        return mMenuView != null && mMenuView.hideOverflowMenu();
+    }
+
+    /** @hide */
+    public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {
+        if (menu == null && mMenuView == null) {
+            return;
+        }
+
+        ensureMenuView();
+        final MenuBuilder oldMenu = mMenuView.peekMenu();
+        if (oldMenu == menu) {
+            return;
+        }
+
+        if (oldMenu != null) {
+            oldMenu.removeMenuPresenter(mOuterActionMenuPresenter);
+            oldMenu.removeMenuPresenter(mExpandedMenuPresenter);
+        }
+
+        final Context context = getContext();
+
+        if (mExpandedMenuPresenter == null) {
+            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
+        }
+
+        outerPresenter.setExpandedActionViewsExclusive(true);
+        if (menu != null) {
+            menu.addMenuPresenter(outerPresenter);
+            menu.addMenuPresenter(mExpandedMenuPresenter);
+        } else {
+            outerPresenter.initForMenu(context, null);
+            mExpandedMenuPresenter.initForMenu(context, null);
+            outerPresenter.updateMenuView(true);
+            mExpandedMenuPresenter.updateMenuView(true);
+        }
+        mMenuView.setPresenter(outerPresenter);
+        mOuterActionMenuPresenter = outerPresenter;
+    }
+
+    /**
+     * Dismiss all currently showing popup menus, including overflow or submenus.
+     */
+    public void dismissPopupMenus() {
+        if (mMenuView != null) {
+            mMenuView.dismissPopupMenus();
+        }
+    }
+
+    /** @hide */
+    public boolean isTitleTruncated() {
+        if (mTitleTextView == null) {
+            return false;
+        }
+
+        final Layout titleLayout = mTitleTextView.getLayout();
+        if (titleLayout == null) {
+            return false;
+        }
+
+        final int lineCount = titleLayout.getLineCount();
+        for (int i = 0; i < lineCount; i++) {
+            if (titleLayout.getEllipsisCount(i) > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Set a logo drawable.
      *
@@ -222,9 +359,7 @@
      */
     public void setLogo(Drawable drawable) {
         if (drawable != null) {
-            if (mLogoView == null) {
-                mLogoView = new ImageView(getContext());
-            }
+            ensureLogoView();
             if (mLogoView.getParent() == null) {
                 addSystemView(mLogoView);
             }
@@ -268,8 +403,8 @@
      * @param description Description to set
      */
     public void setLogoDescription(CharSequence description) {
-        if (!TextUtils.isEmpty(description) && mLogoView == null) {
-            mLogoView = new ImageView(getContext());
+        if (!TextUtils.isEmpty(description)) {
+            ensureLogoView();
         }
         if (mLogoView != null) {
             mLogoView.setContentDescription(description);
@@ -285,10 +420,48 @@
         return mLogoView != null ? mLogoView.getContentDescription() : null;
     }
 
+    private void ensureLogoView() {
+        if (mLogoView == null) {
+            mLogoView = new ImageView(getContext());
+        }
+    }
+
     /**
-     * Return the current title displayed in the toolbar.
+     * Check whether this Toolbar is currently hosting an expanded action view.
      *
-     * @return The current title
+     * <p>An action view may be expanded either directly from the
+     * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar
+     * has an expanded action view it can be collapsed using the {@link #collapseActionView()}
+     * method.</p>
+     *
+     * @return true if the Toolbar has an expanded action view
+     */
+    public boolean hasExpandedActionView() {
+        return mExpandedMenuPresenter != null &&
+                mExpandedMenuPresenter.mCurrentExpandedItem != null;
+    }
+
+    /**
+     * Collapse a currently expanded action view. If this Toolbar does not have an
+     * expanded action view this method has no effect.
+     *
+     * <p>An action view may be expanded either directly from the
+     * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p>
+     *
+     * @see #hasExpandedActionView()
+     */
+    public void collapseActionView() {
+        final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
+                mExpandedMenuPresenter.mCurrentExpandedItem;
+        if (item != null) {
+            item.collapseActionView();
+        }
+    }
+
+    /**
+     * Returns the title of this toolbar.
+     *
+     * @return The current title.
      */
     public CharSequence getTitle() {
         return mTitleText;
@@ -319,6 +492,8 @@
             if (mTitleTextView == null) {
                 final Context context = getContext();
                 mTitleTextView = new TextView(context);
+                mTitleTextView.setSingleLine();
+                mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
                 mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
             }
             if (mTitleTextView.getParent() == null) {
@@ -365,6 +540,8 @@
             if (mSubtitleTextView == null) {
                 final Context context = getContext();
                 mSubtitleTextView = new TextView(context);
+                mSubtitleTextView.setSingleLine();
+                mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
                 mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
             }
             if (mSubtitleTextView.getParent() == null) {
@@ -395,6 +572,30 @@
     }
 
     /**
+     * Set a content description for the navigation button if one is present. The content
+     * description will be read via screen readers or other accessibility systems to explain
+     * the action of the navigation button.
+     *
+     * @param description Content description to set
+     */
+    public void setNavigationContentDescription(CharSequence description) {
+        ensureNavButtonView();
+        mNavButtonView.setContentDescription(description);
+    }
+
+    /**
+     * Set a content description for the navigation button if one is present. The content
+     * description will be read via screen readers or other accessibility systems to explain
+     * the action of the navigation button.
+     *
+     * @param resId Resource ID of a content description string to set
+     */
+    public void setNavigationContentDescription(int resId) {
+        ensureNavButtonView();
+        mNavButtonView.setContentDescription(resId != 0 ? getContext().getText(resId) : null);
+    }
+
+    /**
      * Set the icon to use for the toolbar's navigation button.
      *
      * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
@@ -480,12 +681,19 @@
      * @return The toolbar's Menu
      */
     public Menu getMenu() {
+        ensureMenuView();
+        return mMenuView.getMenu();
+    }
+
+    private void ensureMenuView() {
         if (mMenuView == null) {
             mMenuView = new ActionMenuView(getContext());
             mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
+            final LayoutParams lp = generateDefaultLayoutParams();
+            lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+            mMenuView.setLayoutParams(lp);
             addSystemView(mMenuView);
         }
-        return mMenuView.getMenu();
     }
 
     private MenuInflater getMenuInflater() {
@@ -634,7 +842,27 @@
 
     private void ensureNavButtonView() {
         if (mNavButtonView == null) {
-            mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle);
+            mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
+            final LayoutParams lp = generateDefaultLayoutParams();
+            lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+            mNavButtonView.setLayoutParams(lp);
+        }
+    }
+
+    private void ensureCollapseButtonView() {
+        if (mCollapseButtonView == null) {
+            mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
+            mCollapseButtonView.setImageDrawable(mCollapseIcon);
+            final LayoutParams lp = generateDefaultLayoutParams();
+            lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+            lp.mViewType = LayoutParams.EXPANDED;
+            mCollapseButtonView.setLayoutParams(lp);
+            mCollapseButtonView.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    collapseActionView();
+                }
+            });
         }
     }
 
@@ -657,6 +885,27 @@
         super.onRestoreInstanceState(ss.getSuperState());
     }
 
+    private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed,
+            int parentHeightSpec, int heightUsed, int heightConstraint) {
+        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
+                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+                        + widthUsed, lp.width);
+        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
+                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+                        + heightUsed, lp.height);
+
+        final int childHeightMode = MeasureSpec.getMode(childHeightSpec);
+        if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) {
+            final int size = childHeightMode != MeasureSpec.UNSPECIFIED ?
+                    Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) :
+                    heightConstraint;
+            childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+        }
+        child.measure(childWidthSpec, childHeightSpec);
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = 0;
@@ -667,18 +916,30 @@
 
         int navWidth = 0;
         if (shouldLayout(mNavButtonView)) {
-            measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0);
+            measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0,
+                    mMaxButtonHeight);
             navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
             height = Math.max(height, mNavButtonView.getMeasuredHeight() +
                     getVerticalMargins(mNavButtonView));
             childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState());
         }
 
+        if (shouldLayout(mCollapseButtonView)) {
+            measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width,
+                    heightMeasureSpec, 0, mMaxButtonHeight);
+            navWidth = mCollapseButtonView.getMeasuredWidth() +
+                    getHorizontalMargins(mCollapseButtonView);
+            height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
+                    getVerticalMargins(mCollapseButtonView));
+            childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
+        }
+
         width += Math.max(getContentInsetStart(), navWidth);
 
         int menuWidth = 0;
         if (shouldLayout(mMenuView)) {
-            measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0);
+            measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0,
+                    mMaxButtonHeight);
             menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
             height = Math.max(height, mMenuView.getMeasuredHeight() +
                     getVerticalMargins(mMenuView));
@@ -687,6 +948,16 @@
 
         width += Math.max(getContentInsetEnd(), menuWidth);
 
+        if (shouldLayout(mExpandedActionView)) {
+            measureChildWithMargins(mExpandedActionView, widthMeasureSpec, width,
+                    heightMeasureSpec, 0);
+            width += mExpandedActionView.getMeasuredWidth() +
+                    getHorizontalMargins(mExpandedActionView);
+            height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
+                    getVerticalMargins(mExpandedActionView));
+            childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState());
+        }
+
         if (shouldLayout(mLogoView)) {
             measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0);
             width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView);
@@ -723,7 +994,7 @@
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) {
+            if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) {
                 // We already got all system views above. Skip them and GONE views.
                 continue;
             }
@@ -768,6 +1039,14 @@
             }
         }
 
+        if (shouldLayout(mCollapseButtonView)) {
+            if (isRtl) {
+                right = layoutChildRight(mCollapseButtonView, right);
+            } else {
+                left = layoutChildLeft(mCollapseButtonView, left);
+            }
+        }
+
         if (shouldLayout(mMenuView)) {
             if (isRtl) {
                 left = layoutChildLeft(mMenuView, left);
@@ -779,6 +1058,14 @@
         left = Math.max(left, getContentInsetLeft());
         right = Math.min(right, width - paddingRight - getContentInsetRight());
 
+        if (shouldLayout(mExpandedActionView)) {
+            if (isRtl) {
+                right = layoutChildRight(mExpandedActionView, right);
+            } else {
+                left = layoutChildLeft(mExpandedActionView, left);
+            }
+        }
+
         if (shouldLayout(mLogoView)) {
             if (isRtl) {
                 right = layoutChildRight(mLogoView, right);
@@ -801,40 +1088,42 @@
 
         if (layoutTitle || layoutSubtitle) {
             int titleTop;
+            final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView;
+            final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
+            final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
+            final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
+
             switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
                 case Gravity.TOP:
-                    titleTop = getPaddingTop();
+                    titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop;
                     break;
                 default:
                 case Gravity.CENTER_VERTICAL:
-                    final View child = layoutTitle ? mTitleTextView : mSubtitleTextView;
-                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                     final int space = height - paddingTop - paddingBottom;
                     int spaceAbove = (space - titleHeight) / 2;
-                    if (spaceAbove < lp.topMargin + mTitleMarginTop) {
-                        spaceAbove = lp.topMargin + mTitleMarginTop;
+                    if (spaceAbove < toplp.topMargin + mTitleMarginTop) {
+                        spaceAbove = toplp.topMargin + mTitleMarginTop;
                     } else {
                         final int spaceBelow = height - paddingBottom - titleHeight -
                                 spaceAbove - paddingTop;
-                        if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) {
+                        if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) {
                             spaceAbove = Math.max(0, spaceAbove -
-                                    (lp.bottomMargin + mTitleMarginBottom - spaceBelow));
+                                    (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow));
                         }
                     }
                     titleTop = paddingTop + spaceAbove;
                     break;
                 case Gravity.BOTTOM:
-                    titleTop = height - paddingBottom - titleHeight;
+                    titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom -
+                            titleHeight;
                     break;
             }
             if (isRtl) {
                 int titleRight = right;
                 int subtitleRight = right;
-                titleTop += mTitleMarginTop;
                 if (layoutTitle) {
                     final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
                     titleRight -= lp.rightMargin + mTitleMarginStart;
-                    titleTop += lp.topMargin;
                     final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
                     final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
                     mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
@@ -855,11 +1144,9 @@
             } else {
                 int titleLeft = left;
                 int subtitleLeft = left;
-                titleTop += mTitleMarginTop;
                 if (layoutTitle) {
                     final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
                     titleLeft += lp.leftMargin + mTitleMarginStart;
-                    titleTop += lp.topMargin;
                     final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
                     final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
                     mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
@@ -897,7 +1184,7 @@
 
         // Centered views try to center with respect to the whole bar, but views pinned
         // to the left or right can push the mass of centered views to one side or the other.
-        addCustomViewsWithGravity(mTempViews, Gravity.CENTER);
+        addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
         final int centerViewsWidth = getViewListMeasuredWidth(mTempViews);
         final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
         final int halfCenterViewsWidth = centerViewsWidth / 2;
@@ -1007,17 +1294,16 @@
             for (int i = childCount - 1; i >= 0; i--) {
                 final View child = getChildAt(i);
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) &&
+                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
                         getChildHorizontalGravity(lp.gravity) == absGrav) {
                     views.add(child);
                 }
-
             }
         } else {
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) &&
+                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
                         getChildHorizontalGravity(lp.gravity) == absGrav) {
                     views.add(child);
                 }
@@ -1054,14 +1340,16 @@
     }
 
     @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return super.generateLayoutParams(attrs);
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
     }
 
     @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
         if (p instanceof LayoutParams) {
             return new LayoutParams((LayoutParams) p);
+        } else if (p instanceof ActionBar.LayoutParams) {
+            return new LayoutParams((ActionBar.LayoutParams) p);
         } else if (p instanceof MarginLayoutParams) {
             return new LayoutParams((MarginLayoutParams) p);
         } else {
@@ -1070,7 +1358,7 @@
     }
 
     @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+    protected LayoutParams generateDefaultLayoutParams() {
         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
     }
 
@@ -1083,6 +1371,25 @@
         return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
     }
 
+    /** @hide */
+    public DecorToolbar getWrapper() {
+        if (mWrapper == null) {
+            mWrapper = new ToolbarWidgetWrapper(this);
+        }
+        return mWrapper;
+    }
+
+    private void setChildVisibilityForExpandedActionView(boolean expand) {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
+                child.setVisibility(expand ? GONE : VISIBLE);
+            }
+        }
+    }
+
     /**
      * Interface responsible for receiving menu item click events if the items themselves
      * do not have individual item click listeners.
@@ -1103,44 +1410,15 @@
      *
      * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
      */
-    public static class LayoutParams extends MarginLayoutParams {
-        /**
-         * Gravity for the view associated with these LayoutParams.
-         *
-         * @see android.view.Gravity
-         */
-        @ViewDebug.ExportedProperty(category = "layout", mapping = {
-                @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
-                @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
-                @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
-                @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
-                @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
-                @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
-                @ViewDebug.IntToString(from = Gravity.START,             to = "START"),
-                @ViewDebug.IntToString(from = Gravity.END,               to = "END"),
-                @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
-                @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
-                @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
-                @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
-                @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
-                @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
-        })
-        public int gravity = Gravity.NO_GRAVITY;
-
+    public static class LayoutParams extends ActionBar.LayoutParams {
         static final int CUSTOM = 0;
         static final int SYSTEM = 1;
+        static final int EXPANDED = 2;
 
         int mViewType = CUSTOM;
 
         public LayoutParams(@NonNull Context c, AttributeSet attrs) {
             super(c, attrs);
-
-            TypedArray a = c.obtainStyledAttributes(attrs,
-                    com.android.internal.R.styleable.Toolbar_LayoutParams);
-            gravity = a.getInt(
-                    com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity,
-                    Gravity.NO_GRAVITY);
-            a.recycle();
         }
 
         public LayoutParams(int width, int height) {
@@ -1160,7 +1438,11 @@
         public LayoutParams(LayoutParams source) {
             super(source);
 
-            this.gravity = source.gravity;
+            mViewType = source.mViewType;
+        }
+
+        public LayoutParams(ActionBar.LayoutParams source) {
+            super(source);
         }
 
         public LayoutParams(MarginLayoutParams source) {
@@ -1199,4 +1481,126 @@
             }
         };
     }
+
+    private class ExpandedActionViewMenuPresenter implements MenuPresenter {
+        MenuBuilder mMenu;
+        MenuItemImpl mCurrentExpandedItem;
+
+        @Override
+        public void initForMenu(Context context, MenuBuilder menu) {
+            // Clear the expanded action view when menus change.
+            if (mMenu != null && mCurrentExpandedItem != null) {
+                mMenu.collapseItemActionView(mCurrentExpandedItem);
+            }
+            mMenu = menu;
+        }
+
+        @Override
+        public MenuView getMenuView(ViewGroup root) {
+            return null;
+        }
+
+        @Override
+        public void updateMenuView(boolean cleared) {
+            // Make sure the expanded item we have is still there.
+            if (mCurrentExpandedItem != null) {
+                boolean found = false;
+
+                if (mMenu != null) {
+                    final int count = mMenu.size();
+                    for (int i = 0; i < count; i++) {
+                        final MenuItem item = mMenu.getItem(i);
+                        if (item == mCurrentExpandedItem) {
+                            found = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (!found) {
+                    // The item we had expanded disappeared. Collapse.
+                    collapseItemActionView(mMenu, mCurrentExpandedItem);
+                }
+            }
+        }
+
+        @Override
+        public void setCallback(Callback cb) {
+        }
+
+        @Override
+        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+            return false;
+        }
+
+        @Override
+        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+        }
+
+        @Override
+        public boolean flagActionItems() {
+            return false;
+        }
+
+        @Override
+        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+            ensureCollapseButtonView();
+            if (mCollapseButtonView.getParent() != Toolbar.this) {
+                addView(mCollapseButtonView);
+            }
+            mExpandedActionView = item.getActionView();
+            mCurrentExpandedItem = item;
+            if (mExpandedActionView.getParent() != Toolbar.this) {
+                final LayoutParams lp = generateDefaultLayoutParams();
+                lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
+                lp.mViewType = LayoutParams.EXPANDED;
+                mExpandedActionView.setLayoutParams(lp);
+                addView(mExpandedActionView);
+            }
+
+            setChildVisibilityForExpandedActionView(true);
+            requestLayout();
+            item.setActionViewExpanded(true);
+
+            if (mExpandedActionView instanceof CollapsibleActionView) {
+                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
+            }
+
+            return true;
+        }
+
+        @Override
+        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+            // Do this before detaching the actionview from the hierarchy, in case
+            // it needs to dismiss the soft keyboard, etc.
+            if (mExpandedActionView instanceof CollapsibleActionView) {
+                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
+            }
+
+            removeView(mExpandedActionView);
+            removeView(mCollapseButtonView);
+            mExpandedActionView = null;
+
+            setChildVisibilityForExpandedActionView(false);
+            mCurrentExpandedItem = null;
+            requestLayout();
+            item.setActionViewExpanded(false);
+
+            return true;
+        }
+
+        @Override
+        public int getId() {
+            return 0;
+        }
+
+        @Override
+        public Parcelable onSaveInstanceState() {
+            return null;
+        }
+
+        @Override
+        public void onRestoreInstanceState(Parcelable state) {
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 41f3337..7e11850 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1100,7 +1100,7 @@
 
     public boolean evaluateSystemProperties(boolean update) {
         boolean changed = false;
-        String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.1",
+        String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2",
                 VMRuntime.getRuntime().vmLibrary());
         if (!Objects.equals(runtime, mRuntime)) {
             changed = true;
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index a238ae3..5c7a4e6 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -18,7 +18,10 @@
 
 import android.animation.ValueAnimator;
 import android.content.res.TypedArray;
+import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.widget.AdapterView;
+import android.widget.Toolbar;
 import com.android.internal.R;
 import com.android.internal.view.ActionBarPolicy;
 import com.android.internal.view.menu.MenuBuilder;
@@ -28,6 +31,7 @@
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.ActionBarOverlayLayout;
 import com.android.internal.widget.ActionBarView;
+import com.android.internal.widget.DecorToolbar;
 import com.android.internal.widget.ScrollingTabContainerView;
 
 import android.animation.Animator;
@@ -55,6 +59,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.widget.SpinnerAdapter;
+import com.android.internal.widget.ToolbarWidgetWrapper;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -77,7 +82,7 @@
 
     private ActionBarOverlayLayout mOverlayLayout;
     private ActionBarContainer mContainerView;
-    private ActionBarView mActionView;
+    private DecorToolbar mDecorToolbar;
     private ActionBarContextView mContextView;
     private ActionBarContainer mSplitView;
     private View mContentView;
@@ -187,7 +192,7 @@
         if (mOverlayLayout != null) {
             mOverlayLayout.setActionBarVisibilityCallback(this);
         }
-        mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
+        mDecorToolbar = getDecorToolbar(decor.findViewById(com.android.internal.R.id.action_bar));
         mContextView = (ActionBarContextView) decor.findViewById(
                 com.android.internal.R.id.action_context_bar);
         mContainerView = (ActionBarContainer) decor.findViewById(
@@ -195,18 +200,17 @@
         mSplitView = (ActionBarContainer) decor.findViewById(
                 com.android.internal.R.id.split_action_bar);
 
-        if (mActionView == null || mContextView == null || mContainerView == null) {
+        if (mDecorToolbar == null || mContextView == null || mContainerView == null) {
             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
                     "with a compatible window decor layout");
         }
 
-        mContext = mActionView.getContext();
-        mActionView.setContextView(mContextView);
-        mContextDisplayMode = mActionView.isSplitActionBar() ?
+        mContext = mDecorToolbar.getContext();
+        mContextDisplayMode = mDecorToolbar.isSplit() ?
                 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
 
         // This was initially read from the action bar style
-        final int current = mActionView.getDisplayOptions();
+        final int current = mDecorToolbar.getDisplayOptions();
         final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
         if (homeAsUp) {
             mDisplayHomeAsUpSet = true;
@@ -225,6 +229,17 @@
         a.recycle();
     }
 
+    private DecorToolbar getDecorToolbar(View view) {
+        if (view instanceof DecorToolbar) {
+            return (DecorToolbar) view;
+        } else if (view instanceof Toolbar) {
+            return ((Toolbar) view).getWrapper();
+        } else {
+            throw new IllegalStateException("Can't make a decor toolbar out of " +
+                    view.getClass().getSimpleName());
+        }
+    }
+
     public void onConfigurationChanged(Configuration newConfig) {
         setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs());
     }
@@ -233,11 +248,11 @@
         mHasEmbeddedTabs = hasEmbeddedTabs;
         // Switch tab layout configuration if needed
         if (!mHasEmbeddedTabs) {
-            mActionView.setEmbeddedTabView(null);
+            mDecorToolbar.setEmbeddedTabView(null);
             mContainerView.setTabContainer(mTabScrollView);
         } else {
             mContainerView.setTabContainer(null);
-            mActionView.setEmbeddedTabView(mTabScrollView);
+            mDecorToolbar.setEmbeddedTabView(mTabScrollView);
         }
         final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
         if (mTabScrollView != null) {
@@ -250,7 +265,7 @@
                 mTabScrollView.setVisibility(View.GONE);
             }
         }
-        mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
+        mDecorToolbar.setCollapsible(!mHasEmbeddedTabs && isInTabMode);
         mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode);
     }
 
@@ -263,7 +278,7 @@
 
         if (mHasEmbeddedTabs) {
             tabScroller.setVisibility(View.VISIBLE);
-            mActionView.setEmbeddedTabView(tabScroller);
+            mDecorToolbar.setEmbeddedTabView(tabScroller);
         } else {
             if (getNavigationMode() == NAVIGATION_MODE_TABS) {
                 tabScroller.setVisibility(View.VISIBLE);
@@ -326,7 +341,8 @@
 
     @Override
     public void setCustomView(int resId) {
-        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
+        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId,
+                mDecorToolbar.getViewGroup(), false));
     }
 
     @Override
@@ -356,7 +372,7 @@
 
     @Override
     public void setHomeButtonEnabled(boolean enable) {
-        mActionView.setHomeButtonEnabled(enable);
+        mDecorToolbar.setHomeButtonEnabled(enable);
     }
 
     @Override
@@ -370,12 +386,12 @@
     }
 
     public void setSelectedNavigationItem(int position) {
-        switch (mActionView.getNavigationMode()) {
+        switch (mDecorToolbar.getNavigationMode()) {
         case NAVIGATION_MODE_TABS:
             selectTab(mTabs.get(position));
             break;
         case NAVIGATION_MODE_LIST:
-            mActionView.setDropdownSelectedPosition(position);
+            mDecorToolbar.setDropdownSelectedPosition(position);
             break;
         default:
             throw new IllegalStateException(
@@ -399,26 +415,26 @@
     }
 
     public void setTitle(CharSequence title) {
-        mActionView.setTitle(title);
+        mDecorToolbar.setTitle(title);
     }
 
     public void setSubtitle(CharSequence subtitle) {
-        mActionView.setSubtitle(subtitle);
+        mDecorToolbar.setSubtitle(subtitle);
     }
 
     public void setDisplayOptions(int options) {
         if ((options & DISPLAY_HOME_AS_UP) != 0) {
             mDisplayHomeAsUpSet = true;
         }
-        mActionView.setDisplayOptions(options);
+        mDecorToolbar.setDisplayOptions(options);
     }
 
     public void setDisplayOptions(int options, int mask) {
-        final int current = mActionView.getDisplayOptions(); 
+        final int current = mDecorToolbar.getDisplayOptions();
         if ((mask & DISPLAY_HOME_AS_UP) != 0) {
             mDisplayHomeAsUpSet = true;
         }
-        mActionView.setDisplayOptions((options & mask) | (current & ~mask));
+        mDecorToolbar.setDisplayOptions((options & mask) | (current & ~mask));
     }
 
     public void setBackgroundDrawable(Drawable d) {
@@ -436,23 +452,23 @@
     }
 
     public View getCustomView() {
-        return mActionView.getCustomNavigationView();
+        return mDecorToolbar.getCustomView();
     }
 
     public CharSequence getTitle() {
-        return mActionView.getTitle();
+        return mDecorToolbar.getTitle();
     }
 
     public CharSequence getSubtitle() {
-        return mActionView.getSubtitle();
+        return mDecorToolbar.getSubtitle();
     }
 
     public int getNavigationMode() {
-        return mActionView.getNavigationMode();
+        return mDecorToolbar.getNavigationMode();
     }
 
     public int getDisplayOptions() {
-        return mActionView.getDisplayOptions();
+        return mDecorToolbar.getDisplayOptions();
     }
 
     public ActionMode startActionMode(ActionMode.Callback callback) {
@@ -572,7 +588,7 @@
             return;
         }
 
-        final FragmentTransaction trans = mActionView.isInEditMode() ? null :
+        final FragmentTransaction trans = ((View) mDecorToolbar).isInEditMode() ? null :
                 mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack();
 
         if (mSelectedTab == tab) {
@@ -828,13 +844,18 @@
             hideForActionMode();
         }
 
-        mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
+        mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
         mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE);
-        if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) {
+        if (mTabScrollView != null && !mDecorToolbar.hasEmbeddedTabs() &&
+                isCollapsed((View) mDecorToolbar)) {
             mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE);
         }
     }
 
+    private boolean isCollapsed(View view) {
+        return view == null || view.getVisibility() == View.GONE || view.getMeasuredHeight() == 0;
+    }
+
     public Context getThemedContext() {
         if (mThemedContext == null) {
             TypedValue outValue = new TypedValue();
@@ -854,27 +875,27 @@
     
     @Override
     public boolean isTitleTruncated() {
-        return mActionView != null && mActionView.isTitleTruncated();
+        return mDecorToolbar != null && mDecorToolbar.isTitleTruncated();
     }
 
     @Override
     public void setHomeAsUpIndicator(Drawable indicator) {
-        mActionView.setHomeAsUpIndicator(indicator);
+        mDecorToolbar.setNavigationIcon(indicator);
     }
 
     @Override
     public void setHomeAsUpIndicator(int resId) {
-        mActionView.setHomeAsUpIndicator(resId);
+        mDecorToolbar.setNavigationIcon(resId);
     }
 
     @Override
     public void setHomeActionContentDescription(CharSequence description) {
-        mActionView.setHomeActionContentDescription(description);
+        mDecorToolbar.setNavigationContentDescription(description);
     }
 
     @Override
     public void setHomeActionContentDescription(int resId) {
-        mActionView.setHomeActionContentDescription(resId);
+        mDecorToolbar.setNavigationContentDescription(resId);
     }
 
     @Override
@@ -938,7 +959,8 @@
 
             // Clear out the context mode views after the animation finishes
             mContextView.closeMode();
-            mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+            ((View) mDecorToolbar).sendAccessibilityEvent(
+                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
 
             mActionMode = null;
@@ -1178,28 +1200,27 @@
 
     @Override
     public void setCustomView(View view) {
-        mActionView.setCustomNavigationView(view);
+        mDecorToolbar.setCustomView(view);
     }
 
     @Override
     public void setCustomView(View view, LayoutParams layoutParams) {
         view.setLayoutParams(layoutParams);
-        mActionView.setCustomNavigationView(view);
+        mDecorToolbar.setCustomView(view);
     }
 
     @Override
     public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
-        mActionView.setDropdownAdapter(adapter);
-        mActionView.setCallback(callback);
+        mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
     }
 
     @Override
     public int getSelectedNavigationIndex() {
-        switch (mActionView.getNavigationMode()) {
+        switch (mDecorToolbar.getNavigationMode()) {
             case NAVIGATION_MODE_TABS:
                 return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
             case NAVIGATION_MODE_LIST:
-                return mActionView.getDropdownSelectedPosition();
+                return mDecorToolbar.getDropdownSelectedPosition();
             default:
                 return -1;
         }
@@ -1207,12 +1228,11 @@
 
     @Override
     public int getNavigationItemCount() {
-        switch (mActionView.getNavigationMode()) {
+        switch (mDecorToolbar.getNavigationMode()) {
             case NAVIGATION_MODE_TABS:
                 return mTabs.size();
             case NAVIGATION_MODE_LIST:
-                SpinnerAdapter adapter = mActionView.getDropdownAdapter();
-                return adapter != null ? adapter.getCount() : 0;
+                return mDecorToolbar.getDropdownItemCount();
             default:
                 return 0;
         }
@@ -1225,7 +1245,7 @@
 
     @Override
     public void setNavigationMode(int mode) {
-        final int oldMode = mActionView.getNavigationMode();
+        final int oldMode = mDecorToolbar.getNavigationMode();
         switch (oldMode) {
             case NAVIGATION_MODE_TABS:
                 mSavedTabPosition = getSelectedNavigationIndex();
@@ -1238,7 +1258,7 @@
                 mOverlayLayout.requestFitSystemWindows();
             }
         }
-        mActionView.setNavigationMode(mode);
+        mDecorToolbar.setNavigationMode(mode);
         switch (mode) {
             case NAVIGATION_MODE_TABS:
                 ensureTabsExist();
@@ -1249,7 +1269,7 @@
                 }
                 break;
         }
-        mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
+        mDecorToolbar.setCollapsible(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
         mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
     }
 
@@ -1261,30 +1281,30 @@
 
     @Override
     public void setIcon(int resId) {
-        mActionView.setIcon(resId);
+        mDecorToolbar.setIcon(resId);
     }
 
     @Override
     public void setIcon(Drawable icon) {
-        mActionView.setIcon(icon);
+        mDecorToolbar.setIcon(icon);
     }
 
     public boolean hasIcon() {
-        return mActionView.hasIcon();
+        return mDecorToolbar.hasIcon();
     }
 
     @Override
     public void setLogo(int resId) {
-        mActionView.setLogo(resId);
+        mDecorToolbar.setLogo(resId);
     }
 
     @Override
     public void setLogo(Drawable logo) {
-        mActionView.setLogo(logo);
+        mDecorToolbar.setLogo(logo);
     }
 
     public boolean hasLogo() {
-        return mActionView.hasLogo();
+        return mDecorToolbar.hasLogo();
     }
 
     public void setDefaultDisplayHomeAsUpEnabled(boolean enable) {
@@ -1292,4 +1312,24 @@
             setDisplayHomeAsUpEnabled(enable);
         }
     }
+
+    static class NavItemSelectedListener implements AdapterView.OnItemSelectedListener {
+        private final OnNavigationListener mListener;
+
+        public NavItemSelectedListener(OnNavigationListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (mListener != null) {
+                mListener.onNavigationItemSelected(position, id);
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Do nothing
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 24e55e4..ed9f9bc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2342,7 +2342,13 @@
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
             aggregateLastWakeupUptimeLocked(uptime);
-            historyName = historyName == null || mRecordAllWakeLocks ? name : historyName;
+            if (mRecordAllWakeLocks) {
+                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, name, uid, 0)) {
+                    addHistoryEventLocked(elapsedRealtime, uptime,
+                            HistoryItem.EVENT_WAKE_LOCK_START, name, uid);
+                }
+            }
+            historyName = historyName == null ? name : historyName;
             if (mWakeLockNesting == 0) {
                 mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2352,7 +2358,7 @@
                 mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                 mWakeLockImportant = !unimportantForLogging;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
-            } else if (!mRecordAllWakeLocks && !mWakeLockImportant && !unimportantForLogging) {
+            } else if (!mWakeLockImportant && !unimportantForLogging) {
                 if (mHistoryLastWritten.wakelockTag != null) {
                     // We'll try to update the last tag.
                     mHistoryLastWritten.wakelockTag = null;
@@ -2362,14 +2368,6 @@
                     addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 mWakeLockImportant = true;
-            } else if (mRecordAllWakeLocks) {
-                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
-                        uid, 0)) {
-                    mWakeLockNesting++;
-                    return;
-                }
-                addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_START,
-                        historyName, uid);
             }
             mWakeLockNesting++;
         }
@@ -2387,28 +2385,19 @@
         uid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             mWakeLockNesting--;
-            historyName = historyName == null || mRecordAllWakeLocks ? name : historyName;
+            if (mRecordAllWakeLocks) {
+                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid, 0)) {
+                    addHistoryEventLocked(elapsedRealtime, uptime,
+                            HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid);
+                }
+            }
             if (mWakeLockNesting == 0) {
                 mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
-                if (mRecordAllWakeLocks
-                        || (historyName != null && !historyName.equals(mInitialAcquireWakeName))
-                        || uid != mInitialAcquireWakeUid) {
-                    mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                    mHistoryCur.wakelockTag.string = historyName;
-                    mHistoryCur.wakelockTag.uid = uid;
-                }
                 mInitialAcquireWakeName = null;
                 mInitialAcquireWakeUid = -1;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
-            } else if (mRecordAllWakeLocks) {
-                if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
-                        uid, 0)) {
-                    return;
-                }
-                addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_FINISH,
-                        historyName, uid);
             }
         }
         if (uid >= 0) {
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index b78c70f..f22800c 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -56,4 +56,10 @@
     oneway void dispatch(in MotionEvent event);
     oneway void launchCamera();
     oneway void onBootCompleted();
+
+    /**
+     * Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper
+     * and keyguard flag.
+     */
+    oneway void startKeyguardExitAnimation(long fadeoutDuration);
 }
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 5b59599..dca9921 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -220,31 +220,77 @@
      * @see #readMapXml
      */
     public static final void writeMapXml(Map val, String name, XmlSerializer out)
-    throws XmlPullParserException, java.io.IOException
-    {
+            throws XmlPullParserException, java.io.IOException {
+        writeMapXml(val, name, out, null);
+    }
+
+    /**
+     * Flatten a Map into an XmlSerializer.  The map can later be read back
+     * with readThisMapXml().
+     *
+     * @param val The map to be flattened.
+     * @param name Name attribute to include with this list's tag, or null for
+     *             none.
+     * @param out XmlSerializer to write the map into.
+     * @param callback Method to call when an Object type is not recognized.
+     *
+     * @see #writeMapXml(Map, OutputStream)
+     * @see #writeListXml
+     * @see #writeValueXml
+     * @see #readMapXml
+     *
+     * @hide
+     */
+    public static final void writeMapXml(Map val, String name, XmlSerializer out,
+            WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
+
         if (val == null) {
             out.startTag(null, "null");
             out.endTag(null, "null");
             return;
         }
 
-        Set s = val.entrySet();
-        Iterator i = s.iterator();
-
         out.startTag(null, "map");
         if (name != null) {
             out.attribute(null, "name", name);
         }
 
-        while (i.hasNext()) {
-            Map.Entry e = (Map.Entry)i.next();
-            writeValueXml(e.getValue(), (String)e.getKey(), out);
-        }
+        writeMapXml(val, out, callback);
 
         out.endTag(null, "map");
     }
 
     /**
+     * Flatten a Map into an XmlSerializer.  The map can later be read back
+     * with readThisMapXml(). This method presumes that the start tag and
+     * name attribute have already been written and does not write an end tag.
+     *
+     * @param val The map to be flattened.
+     * @param out XmlSerializer to write the map into.
+     *
+     * @see #writeMapXml(Map, OutputStream)
+     * @see #writeListXml
+     * @see #writeValueXml
+     * @see #readMapXml
+     *
+     * @hide
+     */
+    public static final void writeMapXml(Map val, XmlSerializer out,
+            WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
+        if (val == null) {
+            return;
+        }
+
+        Set s = val.entrySet();
+        Iterator i = s.iterator();
+
+        while (i.hasNext()) {
+            Map.Entry e = (Map.Entry)i.next();
+            writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
+        }
+    }
+
+    /**
      * Flatten a List into an XmlSerializer.  The list can later be read back
      * with readThisListXml().
      *
@@ -387,6 +433,123 @@
     }
 
     /**
+     * Flatten a long[] into an XmlSerializer.  The list can later be read back
+     * with readThisLongArrayXml().
+     *
+     * @param val The long array to be flattened.
+     * @param name Name attribute to include with this array's tag, or null for
+     *             none.
+     * @param out XmlSerializer to write the array into.
+     *
+     * @see #writeMapXml
+     * @see #writeValueXml
+     * @see #readThisIntArrayXml
+     */
+    public static final void writeLongArrayXml(long[] val, String name, XmlSerializer out)
+            throws XmlPullParserException, java.io.IOException {
+
+        if (val == null) {
+            out.startTag(null, "null");
+            out.endTag(null, "null");
+            return;
+        }
+
+        out.startTag(null, "long-array");
+        if (name != null) {
+            out.attribute(null, "name", name);
+        }
+
+        final int N = val.length;
+        out.attribute(null, "num", Integer.toString(N));
+
+        for (int i=0; i<N; i++) {
+            out.startTag(null, "item");
+            out.attribute(null, "value", Long.toString(val[i]));
+            out.endTag(null, "item");
+        }
+
+        out.endTag(null, "long-array");
+    }
+
+    /**
+     * Flatten a double[] into an XmlSerializer.  The list can later be read back
+     * with readThisDoubleArrayXml().
+     *
+     * @param val The double array to be flattened.
+     * @param name Name attribute to include with this array's tag, or null for
+     *             none.
+     * @param out XmlSerializer to write the array into.
+     *
+     * @see #writeMapXml
+     * @see #writeValueXml
+     * @see #readThisIntArrayXml
+     */
+    public static final void writeDoubleArrayXml(double[] val, String name, XmlSerializer out)
+            throws XmlPullParserException, java.io.IOException {
+
+        if (val == null) {
+            out.startTag(null, "null");
+            out.endTag(null, "null");
+            return;
+        }
+
+        out.startTag(null, "double-array");
+        if (name != null) {
+            out.attribute(null, "name", name);
+        }
+
+        final int N = val.length;
+        out.attribute(null, "num", Integer.toString(N));
+
+        for (int i=0; i<N; i++) {
+            out.startTag(null, "item");
+            out.attribute(null, "value", Double.toString(val[i]));
+            out.endTag(null, "item");
+        }
+
+        out.endTag(null, "double-array");
+    }
+
+    /**
+     * Flatten a String[] into an XmlSerializer.  The list can later be read back
+     * with readThisStringArrayXml().
+     *
+     * @param val The long array to be flattened.
+     * @param name Name attribute to include with this array's tag, or null for
+     *             none.
+     * @param out XmlSerializer to write the array into.
+     *
+     * @see #writeMapXml
+     * @see #writeValueXml
+     * @see #readThisIntArrayXml
+     */
+    public static final void writeStringArrayXml(String[] val, String name, XmlSerializer out)
+            throws XmlPullParserException, java.io.IOException {
+
+        if (val == null) {
+            out.startTag(null, "null");
+            out.endTag(null, "null");
+            return;
+        }
+
+        out.startTag(null, "string-array");
+        if (name != null) {
+            out.attribute(null, "name", name);
+        }
+
+        final int N = val.length;
+        out.attribute(null, "num", Integer.toString(N));
+
+        for (int i=0; i<N; i++) {
+            out.startTag(null, "item");
+            out.attribute(null, "value", val[i]);
+            out.endTag(null, "item");
+        }
+
+        out.endTag(null, "string-array");
+    }
+
+    /**
      * Flatten an object's value into an XmlSerializer.  The value can later
      * be read back with readThisValueXml().
      *
@@ -403,8 +566,29 @@
      * @see #readValueXml
      */
     public static final void writeValueXml(Object v, String name, XmlSerializer out)
-    throws XmlPullParserException, java.io.IOException
-    {
+            throws XmlPullParserException, java.io.IOException {
+        writeValueXml(v, name, out, null);
+    }
+
+    /**
+     * Flatten an object's value into an XmlSerializer.  The value can later
+     * be read back with readThisValueXml().
+     *
+     * Currently supported value types are: null, String, Integer, Long,
+     * Float, Double Boolean, Map, List.
+     *
+     * @param v The object to be flattened.
+     * @param name Name attribute to include with this value's tag, or null
+     *             for none.
+     * @param out XmlSerializer to write the object into.
+     * @param callback Handler for Object types not recognized.
+     *
+     * @see #writeMapXml
+     * @see #writeListXml
+     * @see #readValueXml
+     */
+    private static final void writeValueXml(Object v, String name, XmlSerializer out,
+            WriteMapCallback callback)  throws XmlPullParserException, java.io.IOException {
         String typeStr;
         if (v == null) {
             out.startTag(null, "null");
@@ -437,14 +621,23 @@
         } else if (v instanceof int[]) {
             writeIntArrayXml((int[])v, name, out);
             return;
+        } else if (v instanceof long[]) {
+            writeLongArrayXml((long[])v, name, out);
+            return;
+        } else if (v instanceof double[]) {
+            writeDoubleArrayXml((double[])v, name, out);
+            return;
+        } else if (v instanceof String[]) {
+            writeStringArrayXml((String[])v, name, out);
+            return;
         } else if (v instanceof Map) {
             writeMapXml((Map)v, name, out);
             return;
         } else if (v instanceof List) {
-            writeListXml((List)v, name, out);
+            writeListXml((List) v, name, out);
             return;
         } else if (v instanceof Set) {
-            writeSetXml((Set)v, name, out);
+            writeSetXml((Set) v, name, out);
             return;
         } else if (v instanceof CharSequence) {
             // XXX This is to allow us to at least write something if
@@ -457,6 +650,9 @@
             out.text(v.toString());
             out.endTag(null, "string");
             return;
+        } else if (callback != null) {
+            callback.writeUnknownObject(v, name, out);
+            return;
         } else {
             throw new RuntimeException("writeValueXml: unable to write value " + v);
         }
@@ -550,14 +746,35 @@
      * @see #readMapXml
      */
     public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
-            String[] name) throws XmlPullParserException, java.io.IOException
+            String[] name) throws XmlPullParserException, java.io.IOException {
+        return readThisMapXml(parser, endTag, name, null);
+    }
+
+    /**
+     * Read a HashMap object from an XmlPullParser.  The XML data could
+     * previously have been generated by writeMapXml().  The XmlPullParser
+     * must be positioned <em>after</em> the tag that begins the map.
+     *
+     * @param parser The XmlPullParser from which to read the map data.
+     * @param endTag Name of the tag that will end the map, usually "map".
+     * @param name An array of one string, used to return the name attribute
+     *             of the map's tag.
+     *
+     * @return HashMap The newly generated map.
+     *
+     * @see #readMapXml
+     * @hide
+     */
+    public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
+            String[] name, ReadMapCallback callback)
+            throws XmlPullParserException, java.io.IOException
     {
         HashMap<String, Object> map = new HashMap<String, Object>();
 
         int eventType = parser.getEventType();
         do {
             if (eventType == parser.START_TAG) {
-                Object val = readThisValueXml(parser, name);
+                Object val = readThisValueXml(parser, name, callback);
                 map.put(name[0], val);
             } else if (eventType == parser.END_TAG) {
                 if (parser.getName().equals(endTag)) {
@@ -587,15 +804,34 @@
      *
      * @see #readListXml
      */
-    public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name)
-    throws XmlPullParserException, java.io.IOException
-    {
+    public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+            String[] name) throws XmlPullParserException, java.io.IOException {
+        return readThisListXml(parser, endTag, name, null);
+    }
+
+    /**
+     * Read an ArrayList object from an XmlPullParser.  The XML data could
+     * previously have been generated by writeListXml().  The XmlPullParser
+     * must be positioned <em>after</em> the tag that begins the list.
+     *
+     * @param parser The XmlPullParser from which to read the list data.
+     * @param endTag Name of the tag that will end the list, usually "list".
+     * @param name An array of one string, used to return the name attribute
+     *             of the list's tag.
+     *
+     * @return HashMap The newly generated list.
+     *
+     * @see #readListXml
+     */
+    private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
+            String[] name, ReadMapCallback callback)
+            throws XmlPullParserException, java.io.IOException {
         ArrayList list = new ArrayList();
 
         int eventType = parser.getEventType();
         do {
             if (eventType == parser.START_TAG) {
-                Object val = readThisValueXml(parser, name);
+                Object val = readThisValueXml(parser, name, callback);
                 list.add(val);
                 //System.out.println("Adding to list: " + val);
             } else if (eventType == parser.END_TAG) {
@@ -611,7 +847,29 @@
         throw new XmlPullParserException(
             "Document ended before " + endTag + " end tag");
     }
-    
+
+    /**
+     * Read a HashSet object from an XmlPullParser. The XML data could previously
+     * have been generated by writeSetXml(). The XmlPullParser must be positioned
+     * <em>after</em> the tag that begins the set.
+     *
+     * @param parser The XmlPullParser from which to read the set data.
+     * @param endTag Name of the tag that will end the set, usually "set".
+     * @param name An array of one string, used to return the name attribute
+     *             of the set's tag.
+     *
+     * @return HashSet The newly generated set.
+     *
+     * @throws XmlPullParserException
+     * @throws java.io.IOException
+     *
+     * @see #readSetXml
+     */
+    public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
+            throws XmlPullParserException, java.io.IOException {
+        return readThisSetXml(parser, endTag, name, null);
+    }
+
     /**
      * Read a HashSet object from an XmlPullParser. The XML data could previously
      * have been generated by writeSetXml(). The XmlPullParser must be positioned
@@ -628,15 +886,16 @@
      * @throws java.io.IOException
      * 
      * @see #readSetXml
+     * @hide
      */
-    public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
-            throws XmlPullParserException, java.io.IOException {
+    private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
+            ReadMapCallback callback) throws XmlPullParserException, java.io.IOException {
         HashSet set = new HashSet();
         
         int eventType = parser.getEventType();
         do {
             if (eventType == parser.START_TAG) {
-                Object val = readThisValueXml(parser, name);
+                Object val = readThisValueXml(parser, name, callback);
                 set.add(val);
                 //System.out.println("Adding to set: " + val);
             } else if (eventType == parser.END_TAG) {
@@ -681,6 +940,7 @@
             throw new XmlPullParserException(
                     "Not a number in num attribute in byte-array");
         }
+        parser.next();
 
         int[] array = new int[num];
         int i = 0;
@@ -722,6 +982,187 @@
     }
 
     /**
+     * Read a long[] object from an XmlPullParser.  The XML data could
+     * previously have been generated by writeLongArrayXml().  The XmlPullParser
+     * must be positioned <em>after</em> the tag that begins the list.
+     *
+     * @param parser The XmlPullParser from which to read the list data.
+     * @param endTag Name of the tag that will end the list, usually "list".
+     * @param name An array of one string, used to return the name attribute
+     *             of the list's tag.
+     *
+     * @return Returns a newly generated long[].
+     *
+     * @see #readListXml
+     */
+    public static final long[] readThisLongArrayXml(XmlPullParser parser,
+            String endTag, String[] name)
+            throws XmlPullParserException, java.io.IOException {
+
+        int num;
+        try {
+            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+        } catch (NullPointerException e) {
+            throw new XmlPullParserException("Need num attribute in long-array");
+        } catch (NumberFormatException e) {
+            throw new XmlPullParserException("Not a number in num attribute in long-array");
+        }
+        parser.next();
+
+        long[] array = new long[num];
+        int i = 0;
+
+        int eventType = parser.getEventType();
+        do {
+            if (eventType == parser.START_TAG) {
+                if (parser.getName().equals("item")) {
+                    try {
+                        array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
+                    } catch (NullPointerException e) {
+                        throw new XmlPullParserException("Need value attribute in item");
+                    } catch (NumberFormatException e) {
+                        throw new XmlPullParserException("Not a number in value attribute in item");
+                    }
+                } else {
+                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+                }
+            } else if (eventType == parser.END_TAG) {
+                if (parser.getName().equals(endTag)) {
+                    return array;
+                } else if (parser.getName().equals("item")) {
+                    i++;
+                } else {
+                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+                            parser.getName());
+                }
+            }
+            eventType = parser.next();
+        } while (eventType != parser.END_DOCUMENT);
+
+        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+    }
+
+    /**
+     * Read a double[] object from an XmlPullParser.  The XML data could
+     * previously have been generated by writeDoubleArrayXml().  The XmlPullParser
+     * must be positioned <em>after</em> the tag that begins the list.
+     *
+     * @param parser The XmlPullParser from which to read the list data.
+     * @param endTag Name of the tag that will end the list, usually "double-array".
+     * @param name An array of one string, used to return the name attribute
+     *             of the list's tag.
+     *
+     * @return Returns a newly generated double[].
+     *
+     * @see #readListXml
+     */
+    public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
+            String[] name) throws XmlPullParserException, java.io.IOException {
+
+        int num;
+        try {
+            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+        } catch (NullPointerException e) {
+            throw new XmlPullParserException("Need num attribute in double-array");
+        } catch (NumberFormatException e) {
+            throw new XmlPullParserException("Not a number in num attribute in double-array");
+        }
+        parser.next();
+
+        double[] array = new double[num];
+        int i = 0;
+
+        int eventType = parser.getEventType();
+        do {
+            if (eventType == parser.START_TAG) {
+                if (parser.getName().equals("item")) {
+                    try {
+                        array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
+                    } catch (NullPointerException e) {
+                        throw new XmlPullParserException("Need value attribute in item");
+                    } catch (NumberFormatException e) {
+                        throw new XmlPullParserException("Not a number in value attribute in item");
+                    }
+                } else {
+                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+                }
+            } else if (eventType == parser.END_TAG) {
+                if (parser.getName().equals(endTag)) {
+                    return array;
+                } else if (parser.getName().equals("item")) {
+                    i++;
+                } else {
+                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+                            parser.getName());
+                }
+            }
+            eventType = parser.next();
+        } while (eventType != parser.END_DOCUMENT);
+
+        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+    }
+
+    /**
+     * Read a String[] object from an XmlPullParser.  The XML data could
+     * previously have been generated by writeStringArrayXml().  The XmlPullParser
+     * must be positioned <em>after</em> the tag that begins the list.
+     *
+     * @param parser The XmlPullParser from which to read the list data.
+     * @param endTag Name of the tag that will end the list, usually "string-array".
+     * @param name An array of one string, used to return the name attribute
+     *             of the list's tag.
+     *
+     * @return Returns a newly generated String[].
+     *
+     * @see #readListXml
+     */
+    public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
+            String[] name) throws XmlPullParserException, java.io.IOException {
+
+        int num;
+        try {
+            num = Integer.parseInt(parser.getAttributeValue(null, "num"));
+        } catch (NullPointerException e) {
+            throw new XmlPullParserException("Need num attribute in string-array");
+        } catch (NumberFormatException e) {
+            throw new XmlPullParserException("Not a number in num attribute in string-array");
+        }
+        parser.next();
+
+        String[] array = new String[num];
+        int i = 0;
+
+        int eventType = parser.getEventType();
+        do {
+            if (eventType == parser.START_TAG) {
+                if (parser.getName().equals("item")) {
+                    try {
+                        array[i] = parser.getAttributeValue(null, "value");
+                    } catch (NullPointerException e) {
+                        throw new XmlPullParserException("Need value attribute in item");
+                    } catch (NumberFormatException e) {
+                        throw new XmlPullParserException("Not a number in value attribute in item");
+                    }
+                } else {
+                    throw new XmlPullParserException("Expected item tag at: " + parser.getName());
+                }
+            } else if (eventType == parser.END_TAG) {
+                if (parser.getName().equals(endTag)) {
+                    return array;
+                } else if (parser.getName().equals("item")) {
+                    i++;
+                } else {
+                    throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
+                            parser.getName());
+                }
+            }
+            eventType = parser.next();
+        } while (eventType != parser.END_DOCUMENT);
+
+        throw new XmlPullParserException("Document ended before " + endTag + " end tag");
+    }
+
+    /**
      * Read a flattened object from an XmlPullParser.  The XML data could
      * previously have been written with writeMapXml(), writeListXml(), or
      * writeValueXml().  The XmlPullParser must be positioned <em>at</em> the
@@ -743,7 +1184,7 @@
         int eventType = parser.getEventType();
         do {
             if (eventType == parser.START_TAG) {
-                return readThisValueXml(parser, name);
+                return readThisValueXml(parser, name, null);
             } else if (eventType == parser.END_TAG) {
                 throw new XmlPullParserException(
                     "Unexpected end tag at: " + parser.getName());
@@ -758,9 +1199,8 @@
             "Unexpected end of document");
     }
 
-    private static final Object readThisValueXml(XmlPullParser parser, String[] name)
-    throws XmlPullParserException, java.io.IOException
-    {
+    private static final Object readThisValueXml(XmlPullParser parser, String[] name,
+            ReadMapCallback callback)  throws XmlPullParserException, java.io.IOException {
         final String valueName = parser.getAttributeValue(null, "name");
         final String tagName = parser.getName();
 
@@ -794,11 +1234,25 @@
         } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
             // all work already done by readThisPrimitiveValueXml
         } else if (tagName.equals("int-array")) {
-            parser.next();
             res = readThisIntArrayXml(parser, "int-array", name);
             name[0] = valueName;
             //System.out.println("Returning value for " + valueName + ": " + res);
             return res;
+        } else if (tagName.equals("long-array")) {
+            res = readThisLongArrayXml(parser, "long-array", name);
+            name[0] = valueName;
+            //System.out.println("Returning value for " + valueName + ": " + res);
+            return res;
+        } else if (tagName.equals("double-array")) {
+            res = readThisDoubleArrayXml(parser, "double-array", name);
+            name[0] = valueName;
+            //System.out.println("Returning value for " + valueName + ": " + res);
+            return res;
+        } else if (tagName.equals("string-array")) {
+            res = readThisStringArrayXml(parser, "string-array", name);
+            name[0] = valueName;
+            //System.out.println("Returning value for " + valueName + ": " + res);
+            return res;
         } else if (tagName.equals("map")) {
             parser.next();
             res = readThisMapXml(parser, "map", name);
@@ -817,9 +1271,12 @@
             name[0] = valueName;
             //System.out.println("Returning value for " + valueName + ": " + res);
             return res;
+        } else if (callback != null) {
+            res = callback.readThisUnknownObjectXml(parser, tagName);
+            name[0] = valueName;
+            return res;
         } else {
-            throw new XmlPullParserException(
-                "Unknown tag: " + tagName);
+            throw new XmlPullParserException("Unknown tag: " + tagName);
         }
 
         // Skip through to end tag.
@@ -967,4 +1424,39 @@
             throws IOException {
         out.attribute(null, name, Boolean.toString(value));
     }
+
+    /** @hide */
+    public interface WriteMapCallback {
+        /**
+         * Called from writeMapXml when an Object type is not recognized. The implementer
+         * must write out the entire element including start and end tags.
+         *
+         * @param v The object to be written out
+         * @param name The mapping key for v. Must be written into the "name" attribute of the
+         *             start tag.
+         * @param out The XML output stream.
+         * @throws XmlPullParserException on unrecognized Object type.
+         * @throws IOException on XmlSerializer serialization errors.
+         * @hide
+         */
+         public void writeUnknownObject(Object v, String name, XmlSerializer out)
+                 throws XmlPullParserException, IOException;
+    }
+
+    /** @hide */
+    public interface ReadMapCallback {
+        /**
+         * Called from readThisMapXml when a START_TAG is not recognized. The input stream
+         * is positioned within the start tag so that attributes can be read using in.getAttribute.
+         *
+         * @param in the XML input stream
+         * @param tag the START_TAG that was not recognized.
+         * @return the Object parsed from the stream which will be put into the map.
+         * @throws XmlPullParserException if the START_TAG is not recognized.
+         * @throws IOException on XmlPullParser serialization errors.
+         * @hide
+         */
+        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
+                throws XmlPullParserException, IOException;
+    }
 }
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index 183478f..9e7ff93 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -34,7 +34,7 @@
 public abstract class AbsActionBarView extends ViewGroup {
     protected ActionMenuView mMenuView;
     protected ActionMenuPresenter mActionMenuPresenter;
-    protected ActionBarContainer mSplitView;
+    protected ViewGroup mSplitView;
     protected boolean mSplitActionBar;
     protected boolean mSplitWhenNarrow;
     protected int mContentHeight;
@@ -74,7 +74,7 @@
         setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
         a.recycle();
         if (mSplitWhenNarrow) {
-            setSplitActionBar(getContext().getResources().getBoolean(
+            setSplitToolbar(getContext().getResources().getBoolean(
                     com.android.internal.R.bool.split_action_bar_is_narrow));
         }
         if (mActionMenuPresenter != null) {
@@ -86,7 +86,7 @@
      * Sets whether the bar should be split right now, no questions asked.
      * @param split true if the bar should split
      */
-    public void setSplitActionBar(boolean split) {
+    public void setSplitToolbar(boolean split) {
         mSplitActionBar = split;
     }
 
@@ -107,7 +107,7 @@
         return mContentHeight;
     }
 
-    public void setSplitView(ActionBarContainer splitView) {
+    public void setSplitView(ViewGroup splitView) {
         mSplitView = splitView;
     }
 
@@ -214,6 +214,10 @@
         return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
     }
 
+    public boolean canShowOverflowMenu() {
+        return isOverflowReserved() && getVisibility() == VISIBLE;
+    }
+
     public void dismissPopupMenus() {
         if (mActionMenuPresenter != null) {
             mActionMenuPresenter.dismissPopupMenus();
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index ed07514..790b611 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -36,7 +36,7 @@
 public class ActionBarContainer extends FrameLayout {
     private boolean mIsTransitioning;
     private View mTabContainer;
-    private ActionBarView mActionBarView;
+    private View mActionBarView;
 
     private Drawable mBackground;
     private Drawable mStackedBackground;
@@ -76,7 +76,7 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+        mActionBarView = findViewById(com.android.internal.R.id.action_bar);
     }
 
     public void setPrimaryBackground(Drawable bg) {
@@ -251,6 +251,10 @@
         return null;
     }
 
+    private boolean isCollapsed(View view) {
+        return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
+    }
+
     @Override
     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mActionBarView == null &&
@@ -263,7 +267,7 @@
         if (mActionBarView == null) return;
 
         final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
-        final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 :
+        final int actionBarViewHeight = isCollapsed(mActionBarView) ? 0 :
                 mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
 
         if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
@@ -298,9 +302,8 @@
             }
         } else {
             if (mBackground != null) {
-                final ActionBarView actionBarView = mActionBarView;
-                mBackground.setBounds(actionBarView.getLeft(), actionBarView.getTop(),
-                        actionBarView.getRight(), actionBarView.getBottom());
+                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
+                        mActionBarView.getRight(), mActionBarView.getBottom());
                 needsInvalidate = true;
             }
             mIsStacked = hasTabs;
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index e10070f..6ff77a0 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -83,7 +83,7 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.ActionMode, defStyleAttr, defStyleRes);
-        setBackgroundDrawable(a.getDrawable(
+        setBackground(a.getDrawable(
                 com.android.internal.R.styleable.ActionMode_background));
         mTitleStyleRes = a.getResourceId(
                 com.android.internal.R.styleable.ActionMode_titleTextStyle, 0);
@@ -109,7 +109,7 @@
     }
 
     @Override
-    public void setSplitActionBar(boolean split) {
+    public void setSplitToolbar(boolean split) {
         if (mSplitActionBar != split) {
             if (mActionMenuPresenter != null) {
                 // Mode is already active; move everything over and adjust the menu itself.
@@ -137,7 +137,7 @@
                     mSplitView.addView(mMenuView, layoutParams);
                 }
             }
-            super.setSplitActionBar(split);
+            super.setSplitToolbar(split);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 7ab4bed..8a9cb22 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -39,6 +39,7 @@
 import android.view.Window;
 import android.view.WindowInsets;
 import android.widget.OverScroller;
+import android.widget.Toolbar;
 import com.android.internal.view.menu.MenuPresenter;
 
 /**
@@ -59,7 +60,7 @@
     private ActionBarContainer mActionBarTop;
 
     // Some interior UI elements.
-    private ActionBarView mActionBarView;
+    private DecorToolbar mDecorToolbar;
 
     // Content overlay drawable - generally the action bar's shadow
     private Drawable mWindowContentOverlay;
@@ -401,7 +402,7 @@
             topInset = mActionBarTop.getMeasuredHeight();
         }
 
-        if (mActionBarView.isSplitActionBar()) {
+        if (mDecorToolbar.isSplit()) {
             // If action bar is split, adjust bottom insets for it.
             if (mActionBarBottom != null) {
                 if (stable) {
@@ -563,12 +564,23 @@
             mContent = findViewById(com.android.internal.R.id.content);
             mActionBarTop = (ActionBarContainer) findViewById(
                     com.android.internal.R.id.action_bar_container);
-            mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+            mDecorToolbar = getDecorToolbar(findViewById(com.android.internal.R.id.action_bar));
             mActionBarBottom = (ActionBarContainer) findViewById(
                     com.android.internal.R.id.split_action_bar);
         }
     }
 
+    private DecorToolbar getDecorToolbar(View view) {
+        if (view instanceof DecorToolbar) {
+            return (DecorToolbar) view;
+        } else if (view instanceof Toolbar) {
+            return ((Toolbar) view).getWrapper();
+        } else {
+            throw new IllegalStateException("Can't make a decor toolbar out of " +
+                    view.getClass().getSimpleName());
+        }
+    }
+
     public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
         if (hideOnContentScroll != mHideOnContentScroll) {
             mHideOnContentScroll = hideOnContentScroll;
@@ -648,9 +660,9 @@
             final int action = event.getAction();
 
             // Collapse any expanded action views.
-            if (mActionBarView != null && mActionBarView.hasExpandedActionView()) {
+            if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
                 if (action == KeyEvent.ACTION_UP) {
-                    mActionBarView.collapseActionView();
+                    mDecorToolbar.collapseActionView();
                 }
                 return true;
             }
@@ -662,19 +674,19 @@
     @Override
     public void setWindowCallback(Window.Callback cb) {
         pullChildren();
-        mActionBarView.setWindowCallback(cb);
+        mDecorToolbar.setWindowCallback(cb);
     }
 
     @Override
     public void setWindowTitle(CharSequence title) {
         pullChildren();
-        mActionBarView.setWindowTitle(title);
+        mDecorToolbar.setWindowTitle(title);
     }
 
     @Override
     public CharSequence getTitle() {
         pullChildren();
-        return mActionBarView.getTitle();
+        return mDecorToolbar.getTitle();
     }
 
     @Override
@@ -682,10 +694,10 @@
         pullChildren();
         switch (windowFeature) {
             case Window.FEATURE_PROGRESS:
-                mActionBarView.initProgress();
+                mDecorToolbar.initProgress();
                 break;
             case Window.FEATURE_INDETERMINATE_PROGRESS:
-                mActionBarView.initIndeterminateProgress();
+                mDecorToolbar.initIndeterminateProgress();
                 break;
             case Window.FEATURE_ACTION_BAR_OVERLAY:
                 setOverlayMode(true);
@@ -704,15 +716,15 @@
         }
         if (splitActionBar) {
             pullChildren();
-            if (mActionBarBottom != null) {
-                mActionBarView.setSplitView(mActionBarBottom);
-                mActionBarView.setSplitActionBar(splitActionBar);
-                mActionBarView.setSplitWhenNarrow(splitWhenNarrow);
+            if (mActionBarBottom != null && mDecorToolbar.canSplit()) {
+                mDecorToolbar.setSplitView(mActionBarBottom);
+                mDecorToolbar.setSplitToolbar(splitActionBar);
+                mDecorToolbar.setSplitWhenNarrow(splitWhenNarrow);
 
                 final ActionBarContextView cab = (ActionBarContextView) findViewById(
                         com.android.internal.R.id.action_context_bar);
                 cab.setSplitView(mActionBarBottom);
-                cab.setSplitActionBar(splitActionBar);
+                cab.setSplitToolbar(splitActionBar);
                 cab.setSplitWhenNarrow(splitWhenNarrow);
             } else if (splitActionBar) {
                 Log.e(TAG, "Requested split action bar with " +
@@ -724,91 +736,91 @@
     @Override
     public boolean hasIcon() {
         pullChildren();
-        return mActionBarView.hasIcon();
+        return mDecorToolbar.hasIcon();
     }
 
     @Override
     public boolean hasLogo() {
         pullChildren();
-        return mActionBarView.hasLogo();
+        return mDecorToolbar.hasLogo();
     }
 
     @Override
     public void setIcon(int resId) {
         pullChildren();
-        mActionBarView.setIcon(resId);
+        mDecorToolbar.setIcon(resId);
     }
 
     @Override
     public void setIcon(Drawable d) {
         pullChildren();
-        mActionBarView.setIcon(d);
+        mDecorToolbar.setIcon(d);
     }
 
     @Override
     public void setLogo(int resId) {
         pullChildren();
-        mActionBarView.setLogo(resId);
+        mDecorToolbar.setLogo(resId);
     }
 
     @Override
     public boolean canShowOverflowMenu() {
         pullChildren();
-        return mActionBarView.isOverflowReserved() && mActionBarView.getVisibility() == VISIBLE;
+        return mDecorToolbar.canShowOverflowMenu();
     }
 
     @Override
     public boolean isOverflowMenuShowing() {
         pullChildren();
-        return mActionBarView.isOverflowMenuShowing();
+        return mDecorToolbar.isOverflowMenuShowing();
     }
 
     @Override
     public boolean isOverflowMenuShowPending() {
         pullChildren();
-        return mActionBarView.isOverflowMenuShowPending();
+        return mDecorToolbar.isOverflowMenuShowPending();
     }
 
     @Override
     public boolean showOverflowMenu() {
         pullChildren();
-        return mActionBarView.showOverflowMenu();
+        return mDecorToolbar.showOverflowMenu();
     }
 
     @Override
     public boolean hideOverflowMenu() {
         pullChildren();
-        return mActionBarView.hideOverflowMenu();
+        return mDecorToolbar.hideOverflowMenu();
     }
 
     @Override
     public void setMenuPrepared() {
         pullChildren();
-        mActionBarView.setMenuPrepared();
+        mDecorToolbar.setMenuPrepared();
     }
 
     @Override
     public void setMenu(Menu menu, MenuPresenter.Callback cb) {
         pullChildren();
-        mActionBarView.setMenu(menu, cb);
+        mDecorToolbar.setMenu(menu, cb);
     }
 
     @Override
     public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
         pullChildren();
-        mActionBarView.saveHierarchyState(toolbarStates);
+        mDecorToolbar.saveHierarchyState(toolbarStates);
     }
 
     @Override
     public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
         pullChildren();
-        mActionBarView.restoreHierarchyState(toolbarStates);
+        mDecorToolbar.restoreHierarchyState(toolbarStates);
     }
 
     @Override
     public void dismissPopups() {
         pullChildren();
-        mActionBarView.dismissPopupMenus();
+        mDecorToolbar.dismissPopupMenus();
     }
 
     public static class LayoutParams extends MarginLayoutParams {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 60631b9..af82778 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -18,7 +18,6 @@
 
 import android.animation.LayoutTransition;
 import android.app.ActionBar;
-import android.app.ActionBar.OnNavigationListener;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -63,7 +62,7 @@
 /**
  * @hide
  */
-public class ActionBarView extends AbsActionBarView {
+public class ActionBarView extends AbsActionBarView implements DecorToolbar {
     private static final String TAG = "ActionBarView";
 
     /**
@@ -117,8 +116,7 @@
 
     private boolean mUserTitle;
     private boolean mIncludeTabs;
-    private boolean mIsCollapsable;
-    private boolean mIsCollapsed;
+    private boolean mIsCollapsible;
     private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
 
     private MenuBuilder mOptionsMenu;
@@ -129,7 +127,7 @@
     private ActionMenuItem mLogoNavItem;
 
     private SpinnerAdapter mSpinnerAdapter;
-    private OnNavigationListener mCallback;
+    private AdapterView.OnItemSelectedListener mNavItemSelectedListener;
 
     private Runnable mTabSelector;
 
@@ -138,18 +136,6 @@
 
     Window.Callback mWindowCallback;
 
-    private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
-            new AdapterView.OnItemSelectedListener() {
-        public void onItemSelected(AdapterView parent, View view, int position, long id) {
-            if (mCallback != null) {
-                mCallback.onNavigationItemSelected(position, id);
-            }
-        }
-        public void onNothingSelected(AdapterView parent) {
-            // Do nothing
-        }
-    };
-
     private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -178,8 +164,6 @@
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
                 com.android.internal.R.attr.actionBarStyle, 0);
 
-        ApplicationInfo appInfo = context.getApplicationInfo();
-        PackageManager pm = context.getPackageManager();
         mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
                 ActionBar.NAVIGATION_MODE_STANDARD);
         mTitle = a.getText(R.styleable.ActionBar_title);
@@ -260,7 +244,7 @@
         }
 
         if (mHomeDescriptionRes != 0) {
-            setHomeActionContentDescription(mHomeDescriptionRes);
+            setNavigationContentDescription(mHomeDescriptionRes);
         }
 
         if (mTabScrollView != null && mIncludeTabs) {
@@ -313,7 +297,7 @@
     }
 
     @Override
-    public void setSplitActionBar(boolean splitActionBar) {
+    public void setSplitToolbar(boolean splitActionBar) {
         if (mSplitActionBar != splitActionBar) {
             if (mMenuView != null) {
                 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
@@ -349,18 +333,26 @@
                     mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
                 }
             }
-            super.setSplitActionBar(splitActionBar);
+            super.setSplitToolbar(splitActionBar);
         }
     }
 
-    public boolean isSplitActionBar() {
+    public boolean isSplit() {
         return mSplitActionBar;
     }
 
+    public boolean canSplit() {
+        return true;
+    }
+
     public boolean hasEmbeddedTabs() {
         return mIncludeTabs;
     }
 
+    public void setEmbeddedTabView(View view) {
+        setEmbeddedTabView((ScrollingTabContainerView) view);
+    }
+
     public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
         if (mTabScrollView != null) {
             removeView(mTabScrollView);
@@ -376,10 +368,6 @@
         }
     }
 
-    public void setCallback(OnNavigationListener callback) {
-        mCallback = callback;
-    }
-
     public void setMenuPrepared() {
         mMenuPrepared = true;
     }
@@ -473,7 +461,7 @@
         }
     }
 
-    public void setCustomNavigationView(View view) {
+    public void setCustomView(View view) {
         final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
         if (showCustom) {
             ActionBarTransition.beginDelayedTransition(this);
@@ -765,15 +753,16 @@
         }
     }
 
-    public void setDropdownAdapter(SpinnerAdapter adapter) {
+    public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener l) {
         mSpinnerAdapter = adapter;
+        mNavItemSelectedListener = l;
         if (mSpinner != null) {
             mSpinner.setAdapter(adapter);
         }
     }
 
-    public SpinnerAdapter getDropdownAdapter() {
-        return mSpinnerAdapter;
+    public int getDropdownItemCount() {
+        return mSpinnerAdapter != null ? mSpinnerAdapter.getCount() : 0;
     }
 
     public void setDropdownSelectedPosition(int position) {
@@ -784,7 +773,7 @@
         return mSpinner.getSelectedItemPosition();
     }
 
-    public View getCustomNavigationView() {
+    public View getCustomView() {
         return mCustomNavView;
     }
 
@@ -797,6 +786,11 @@
     }
 
     @Override
+    public ViewGroup getViewGroup() {
+        return this;
+    }
+
+    @Override
     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
         // Used by custom nav views if they don't supply layout params. Everything else
         // added to an ActionBarView should have them already.
@@ -860,12 +854,8 @@
         mContextView = view;
     }
 
-    public void setCollapsable(boolean collapsable) {
-        mIsCollapsable = collapsable;
-    }
-
-    public boolean isCollapsed() {
-        return mIsCollapsed;
+    public void setCollapsible(boolean collapsible) {
+        mIsCollapsible = collapsible;
     }
 
     /**
@@ -893,7 +883,7 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int childCount = getChildCount();
-        if (mIsCollapsable) {
+        if (mIsCollapsible) {
             int visibleChildren = 0;
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
@@ -915,11 +905,9 @@
             if (visibleChildren == 0) {
                 // No size for an empty action bar when collapsable.
                 setMeasuredDimension(0, 0);
-                mIsCollapsed = true;
                 return;
             }
         }
-        mIsCollapsed = false;
 
         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
         if (widthMode != MeasureSpec.EXACTLY) {
@@ -1323,20 +1311,20 @@
         }
     }
 
-    public void setHomeAsUpIndicator(Drawable indicator) {
+    public void setNavigationIcon(Drawable indicator) {
         mHomeLayout.setUpIndicator(indicator);
     }
 
-    public void setHomeAsUpIndicator(int resId) {
+    public void setNavigationIcon(int resId) {
         mHomeLayout.setUpIndicator(resId);
     }
 
-    public void setHomeActionContentDescription(CharSequence description) {
+    public void setNavigationContentDescription(CharSequence description) {
         mHomeDescription = description;
         updateHomeAccessibility(mUpGoerFive.isEnabled());
     }
 
-    public void setHomeActionContentDescription(int resId) {
+    public void setNavigationContentDescription(int resId) {
         mHomeDescriptionRes = resId;
         mHomeDescription = resId != 0 ? getResources().getText(resId) : null;
         updateHomeAccessibility(mUpGoerFive.isEnabled());
diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java
new file mode 100644
index 0000000..ee6988e
--- /dev/null
+++ b/core/java/com/android/internal/widget/DecorToolbar.java
@@ -0,0 +1,94 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.SpinnerAdapter;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Common interface for a toolbar that sits as part of the window decor.
+ * Layouts that control window decor use this as a point of interaction with different
+ * bar implementations.
+ *
+ * @hide
+ */
+public interface DecorToolbar {
+    ViewGroup getViewGroup();
+    Context getContext();
+    boolean isSplit();
+    boolean hasExpandedActionView();
+    void collapseActionView();
+    void setWindowCallback(Window.Callback cb);
+    void setWindowTitle(CharSequence title);
+    CharSequence getTitle();
+    void setTitle(CharSequence title);
+    CharSequence getSubtitle();
+    void setSubtitle(CharSequence subtitle);
+    void initProgress();
+    void initIndeterminateProgress();
+    boolean canSplit();
+    void setSplitView(ViewGroup splitView);
+    void setSplitToolbar(boolean split);
+    void setSplitWhenNarrow(boolean splitWhenNarrow);
+    boolean hasIcon();
+    boolean hasLogo();
+    void setIcon(int resId);
+    void setIcon(Drawable d);
+    void setLogo(int resId);
+    void setLogo(Drawable d);
+    boolean canShowOverflowMenu();
+    boolean isOverflowMenuShowing();
+    boolean isOverflowMenuShowPending();
+    boolean showOverflowMenu();
+    boolean hideOverflowMenu();
+    void setMenuPrepared();
+    void setMenu(Menu menu, MenuPresenter.Callback cb);
+    void dismissPopupMenus();
+
+    int getDisplayOptions();
+    void setDisplayOptions(int opts);
+    void setEmbeddedTabView(View tabView);
+    boolean hasEmbeddedTabs();
+    boolean isTitleTruncated();
+    void setCollapsible(boolean collapsible);
+    void setHomeButtonEnabled(boolean enable);
+    int getNavigationMode();
+    void setNavigationMode(int mode);
+    void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener listener);
+    void setDropdownSelectedPosition(int position);
+    int getDropdownSelectedPosition();
+    int getDropdownItemCount();
+    void setCustomView(View view);
+    View getCustomView();
+    void animateToVisibility(int visibility);
+    void setNavigationIcon(Drawable icon);
+    void setNavigationIcon(int resId);
+    void setNavigationContentDescription(CharSequence description);
+    void setNavigationContentDescription(int resId);
+    void saveHierarchyState(SparseArray<Parcelable> toolbarStates);
+    void restoreHierarchyState(SparseArray<Parcelable> toolbarStates);
+}
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
new file mode 100644
index 0000000..f90aaea
--- /dev/null
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -0,0 +1,526 @@
+/*
+ * 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.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ActionMenuPresenter;
+import android.widget.AdapterView;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.Toolbar;
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuItem;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPresenter;
+
+/**
+ * Internal class used to interact with the Toolbar widget without
+ * exposing interface methods to the public API.
+ *
+ * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView
+ * so that either variant acting as a
+ * {@link com.android.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave
+ * in the same way.</p>
+ *
+ * @hide
+ */
+public class ToolbarWidgetWrapper implements DecorToolbar {
+    private static final String TAG = "ToolbarWidgetWrapper";
+
+    private static final int AFFECTS_LOGO_MASK =
+            ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO;
+
+    private Toolbar mToolbar;
+
+    private int mDisplayOpts;
+    private View mTabView;
+    private Spinner mSpinner;
+    private View mCustomView;
+
+    private Drawable mIcon;
+    private Drawable mLogo;
+    private Drawable mNavIcon;
+
+    private boolean mTitleSet;
+    private CharSequence mTitle;
+    private CharSequence mSubtitle;
+
+    private Window.Callback mWindowCallback;
+    private boolean mMenuPrepared;
+    private ActionMenuPresenter mActionMenuPresenter;
+
+    public ToolbarWidgetWrapper(Toolbar toolbar) {
+        mToolbar = toolbar;
+
+        final TypedArray a = toolbar.getContext().obtainStyledAttributes(null,
+                R.styleable.ActionBar, R.attr.actionBarStyle, 0);
+
+        final CharSequence title = a.getText(R.styleable.ActionBar_title);
+        if (title != null) {
+            setTitle(title);
+        }
+
+        final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
+        if (subtitle != null) {
+            setSubtitle(subtitle);
+        }
+
+        final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo);
+        if (logo != null) {
+            setLogo(logo);
+        }
+
+        final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
+        if (icon != null) {
+            setIcon(icon);
+        }
+
+        final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
+        if (navIcon != null) {
+            setNavigationIcon(navIcon);
+        }
+
+        setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
+
+        final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
+        if (customNavId != 0) {
+            setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,
+                    mToolbar, false));
+            setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM);
+        }
+
+        final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+        if (height > 0) {
+            final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams();
+            lp.height = height;
+            mToolbar.setLayoutParams(lp);
+        }
+
+        final int contentInsetStart = a.getDimensionPixelOffset(
+                R.styleable.ActionBar_contentInsetStart, 0);
+        final int contentInsetEnd = a.getDimensionPixelOffset(
+                R.styleable.ActionBar_contentInsetEnd, 0);
+        if (contentInsetStart > 0 || contentInsetEnd > 0) {
+            mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd);
+        }
+
+        a.recycle();
+
+        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
+            final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(),
+                    0, android.R.id.home, 0, 0, mTitle);
+            @Override
+            public void onClick(View v) {
+                if (mWindowCallback != null && mMenuPrepared) {
+                    mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem);
+                }
+            }
+        });
+    }
+
+    @Override
+    public ViewGroup getViewGroup() {
+        return mToolbar;
+    }
+
+    @Override
+    public Context getContext() {
+        return mToolbar.getContext();
+    }
+
+    @Override
+    public boolean isSplit() {
+        return false;
+    }
+
+    @Override
+    public boolean hasExpandedActionView() {
+        return mToolbar.hasExpandedActionView();
+    }
+
+    @Override
+    public void collapseActionView() {
+        mToolbar.collapseActionView();
+    }
+
+    @Override
+    public void setWindowCallback(Window.Callback cb) {
+        mWindowCallback = cb;
+    }
+
+    @Override
+    public void setWindowTitle(CharSequence title) {
+        // "Real" title always trumps window title.
+        if (!mTitleSet) {
+            setTitleInt(title);
+        }
+    }
+
+    @Override
+    public CharSequence getTitle() {
+        return mToolbar.getTitle();
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mTitleSet = true;
+        setTitleInt(title);
+    }
+
+    private void setTitleInt(CharSequence title) {
+        mTitle = title;
+        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            mToolbar.setTitle(title);
+        }
+    }
+
+    @Override
+    public CharSequence getSubtitle() {
+        return mToolbar.getSubtitle();
+    }
+
+    @Override
+    public void setSubtitle(CharSequence subtitle) {
+        mSubtitle = subtitle;
+        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            mToolbar.setSubtitle(subtitle);
+        }
+    }
+
+    @Override
+    public void initProgress() {
+        Log.i(TAG, "Progress display unsupported");
+    }
+
+    @Override
+    public void initIndeterminateProgress() {
+        Log.i(TAG, "Progress display unsupported");
+    }
+
+    @Override
+    public boolean canSplit() {
+        return false;
+    }
+
+    @Override
+    public void setSplitView(ViewGroup splitView) {
+    }
+
+    @Override
+    public void setSplitToolbar(boolean split) {
+        if (split) {
+            throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar");
+        }
+    }
+
+    @Override
+    public void setSplitWhenNarrow(boolean splitWhenNarrow) {
+        // Ignore.
+    }
+
+    @Override
+    public boolean hasIcon() {
+        return mIcon != null;
+    }
+
+    @Override
+    public boolean hasLogo() {
+        return mLogo != null;
+    }
+
+    @Override
+    public void setIcon(int resId) {
+        setIcon(resId != 0 ? getContext().getDrawable(resId) : null);
+    }
+
+    @Override
+    public void setIcon(Drawable d) {
+        mIcon = d;
+        updateToolbarLogo();
+    }
+
+    @Override
+    public void setLogo(int resId) {
+        setLogo(resId != 0 ? getContext().getDrawable(resId) : null);
+    }
+
+    @Override
+    public void setLogo(Drawable d) {
+        mLogo = d;
+        updateToolbarLogo();
+    }
+
+    private void updateToolbarLogo() {
+        Drawable logo = null;
+        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) {
+            if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) {
+                logo = mLogo != null ? mLogo : mIcon;
+            } else {
+                logo = mIcon;
+            }
+        }
+        mToolbar.setLogo(logo);
+    }
+
+    @Override
+    public boolean canShowOverflowMenu() {
+        return mToolbar.canShowOverflowMenu();
+    }
+
+    @Override
+    public boolean isOverflowMenuShowing() {
+        return mToolbar.isOverflowMenuShowing();
+    }
+
+    @Override
+    public boolean isOverflowMenuShowPending() {
+        return mToolbar.isOverflowMenuShowPending();
+    }
+
+    @Override
+    public boolean showOverflowMenu() {
+        return mToolbar.showOverflowMenu();
+    }
+
+    @Override
+    public boolean hideOverflowMenu() {
+        return mToolbar.hideOverflowMenu();
+    }
+
+    @Override
+    public void setMenuPrepared() {
+        mMenuPrepared = true;
+    }
+
+    @Override
+    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
+        if (mActionMenuPresenter == null) {
+            mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext());
+            mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
+        }
+        mActionMenuPresenter.setCallback(cb);
+        mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter);
+    }
+
+    @Override
+    public void dismissPopupMenus() {
+        mToolbar.dismissPopupMenus();
+    }
+
+    @Override
+    public int getDisplayOptions() {
+        return mDisplayOpts;
+    }
+
+    @Override
+    public void setDisplayOptions(int newOpts) {
+        final int oldOpts = mDisplayOpts;
+        final int changed = oldOpts ^ newOpts;
+        mDisplayOpts = newOpts;
+        if (changed != 0) {
+            if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+                if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+                    mToolbar.setNavigationIcon(mNavIcon);
+                } else {
+                    mToolbar.setNavigationIcon(null);
+                }
+            }
+
+            if ((changed & AFFECTS_LOGO_MASK) != 0) {
+                updateToolbarLogo();
+            }
+
+            if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+                if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+                    mToolbar.setTitle(mTitle);
+                    mToolbar.setSubtitle(mSubtitle);
+                } else {
+                    mToolbar.setTitle(null);
+                    mToolbar.setSubtitle(null);
+                }
+            }
+
+            if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) {
+                if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+                    mToolbar.addView(mCustomView);
+                } else {
+                    mToolbar.removeView(mCustomView);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void setEmbeddedTabView(View tabView) {
+        mTabView = tabView;
+    }
+
+    @Override
+    public boolean hasEmbeddedTabs() {
+        return mTabView != null;
+    }
+
+    @Override
+    public boolean isTitleTruncated() {
+        return mToolbar.isTitleTruncated();
+    }
+
+    @Override
+    public void setCollapsible(boolean collapsible) {
+        // Ignore
+    }
+
+    @Override
+    public void setHomeButtonEnabled(boolean enable) {
+        // Ignore
+    }
+
+    @Override
+    public int getNavigationMode() {
+        return 0;
+    }
+
+    @Override
+    public void setNavigationMode(int mode) {
+        if (mode != ActionBar.NAVIGATION_MODE_STANDARD) {
+            throw new IllegalArgumentException(
+                    "Navigation modes not supported in this configuration");
+        }
+    }
+
+    @Override
+    public void setDropdownParams(SpinnerAdapter adapter,
+            AdapterView.OnItemSelectedListener listener) {
+        if (mSpinner == null) {
+            mSpinner = new Spinner(getContext());
+        }
+        mSpinner.setAdapter(adapter);
+        mSpinner.setOnItemSelectedListener(listener);
+    }
+
+    @Override
+    public void setDropdownSelectedPosition(int position) {
+        if (mSpinner == null) {
+            throw new IllegalStateException(
+                    "Can't set dropdown selected position without an adapter");
+        }
+        mSpinner.setSelection(position);
+    }
+
+    @Override
+    public int getDropdownSelectedPosition() {
+        return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0;
+    }
+
+    @Override
+    public int getDropdownItemCount() {
+        return mSpinner != null ? mSpinner.getCount() : 0;
+    }
+
+    @Override
+    public void setCustomView(View view) {
+        if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+            mToolbar.removeView(mCustomView);
+        }
+        mCustomView = view;
+        if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+            mToolbar.addView(mCustomView);
+        }
+    }
+
+    @Override
+    public View getCustomView() {
+        return mCustomView;
+    }
+
+    @Override
+    public void animateToVisibility(int visibility) {
+        if (visibility == View.GONE) {
+            mToolbar.animate().translationY(mToolbar.getHeight()).alpha(0)
+                    .setListener(new AnimatorListenerAdapter() {
+                        private boolean mCanceled = false;
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            if (!mCanceled) {
+                                mToolbar.setVisibility(View.GONE);
+                            }
+                        }
+
+                        @Override
+                        public void onAnimationCancel(Animator animation) {
+                            mCanceled = true;
+                        }
+                    });
+        } else if (visibility == View.VISIBLE) {
+            mToolbar.animate().translationY(0).alpha(1)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            mToolbar.setVisibility(View.VISIBLE);
+                        }
+                    });
+        }
+    }
+
+    @Override
+    public void setNavigationIcon(Drawable icon) {
+        mNavIcon = icon;
+        if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+            mToolbar.setNavigationIcon(icon);
+        }
+    }
+
+    @Override
+    public void setNavigationIcon(int resId) {
+        setNavigationIcon(mToolbar.getContext().getDrawable(resId));
+    }
+
+    @Override
+    public void setNavigationContentDescription(CharSequence description) {
+        mToolbar.setNavigationContentDescription(description);
+    }
+
+    @Override
+    public void setNavigationContentDescription(int resId) {
+        mToolbar.setNavigationContentDescription(resId);
+    }
+
+    @Override
+    public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) {
+        mToolbar.saveHierarchyState(toolbarStates);
+    }
+
+    @Override
+    public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) {
+        mToolbar.restoreHierarchyState(toolbarStates);
+    }
+
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 835a648..f446c3a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -138,6 +138,7 @@
 	android_hardware_Camera.cpp \
 	android_hardware_camera2_CameraMetadata.cpp \
 	android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
+	android_hardware_camera2_DngCreator.cpp \
 	android_hardware_SensorManager.cpp \
 	android_hardware_SerialPort.cpp \
 	android_hardware_UsbDevice.cpp \
@@ -172,8 +173,10 @@
 	$(call include-path-for, bluedroid) \
 	$(call include-path-for, libhardware)/hardware \
 	$(call include-path-for, libhardware_legacy)/hardware_legacy \
+	$(TOP)/bionic/libc/dns/include \
 	$(TOP)/frameworks/av/include \
 	$(TOP)/system/media/camera/include \
+	$(TOP)/system/netd/include \
 	external/pdfium/core/include/fpdfapi \
 	external/pdfium/core/include/fpdfdoc \
 	external/pdfium/fpdfsdk/include \
@@ -232,6 +235,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 2d350e0..e069876 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -80,6 +80,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_camera2_DngCreator(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);
@@ -788,7 +789,7 @@
     }
 
     // libart tolerates libdvm flags, but not vice versa, so only pass some options if libart.
-    property_get("persist.sys.dalvik.vm.lib.1", dalvikVmLibBuf, "libdvm.so");
+    property_get("persist.sys.dalvik.vm.lib.2", dalvikVmLibBuf, "libart.so");
     bool libart = (strncmp(dalvikVmLibBuf, "libart", 6) == 0);
 
     if (libart) {
@@ -1286,6 +1287,7 @@
     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_camera2_DngCreator),
     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/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 041790f..3bab8a2 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -31,9 +31,14 @@
 
 namespace android {
 
-static jlong FontFamily_create(JNIEnv* env, jobject clazz) {
+static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
 #ifdef USE_MINIKIN
-    return (jlong)new FontFamily();
+    FontLanguage fontLanguage;
+    if (lang != NULL) {
+        ScopedUtfChars str(env, lang);
+        fontLanguage = FontLanguage(str.c_str(), str.size());
+    }
+    return (jlong)new FontFamily(fontLanguage, variant);
 #else
     return 0;
 #endif
@@ -67,7 +72,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static JNINativeMethod gFontFamilyMethods[] = {
-    { "nCreateFamily",            "()J", (void*)FontFamily_create },
+    { "nCreateFamily",            "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",             "(J)V", (void*)FontFamily_unref },
     { "nAddFont",                 "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
 };
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index ee04d6f..79381ad 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -28,11 +28,17 @@
     layout->setFontCollection(resolvedFace->fFontCollection);
     FontStyle style = resolvedFace->fStyle;
     char css[256];
-    sprintf(css, "font-size: %d; font-weight: %d; font-style: %s; -minikin-bidi: %d",
+    int off = snprintf(css, sizeof(css),
+        "font-size: %d; font-weight: %d; font-style: %s; -minikin-bidi: %d;",
         (int)paint->getTextSize(),
         style.getWeight() * 100,
         style.getItalic() ? "italic" : "normal",
         flags);
+    SkString langString = paint->getPaintOptionsAndroid().getLanguage().getTag();
+    off += snprintf(css + off, sizeof(css) - off, " lang: %s;", langString.c_str());
+    SkPaintOptionsAndroid::FontVariant var = paint->getPaintOptionsAndroid().getFontVariant();
+    const char* varstr = var == SkPaintOptionsAndroid::kElegant_Variant ? "elegant" : "compact";
+    off += snprintf(css + off, sizeof(css) - off, " -minikin-variant: %s;", varstr);
     layout->setProperties(css);
 }
 
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_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 0d2df80..79353299 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -39,6 +39,9 @@
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 
+#include <sys/types.h> // for socketpair
+#include <sys/socket.h> // for socketpair
+
 #if defined(LOG_NNDEBUG)
 #if !LOG_NNDEBUG
 #define ALOGVV ALOGV
@@ -351,6 +354,119 @@
     }
 }
 
+struct DumpMetadataParams {
+    int writeFd;
+    const CameraMetadata* metadata;
+};
+
+static void* CameraMetadata_writeMetadataThread(void* arg) {
+    DumpMetadataParams* p = static_cast<DumpMetadataParams*>(arg);
+
+    /*
+     * Write the dumped data, and close the writing side FD.
+     */
+    p->metadata->dump(p->writeFd, /*verbosity*/2);
+
+    if (close(p->writeFd) < 0) {
+        ALOGE("%s: Failed to close writeFd (errno = %#x, message = '%s')",
+                __FUNCTION__, errno, strerror(errno));
+    }
+
+    return NULL;
+}
+
+static void CameraMetadata_dump(JNIEnv *env, jobject thiz) {
+    ALOGV("%s", __FUNCTION__);
+    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+    if (metadata == NULL) {
+        return;
+    }
+
+    /*
+     * Create a socket pair for local streaming read/writes.
+     *
+     * The metadata will be dumped into the write side,
+     * and then read back out (and logged) via the read side.
+     */
+
+    int writeFd, readFd;
+    {
+
+        int sv[2];
+        if (socketpair(AF_LOCAL, SOCK_STREAM, /*protocol*/0, &sv[0]) < 0) {
+            jniThrowExceptionFmt(env, "java/io/IOException",
+                    "Failed to create socketpair (errno = %#x, message = '%s')",
+                    errno, strerror(errno));
+            return;
+        }
+        writeFd = sv[0];
+        readFd = sv[1];
+    }
+
+    /*
+     * Create a thread for doing the writing.
+     *
+     * The reading and writing must be concurrent, otherwise
+     * the write will block forever once it exhausts the capped
+     * buffer size (from getsockopt).
+     */
+    pthread_t writeThread;
+    DumpMetadataParams params = {
+        writeFd,
+        metadata
+    };
+
+    {
+        int threadRet = pthread_create(&writeThread, /*attr*/NULL,
+                CameraMetadata_writeMetadataThread, (void*)&params);
+
+        if (threadRet != 0) {
+            close(writeFd);
+
+            jniThrowExceptionFmt(env, "java/io/IOException",
+                    "Failed to create thread for writing (errno = %#x, message = '%s')",
+                    threadRet, strerror(threadRet));
+        }
+    }
+
+    /*
+     * Read out a byte until stream is complete. Write completed lines
+     * to ALOG.
+     */
+    {
+        char out[] = {'\0', '\0'}; // large enough to append as a string
+        String8 logLine;
+
+        // Read one byte at a time! Very slow but avoids complicated \n scanning.
+        ssize_t res;
+        while ((res = TEMP_FAILURE_RETRY(read(readFd, &out[0], /*count*/1))) > 0) {
+            if (out[0] == '\n') {
+                ALOGD("%s", logLine.string());
+                logLine.clear();
+            } else {
+                logLine.append(out);
+            }
+        }
+
+        if (res < 0) {
+            jniThrowExceptionFmt(env, "java/io/IOException",
+                    "Failed to read from fd (errno = %#x, message = '%s')",
+                    errno, strerror(errno));
+            //return;
+        } else if (!logLine.isEmpty()) {
+            ALOGD("%s", logLine.string());
+        }
+    }
+
+    int res;
+
+    // Join until thread finishes. Ensures params/metadata is valid until then.
+    if ((res = pthread_join(writeThread, /*retval*/NULL)) != 0) {
+        ALOGE("%s: Failed to join thread (errno = %#x, message = '%s')",
+                __FUNCTION__, res, strerror(res));
+    }
+}
+
 static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
     ALOGV("%s", __FUNCTION__);
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
@@ -436,6 +552,9 @@
   { "nativeWriteValues",
     "(I[B)V",
     (void *)CameraMetadata_writeValues },
+  { "nativeDump",
+    "()V",
+    (void *)CameraMetadata_dump },
 // Parcelable interface
   { "nativeReadFromParcel",
     "(Landroid/os/Parcel;)V",
diff --git a/media/jni/android_media_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
similarity index 97%
rename from media/jni/android_media_DngCreator.cpp
rename to core/jni/android_hardware_camera2_DngCreator.cpp
index 860d896..7b686e7 100644
--- a/media/jni/android_media_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -54,7 +54,7 @@
         return; \
     }
 
-#define ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID     "mNativeContext"
+#define ANDROID_DNGCREATOR_CTX_JNI_ID     "mNativeContext"
 
 static struct {
     jfieldID mNativeContext;
@@ -163,9 +163,10 @@
     ALOGV("%s:", __FUNCTION__);
 
     gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
-            ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID, "J");
+            ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
     LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
-            "can't find android/media/DngCreator.%s", ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID);
+            "can't find android/hardware/camera2/DngCreator.%s",
+            ANDROID_DNGCREATOR_CTX_JNI_ID);
 
     jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
     LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
@@ -766,7 +767,8 @@
             (void*) DngCreator_nativeWriteInputStream},
 };
 
-int register_android_media_DngCreator(JNIEnv *env) {
+int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
     return AndroidRuntime::registerNativeMethods(env,
-                   "android/media/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
+                   "android/hardware/camera2/DngCreator", gDngCreatorMethods,
+                   NELEM(gDngCreatorMethods));
 }
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_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 867c1b1..26022e0 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -48,6 +48,12 @@
     renderNode->output();
 }
 
+static jint android_view_RenderNode_getDebugSize(JNIEnv* env,
+        jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->getDebugSize();
+}
+
 static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring name) {
     RenderNode* renderNode = new RenderNode();
     renderNode->incStrong(0);
@@ -505,6 +511,7 @@
     { "nDestroyRenderNode",   "(J)V",   (void*) android_view_RenderNode_destroyRenderNode },
     { "nSetDisplayListData",   "(JJ)V", (void*) android_view_RenderNode_setDisplayListData },
     { "nOutput",               "(J)V",  (void*) android_view_RenderNode_output },
+    { "nGetDebugSize",         "(J)I",  (void*) android_view_RenderNode_getDebugSize },
 
     { "nSetCaching",           "(JZ)V",  (void*) android_view_RenderNode_setCaching },
     { "nSetStaticMatrix",      "(JJ)V",  (void*) android_view_RenderNode_setStaticMatrix },
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 48fb729..bd016fd 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -238,10 +238,11 @@
 }
 
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
-        jint dirtyRight, jint dirtyBottom) {
+        jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density,
+        jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    return proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+    return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density,
+            dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
 }
 
 static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -293,12 +294,31 @@
     proxy->destroyLayer(layer);
 }
 
+static void android_view_ThreadedRenderer_flushCaches(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint flushMode) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->flushCaches(static_cast<Caches::FlushMode>(flushMode));
+}
+
 static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz,
         jlong proxyPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     proxy->fence();
 }
 
+static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->notifyFramePending();
+}
+
+static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject javaFileDescriptor) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    proxy->dumpProfileInfo(fd);
+}
+
 #endif
 
 // ----------------------------------------------------------------------------
@@ -320,7 +340,7 @@
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
-    { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nSyncAndDrawFrame", "(JJJFIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
@@ -328,7 +348,10 @@
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
     { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
     { "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
+    { "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches },
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
+    { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
+    { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
 #endif
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1874fd8..14141d7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1014,6 +1014,13 @@
         android:description="@string/permdesc_sim_communication"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows TvInputService to access underlying TV input hardware such as
+         built-in tuners and HDMI-in's.
+         @hide This should only be used by OEM's TvInputService's.
+    -->
+    <permission android:name="android.permission.TV_INPUT_HARDWARE"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- =========================================== -->
     <!-- Permissions associated with audio capture -->
     <!-- =========================================== -->
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index e87352f..4c4f6a4 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* //device/apps/common/res/anim/fade_out.xml
-**
-** Copyright 2007, The Android Open Source Project
+/* Copyright 2007, 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. 
@@ -19,7 +17,7 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
 	android:shareInterpolator="false">
-    <translate android:fromYDelta="0" android:toYDelta="10%"
+    <translate android:fromYDelta="0" android:toYDelta="-20%"
 			android:interpolator="@interpolator/accelerate_quint"
             android:duration="@android:integer/config_shortAnimTime"/>
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index cb47b3c..4a956d7 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -20,9 +20,8 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:background="#ff000000" android:shareInterpolator="false">
     <alpha
-        android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:fromAlpha="1.0" android:toAlpha="1.0"
         android:fillEnabled="true" android:fillBefore="true"
         android:interpolator="@interpolator/decelerate_quint"
-        android:startOffset="@android:integer/config_shortAnimTime"
-        android:duration="@android:integer/config_shortAnimTime"/>
+        android:duration="0"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml b/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
index c29fd1a..f7a6a65 100644
--- a/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_wallpaper_behind_enter.xml
@@ -23,6 +23,6 @@
         android:fromAlpha="0.0" android:toAlpha="1.0"
         android:fillEnabled="true" android:fillBefore="true"
         android:interpolator="@interpolator/decelerate_quad"
-        android:startOffset="@android:integer/config_shortAnimTime"
+        android:startOffset="@android:integer/config_mediumAnimTime"
         android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/voice_activity_close_enter.xml b/core/res/res/anim/voice_activity_close_enter.xml
new file mode 100644
index 0000000..4f3d3d5
--- /dev/null
+++ b/core/res/res/anim/voice_activity_close_enter.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:shareInterpolator="false">
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/anim/voice_activity_close_exit.xml b/core/res/res/anim/voice_activity_close_exit.xml
new file mode 100644
index 0000000..023b012
--- /dev/null
+++ b/core/res/res/anim/voice_activity_close_exit.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:shareInterpolator="false">
+    <translate android:fromYDelta="0" android:toYDelta="-20%"
+			android:interpolator="@interpolator/accelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/anim/voice_activity_open_enter.xml b/core/res/res/anim/voice_activity_open_enter.xml
new file mode 100644
index 0000000..57fba2a
--- /dev/null
+++ b/core/res/res/anim/voice_activity_open_enter.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/fade_in.xml
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+		android:shareInterpolator="false">
+    <translate android:fromYDelta="-20%" android:toYDelta="0"
+	        android:interpolator="@interpolator/decelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+			android:interpolator="@interpolator/decelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/core/res/res/anim/voice_activity_open_exit.xml b/core/res/res/anim/voice_activity_open_exit.xml
new file mode 100644
index 0000000..4f3d3d5
--- /dev/null
+++ b/core/res/res/anim/voice_activity_open_exit.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:shareInterpolator="false">
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/anim/voice_layer_enter.xml b/core/res/res/anim/voice_layer_enter.xml
new file mode 100644
index 0000000..57fba2a
--- /dev/null
+++ b/core/res/res/anim/voice_layer_enter.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/fade_in.xml
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+		android:shareInterpolator="false">
+    <translate android:fromYDelta="-20%" android:toYDelta="0"
+	        android:interpolator="@interpolator/decelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+			android:interpolator="@interpolator/decelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/core/res/res/anim/voice_layer_exit.xml b/core/res/res/anim/voice_layer_exit.xml
new file mode 100644
index 0000000..023b012
--- /dev/null
+++ b/core/res/res/anim/voice_layer_exit.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+	android:shareInterpolator="false">
+    <translate android:fromYDelta="0" android:toYDelta="-20%"
+			android:interpolator="@interpolator/accelerate_quint"
+            android:duration="@android:integer/config_shortAnimTime"/>
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+			android:interpolator="@interpolator/accelerate_cubic"
+            android:duration="@android:integer/config_shortAnimTime"/>
+</set>
diff --git a/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
index 598b98c..9cdc25b 100644
--- a/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
index 79fe7c5..276d480 100644
--- a/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
index 506a186..95c0168 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
index fb0e926..569332a 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
index 38b8e8b..a01ac10 100644
--- a/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
index d6002a7..d3602d9 100644
--- a/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
index 93469a2..75085ce 100644
--- a/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
index b3493e7..e2eb5be 100644
--- a/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable/ic_audio_ring_notif.xml b/core/res/res/drawable/ic_audio_ring_notif.xml
index 247d1b4..b52db5c 100644
--- a/core/res/res/drawable/ic_audio_ring_notif.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_ring_notif_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#8A000000"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_audio_ring_notif_mute.xml b/core/res/res/drawable/ic_audio_ring_notif_mute.xml
index 72aaa9d..8d7d6cb 100644
--- a/core/res/res/drawable/ic_audio_ring_notif_mute.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif_mute.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_ring_notif_mute_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#8A000000"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
+</vector>
diff --git a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
index 9e31aba..2f1d940 100644
--- a/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
+++ b/core/res/res/drawable/ic_audio_ring_notif_vibrate.xml
@@ -1,23 +1,28 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
- * Copyright 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
+Copyright (C) 2014 The Android Open Source Project
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_ring_notif_vibrate_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#8A000000"
+        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
+</vector>
diff --git a/core/res/res/layout/global_actions_silent_mode.xml b/core/res/res/layout/global_actions_silent_mode.xml
index 79401af..a358623 100644
--- a/core/res/res/layout/global_actions_silent_mode.xml
+++ b/core/res/res/layout/global_actions_silent_mode.xml
@@ -37,7 +37,7 @@
             android:layout_marginEnd="8dp"
             android:layout_marginTop="6dp"
             android:layout_marginBottom="6dp"
-            android:src="@drawable/ic_audio_vol_mute"
+            android:src="@drawable/ic_audio_ring_notif_mute"
             android:scaleType="center"
             android:duplicateParentState="true"
             android:background="@drawable/silent_mode_indicator"
@@ -94,7 +94,7 @@
             android:layout_marginEnd="8dp"
             android:layout_marginTop="6dp"
             android:layout_marginBottom="6dp"
-            android:src="@drawable/ic_audio_vol"
+            android:src="@drawable/ic_audio_ring_notif"
             android:scaleType="center"
             android:duplicateParentState="true"
             android:background="@drawable/silent_mode_indicator"
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
new file mode 100644
index 0000000..290c7da
--- /dev/null
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+This is an optimized layout for a screen with a toolbar enabled.
+-->
+
+<com.android.internal.widget.ActionBarOverlayLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/decor_content_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:splitMotionEvents="false"
+    android:theme="?attr/actionBarTheme">
+    <FrameLayout android:id="@android:id/content"
+                 android:layout_width="match_parent"
+                 android:layout_height="match_parent" />
+    <com.android.internal.widget.ActionBarContainer
+        android:id="@+id/action_bar_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        style="?attr/actionBarStyle"
+        android:viewName="android:action_bar"
+        android:gravity="top">
+        <Toolbar
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?attr/toolbarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            style="?attr/actionModeStyle" />
+    </com.android.internal.widget.ActionBarContainer>
+</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
deleted file mode 100644
index 3ad1f23..0000000
--- a/core/res/res/layout/volume_adjust.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/visible_panel"
-    android:orientation="horizontal"
-    android:layout_width="300dp"
-    android:layout_height="wrap_content">
-
-    <LinearLayout
-        android:id="@+id/slider_group"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical">
-        <!-- Sliders go here -->
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/expand_button_divider"
-        android:src="?attr/dividerVertical"
-        android:layout_width="wrap_content"
-        android:layout_height="32dip"
-        android:scaleType="fitXY"
-        android:layout_gravity="top"
-        android:layout_marginTop="16dip"
-        android:layout_marginBottom="16dip" />
-
-    <ImageView
-        android:id="@+id/expand_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:padding="16dip"
-        android:background="?attr/selectableItemBackground"
-        android:src="@drawable/ic_sysbar_quicksettings" />
-
-</LinearLayout>
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..5fec907 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -493,6 +493,9 @@
              {@link android.view.Window#setAllowExitTransitionOverlap(boolean)}. -->
         <attr name="windowAllowExitTransitionOverlap" format="boolean"/>
 
+        <!-- Internal layout used internally for window decor -->
+        <attr name="windowActionBarFullscreenDecorLayout" format="reference" />
+
         <!-- ============ -->
         <!-- Alert Dialog styles -->
         <!-- ============ -->
@@ -685,6 +688,7 @@
         <!-- Default ActivityChooserView style. -->
         <attr name="activityChooserViewStyle" format="reference" />
 
+        <!-- Default Toolbar style. -->
         <attr name="toolbarStyle" format="reference" />
 
         <!-- Fast scroller styles -->
@@ -1713,6 +1717,7 @@
         <attr name="windowSwipeToDismiss" />
         <attr name="windowContentTransitions" />
         <attr name="windowContentTransitionManager" />
+        <attr name="windowActionBarFullscreenDecorLayout" />
 
         <!-- The minimum width the window is allowed to be, along the major
              axis of the screen.  That is, when in landscape.  Can be either
@@ -6398,11 +6403,17 @@
         <attr name="indeterminateProgressStyle" format="reference" />
         <!-- Specifies the horizontal padding on either end for an embedded progress bar. -->
         <attr name="progressBarPadding" format="dimension" />
+        <!-- Up navigation glyph -->
+        <attr name="homeAsUpIndicator" />
         <!-- Specifies padding that should be applied to the left and right sides of
              system-provided items in the bar. -->
         <attr name="itemPadding" format="dimension" />
         <!-- Set true to hide the action bar on a vertical nested scroll of content. -->
         <attr name="hideOnContentScroll" format="boolean" />
+        <attr name="contentInsetStart" format="dimension" />
+        <attr name="contentInsetEnd" format="dimension" />
+        <attr name="contentInsetLeft" format="dimension" />
+        <attr name="contentInsetRight" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="ActionMode">
@@ -6653,10 +6664,19 @@
         <attr name="titleMarginEnd" format="dimension" />
         <attr name="titleMarginTop" format="dimension" />
         <attr name="titleMarginBottom" format="dimension" />
-        <attr name="contentInsetStart" format="dimension" />
-        <attr name="contentInsetEnd" format="dimension" />
-        <attr name="contentInsetLeft" format="dimension" />
-        <attr name="contentInsetRight" format="dimension" />
+        <attr name="contentInsetStart" />
+        <attr name="contentInsetEnd" />
+        <attr name="contentInsetLeft" />
+        <attr name="contentInsetRight" />
+        <attr name="maxButtonHeight" format="dimension" />
+        <attr name="navigationButtonStyle" format="reference" />
+        <attr name="buttonGravity">
+            <!-- Push object to the top of its container, not changing its size. -->
+            <flag name="top" value="0x30" />
+            <!-- Push object to the bottom of its container, not changing its size. -->
+            <flag name="bottom" value="0x50" />
+        </attr>
+        <attr name="collapseIcon" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="Toolbar_LayoutParams">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b1f256e..acfbe2d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -885,41 +885,57 @@
          be passed a persistable Bundle in their Intent.extras. -->
     <attr name="persistable" format="boolean" />
 
-    <!-- Specify whether this activity should always be launched in doc-centric mode. For
-         values other than <code>none</code> the activity must be defined with
-         {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
-         This attribute can be overridden by {@link
-         android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+    <!-- This attribute specifies that an activity shall become the root activity of a
+         new task each time it is launched. Using this attribute permits the user to
+         have multiple documents from the same applications appear in the recent tasks list.
 
-         <p>If this attribute is not specified, <code>none</code> will be used.
-         Note that this launch behavior can be changed in some ways at runtime
-         through the {@link android.content.Intent} flags
-         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. -->
+         <p>Such a document is any kind of item for which an application may want to
+         maintain multiple simultaneous instances. Examples might be text files, web
+         pages, spreadsheets, or emails. Each such document will be in a separate
+         task in the recent taskss list.
+
+         <p>This attribute is equivalent to adding the flag {@link
+         android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch
+         the activity.
+
+         <p>The documentLaunchMode attribute may be assigned one of three values, "none",
+         "intoExisting" and "always", described in detail below. For values other than
+         <code>none</code> the activity must be defined with
+         {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
+         If this attribute is not specified, <code>none</code> will be used.
+         Note that <code>none</code> can be overridden at run time if the Intent used
+         to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+         Similarly <code>intoExisting</code> will be overridden by the flag
+         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} combined with
+         {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. -->
     <attr name="documentLaunchMode">
         <!-- The default mode, which will create a new task only when
              {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
              Intent.FLAG_ACTIVITY_NEW_TASK} is set. -->
         <enum name="none" value="0" />
-        <!-- All tasks will be searched for a matching Intent. If one is found
-             That task will cleared and restarted with the root activity receiving a call
+        <!-- All tasks will be searched for one whose base Intent's ComponentName and
+             data URI match those of the launching Intent. If such a task is found
+             that task will be cleared and restarted with the root activity receiving a call
              to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no
              such task is found a new task will be created.
-             This is the equivalent of with {@link
+             <p>This is the equivalent of launching an activity with {@link
              android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
-             without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
-             Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+             set and without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+             Intent.FLAG_ACTIVITY_MULTIPLE_TASK} set. -->
         <enum name="intoExisting" value="1" />
-        <!-- A new task rooted at this activity will be created.
-             This is the equivalent of with {@link
+        <!-- A new task rooted at this activity will be created. This will happen whether or
+             not there is an existing task whose ComponentName and data URI match
+             that of the launcing intent This is the equivalent of launching an activity
+             with {@link
              android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
-             paired with {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
-             Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+             and {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+             Intent.FLAG_ACTIVITY_MULTIPLE_TASK} both set. -->
         <enum name="always" value="2" />
     </attr>
 
-    <!-- Tasks launched by activities with this attribute will remain in the recent task
+    <!-- Tasks launched by activities with this attribute will remain in the recent tasks
          list until the last activity in the task is completed. When that happens the task
-         will be automatically removed from the recent task list.
+         will be automatically removed from the recent tasks list.
 
          This attribute is the equivalent of {@link
          android.content.Intent#FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5375c14..e9d8ccc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1538,9 +1538,6 @@
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
-        <item>airplane</item>
-        <item>bugreport</item>
-        <item>silent</item>
         <item>users</item>
     </string-array>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 52b021f..657f614 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>
@@ -206,9 +202,6 @@
     <!-- Default width for a textview error popup -->
     <dimen name="textview_error_popup_default_width">240dip</dimen>
 
-    <!-- Volume panel y offset -->
-    <dimen name="volume_panel_top">16dp</dimen>
-
     <!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. -->
     <dimen name="default_app_widget_padding_left">8dp</dimen>
     <dimen name="default_app_widget_padding_top">8dp</dimen>
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index 53e97fd..2defee2 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -47,6 +47,10 @@
     <dimen name="text_size_menu_quantum">14sp</dimen>
     <dimen name="text_size_button_quantum">14sp</dimen>
 
+    <dimen name="text_size_large_quantum">22sp</dimen>
+    <dimen name="text_size_medium_quantum">18sp</dimen>
+    <dimen name="text_size_small_quantum">14sp</dimen>
+
     <dimen name="floating_window_z">16dp</dimen>
     <dimen name="floating_window_margin">32dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ce0d2d5..7dc967c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2181,9 +2181,6 @@
 
   <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.xml b/core/res/res/values/styles.xml
index 4a27ebe..933063f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -171,8 +171,8 @@
 
     <!-- Window animations that are applied to voice interaction overlay windows. -->
     <style name="Animation.VoiceInteractionSession">
-        <item name="windowEnterAnimation">@anim/input_method_enter</item>
-        <item name="windowExitAnimation">@anim/input_method_exit</item>
+        <item name="windowEnterAnimation">@anim/voice_layer_enter</item>
+        <item name="windowExitAnimation">@anim/voice_layer_exit</item>
     </style>
 
     <!-- Special optional fancy IM animations. @hide -->
@@ -277,37 +277,6 @@
         <item name="android:textColor">#CCCCCC</item>
     </style>
 
-    <style name="TextAppearance.StatusBar.Quantum">
-    </style>
-    <style name="TextAppearance.StatusBar.Quantum.EventContent">
-        <item name="android:textColor">#90000000</item>
-        <item name="android:textSize">@dimen/notification_text_size</item>
-    </style>
-    <style name="TextAppearance.StatusBar.Quantum.EventContent.Title">
-        <item name="android:textColor">#DD000000</item>
-        <item name="android:textSize">@dimen/notification_title_text_size</item>
-    </style>
-    <style name="TextAppearance.StatusBar.Quantum.EventContent.Line2">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
-    </style>
-    <style name="TextAppearance.StatusBar.Quantum.EventContent.Info">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
-    </style>
-    <style name="TextAppearance.StatusBar.Quantum.EventContent.Time">
-        <item name="android:textSize">@dimen/notification_subtext_size</item>
-    </style>
-    <style name="TextAppearance.StatusBar.Quantum.EventContent.Emphasis">
-        <item name="android:textColor">#66000000</item>
-    </style>
-    <style name="Widget.StatusBar.Quantum.ProgressBar"
-           parent="Widget.Quantum.Light.ProgressBar.Horizontal">
-        <item name="android:progressDrawable">@drawable/notification_quantum_media_progress</item>
-    </style>
-
-    <style name="Widget.StatusBar.Quantum.ProgressBar"
-           parent="Widget.Quantum.Light.ProgressBar.Horizontal">
-    </style>
-
     <style name="TextAppearance.Small.CalendarViewWeekDayView">
         <item name="android:textStyle">bold</item>
     </style>
@@ -1228,6 +1197,16 @@
         <item name="android:subtitleTextAppearance">@android:style/TextAppearance.Widget.Toolbar.Subtitle</item>
         <item name="android:minHeight">?android:attr/actionBarSize</item>
         <item name="android:titleMargins">4dp</item>
+        <item name="android:maxButtonHeight">56dp</item>
+        <item name="android:buttonGravity">top</item>
+        <item name="android:navigationButtonStyle">@android:style/Widget.Toolbar.Button.Navigation</item>
+        <item name="android:collapseIcon">?android:attr/homeAsUpIndicator</item>
+    </style>
+
+    <style name="Widget.Toolbar.Button.Navigation" parent="@android:style/Widget">
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+        <item name="android:minWidth">56dp</item>
+        <item name="android:scaleType">center</item>
     </style>
 
     <style name="TextAppearance.Widget.ActionBar.Title"
@@ -2425,7 +2404,6 @@
         <item name="android:background">@android:drawable/ab_transparent_light_holo</item>
         <item name="android:backgroundStacked">@android:drawable/ab_stacked_transparent_light_holo</item>
         <item name="android:backgroundSplit">@android:drawable/ab_bottom_transparent_light_holo</item>
-        <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
         <item name="android:progressBarStyle">@android:style/Widget.Holo.Light.ProgressBar.Horizontal</item>
         <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.Light.ProgressBar</item>
     </style>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 4cd2244..6943533 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -182,7 +182,10 @@
         <item name="textColorLink">?attr/textColorLinkInverse</item>
     </style>
 
-    <style name="TextAppearance.Quantum.Large" parent="TextAppearance.Quantum.Headline" />
+    <style name="TextAppearance.Quantum.Large">
+        <item name="textSize">@dimen/text_size_large_quantum</item>
+        <item name="textColor">?attr/textColorPrimary</item>
+    </style>
 
     <style name="TextAppearance.Quantum.Large.Inverse">
         <item name="textColor">?attr/textColorPrimaryInverse</item>
@@ -191,7 +194,10 @@
         <item name="textColorLink">?attr/textColorLinkInverse</item>
     </style>
 
-    <style name="TextAppearance.Quantum.Medium" parent="TextAppearance.Quantum.Body1" />
+    <style name="TextAppearance.Quantum.Medium">
+        <item name="textSize">@dimen/text_size_medium_quantum</item>
+        <item name="textColor">?attr/textColorSecondary</item>
+    </style>
 
     <style name="TextAppearance.Quantum.Medium.Inverse">
         <item name="textColor">?attr/textColorSecondaryInverse</item>
@@ -200,7 +206,10 @@
         <item name="textColorLink">?attr/textColorLinkInverse</item>
     </style>
 
-    <style name="TextAppearance.Quantum.Small" parent="TextAppearance.Quantum.Caption" />
+    <style name="TextAppearance.Quantum.Small">
+        <item name="textSize">@dimen/text_size_small_quantum</item>
+        <item name="textColor">?attr/textColorTertiary</item>
+    </style>
 
     <style name="TextAppearance.Quantum.Small.Inverse">
         <item name="textColor">?attr/textColorTertiaryInverse</item>
@@ -351,6 +360,38 @@
         <item name="textStyle">bold</item>
     </style>
 
+    <style name="TextAppearance.StatusBar.Quantum" />
+
+    <style name="TextAppearance.StatusBar.Quantum.EventContent">
+        <item name="android:textColor">#90000000</item>
+        <item name="android:textSize">@dimen/notification_text_size</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Quantum.EventContent.Title">
+        <item name="android:textColor">#DD000000</item>
+        <item name="android:textSize">@dimen/notification_title_text_size</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Quantum.EventContent.Line2">
+        <item name="android:textSize">@dimen/notification_subtext_size</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Quantum.EventContent.Info">
+        <item name="android:textSize">@dimen/notification_subtext_size</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Quantum.EventContent.Time">
+        <item name="android:textSize">@dimen/notification_subtext_size</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Quantum.EventContent.Emphasis">
+        <item name="android:textColor">#66000000</item>
+    </style>
+
+    <style name="Widget.StatusBar.Quantum.ProgressBar" parent="Widget.Quantum.Light.ProgressBar.Horizontal">
+        <item name="android:progressDrawable">@drawable/notification_quantum_media_progress</item>
+    </style>
+
     <!-- Widget Styles -->
 
     <style name="Quantum"/>
@@ -732,7 +773,7 @@
         <item name="background">@null</item>
         <item name="backgroundStacked">@null</item>
         <item name="backgroundSplit">@null</item>
-        <item name="displayOptions">useLogo|showHome|showTitle</item>
+        <item name="displayOptions">showTitle</item>
         <item name="divider">?attr/dividerVertical</item>
         <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item>
         <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dcff978..6e1629b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -69,8 +69,6 @@
   <java-symbol type="id" name="edittext_container" />
   <java-symbol type="id" name="enter_pin_section" />
   <java-symbol type="id" name="expand_activities_button" />
-  <java-symbol type="id" name="expand_button" />
-  <java-symbol type="id" name="expand_button_divider" />
   <java-symbol type="id" name="expires_on" />
   <java-symbol type="id" name="find_next" />
   <java-symbol type="id" name="find_prev" />
@@ -161,9 +159,7 @@
   <java-symbol type="id" name="share" />
   <java-symbol type="id" name="shortcut" />
   <java-symbol type="id" name="skip_button" />
-  <java-symbol type="id" name="slider_group" />
   <java-symbol type="id" name="split_action_bar" />
-  <java-symbol type="id" name="stream_icon" />
   <java-symbol type="id" name="submit_area" />
   <java-symbol type="id" name="switch_new" />
   <java-symbol type="id" name="switch_old" />
@@ -181,7 +177,6 @@
   <java-symbol type="id" name="topPanel" />
   <java-symbol type="id" name="up" />
   <java-symbol type="id" name="value" />
-  <java-symbol type="id" name="visible_panel" />
   <java-symbol type="id" name="websearch" />
   <java-symbol type="id" name="wifi_p2p_wps_pin" />
   <java-symbol type="id" name="year" />
@@ -343,7 +338,6 @@
   <java-symbol type="dimen" name="search_view_preferred_width" />
   <java-symbol type="dimen" name="textview_error_popup_default_width" />
   <java-symbol type="dimen" name="toast_y_offset" />
-  <java-symbol type="dimen" name="volume_panel_top" />
   <java-symbol type="dimen" name="action_bar_stacked_max_height" />
   <java-symbol type="dimen" name="action_bar_stacked_tab_max_width" />
   <java-symbol type="dimen" name="notification_text_size" />
@@ -1199,8 +1193,6 @@
   <java-symbol type="layout" name="time_picker_legacy" />
   <java-symbol type="layout" name="time_picker_dialog" />
   <java-symbol type="layout" name="transient_notification" />
-  <java-symbol type="layout" name="volume_adjust" />
-  <java-symbol type="layout" name="volume_adjust_item" />
   <java-symbol type="layout" name="voice_interaction_session" />
   <java-symbol type="layout" name="web_text_view_dropdown" />
   <java-symbol type="layout" name="webview_find" />
@@ -1294,6 +1286,10 @@
   <java-symbol type="anim" name="dock_left_exit" />
   <java-symbol type="anim" name="dock_right_enter" />
   <java-symbol type="anim" name="dock_right_exit" />
+  <java-symbol type="anim" name="voice_activity_close_exit" />
+  <java-symbol type="anim" name="voice_activity_close_enter" />
+  <java-symbol type="anim" name="voice_activity_open_exit" />
+  <java-symbol type="anim" name="voice_activity_open_enter" />
 
   <java-symbol type="array" name="config_hdmiCecLogicalDeviceType" />
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
@@ -1870,6 +1866,7 @@
   <java-symbol type="attr" name="toolbarStyle" />
   <java-symbol type="attr" name="titleTextAppearance" />
   <java-symbol type="attr" name="subtitleTextAppearance" />
+  <java-symbol type="attr" name="windowActionBarFullscreenDecorLayout" />
   <java-symbol type="drawable" name="ic_lock_bugreport" />
   <java-symbol type="id" name="icon_frame" />
   <java-symbol type="style" name="Animation.VolumePanel" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index aaab949..41f4ff8 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -192,6 +192,7 @@
         <item name="windowDrawsSystemBarBackgrounds">false</item>
         <item name="statusBarColor">@android:color/black</item>
         <item name="navigationBarColor">@android:color/black</item>
+        <item name="windowActionBarFullscreenDecorLayout">@layout/screen_action_bar</item>
 
         <!-- Define these here; ContextThemeWrappers around themes that define them should
              always clear these values. -->
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index bb787bb..484c694 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -162,6 +162,7 @@
         <item name="windowActionBar">true</item>
         <item name="windowActionModeOverlay">false</item>
         <item name="windowDrawsSystemBarBackgrounds">true</item>
+        <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
         <item name="statusBarColor">?attr/colorPrimaryDark</item>
         <item name="navigationBarColor">?attr/colorPrimaryDark</item>
 
@@ -505,6 +506,7 @@
         <item name="windowActionBar">true</item>
         <item name="windowActionModeOverlay">false</item>
         <item name="windowDrawsSystemBarBackgrounds">true</item>
+        <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
         <item name="statusBarColor">?attr/colorPrimaryDark</item>
         <item name="navigationBarColor">?attr/colorPrimaryDark</item>
 
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..2cdbe43
--- /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..15c9b4e 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..a0abf30 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..67b5394 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..d9fb64a 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..1fd1d31 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..c63c115
--- /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..cd7c835
--- /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..9cb4a5a 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..f02f100 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..12a2ce0 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..1079af6 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..e7f13c2 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..7fa04481 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..96b75dd 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..7a2c164
--- 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..734cc40 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/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index a759a79..6802b9a 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -30,10 +30,22 @@
     public long mNativePtr;
 
     public FontFamily() {
-        mNativePtr = nCreateFamily();
-        mNativePtr = nCreateFamily();
+        mNativePtr = nCreateFamily(null, 0);
         if (mNativePtr == 0) {
-            throw new RuntimeException();
+            throw new IllegalStateException("error creating native FontFamily");
+        }
+    }
+
+    public FontFamily(String lang, String variant) {
+        int varEnum = 0;
+        if ("compact".equals(variant)) {
+            varEnum = 1;
+        } else if ("elegant".equals(variant)) {
+            varEnum = 2;
+        }
+        mNativePtr = nCreateFamily(lang, varEnum);
+        if (mNativePtr == 0) {
+            throw new IllegalStateException("error creating native FontFamily");
         }
     }
 
@@ -50,7 +62,7 @@
         return nAddFont(mNativePtr, path.getAbsolutePath());
     }
 
-    static native long nCreateFamily();
+    static native long nCreateFamily(String lang, int variant);
     static native void nUnrefFamily(long nativePtr);
     static native boolean nAddFont(long nativeFamily, String path);
 }
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index f304f4e..a863a06 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -34,14 +34,18 @@
 public class FontListParser {
 
     public static class Family {
-        public Family(List<String> names, List<String> fontFiles) {
+        public Family(List<String> names, List<String> fontFiles, String lang, String variant) {
             this.names = names;
             this.fontFiles = fontFiles;
+            this.lang = lang;
+            this.variant = variant;
         }
 
         public List<String> names;
         // todo: need attributes for font files
         public List<String> fontFiles;
+        public String lang;
+        public String variant;
     }
 
     /* Parse fallback list (no names) */
@@ -75,6 +79,8 @@
             throws XmlPullParserException, IOException {
         List<String> names = null;
         List<String> fontFiles = new ArrayList<String>();
+        String lang = null;
+        String variant = null;
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
@@ -82,6 +88,12 @@
                 while (parser.next() != XmlPullParser.END_TAG) {
                     if (parser.getEventType() != XmlPullParser.START_TAG) continue;
                     if (parser.getName().equals("file")) {
+                        if (lang == null) {
+                            lang = parser.getAttributeValue(null, "lang");
+                        }
+                        if (variant == null) {
+                            variant = parser.getAttributeValue(null, "variant");
+                        }
                         String filename = parser.nextText();
                         String fullFilename = "/system/fonts/" + filename;
                         fontFiles.add(fullFilename);
@@ -98,7 +110,7 @@
                 }
             }
         }
-        return new Family(names, fontFiles);
+        return new Family(names, fontFiles, lang, variant);
     }
 
     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
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/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 64451c4..2b07c3f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -66,6 +66,9 @@
     static Map<String, Typeface> sSystemFontMap;
     static FontFamily[] sFallbackFonts;
 
+    static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
+    static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
+
     /**
      * @hide
      */
@@ -242,17 +245,23 @@
 
     private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
         // TODO: expand to handle attributes like lang and variant
-        FontFamily fontFamily = new FontFamily();
+        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
         for (String fontFile : family.fontFiles) {
             fontFamily.addFont(new File(fontFile));
         }
         return fontFamily;
     }
 
-    static {
+    /*
+     * (non-Javadoc)
+     *
+     * This should only be called once, from the static class initializer block.
+     */
+    private static void init() {
         // Load font config and initialize Minikin state
-        String systemConfigFilename = "/system/etc/system_fonts.xml";
-        String configFilename = "/system/etc/fallback_fonts.xml";
+        File systemFontConfigLocation = getSystemFontConfigLocation();
+        File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
+        File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
         try {
             // TODO: throws an exception non-Minikin builds, to fail early;
             // remove when Minikin-only
@@ -301,7 +310,10 @@
         } catch (XmlPullParserException e) {
             Log.e(TAG, "XML parse exception for " + configFilename);
         }
+    }
 
+    static {
+        init();
         // Set up defaults and typefaces exposed in public API
         DEFAULT         = create((String) null, 0);
         DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
@@ -315,6 +327,11 @@
             create((String) null, Typeface.ITALIC),
             create((String) null, Typeface.BOLD_ITALIC),
         };
+
+    }
+
+    private static File getSystemFontConfigLocation() {
+        return new File("/system/etc/");
     }
 
     @Override
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e3ed75e..9a63fa3 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -101,6 +101,7 @@
  * <dd>Sets the Miter limit for a stroked path</dd></dt>
  * </dl>
  * </dd>
+ * @hide
  */
 public class VectorDrawable extends Drawable {
     private static final String LOGTAG = VectorDrawable.class.getSimpleName();
@@ -159,7 +160,8 @@
 
     @Override
     public void setColorFilter(ColorFilter colorFilter) {
-        // TODO: support color filter
+        mVectorState.mVPathRenderer.setColorFilter(colorFilter);
+        invalidateSelf();
     }
 
     @Override
@@ -365,14 +367,15 @@
         private VPath[] mCurrentPaths;
         private Paint mStrokePaint;
         private Paint mFillPaint;
+        private ColorFilter mColorFilter;
         private PathMeasure mPathMeasure;
 
         private VGroup mCurrentGroup = new VGroup();
 
-        float mBaseWidth = 1;
-        float mBaseHeight = 1;
-        float mViewportWidth;
-        float mViewportHeight;
+        float mBaseWidth = 0;
+        float mBaseHeight = 0;
+        float mViewportWidth = 0;
+        float mViewportHeight = 0;
 
         public VPathRenderer() {
         }
@@ -413,6 +416,18 @@
             }
         }
 
+        public void setColorFilter(ColorFilter colorFilter) {
+            mColorFilter = colorFilter;
+
+            if (mFillPaint != null) {
+                mFillPaint.setColorFilter(colorFilter);
+            }
+
+            if (mStrokePaint != null) {
+                mStrokePaint.setColorFilter(colorFilter);
+            }
+        }
+
         public void draw(Canvas canvas, int w, int h) {
             if (mCurrentPaths == null) {
                 Log.e(LOGTAG,"mCurrentPaths == null");
@@ -470,6 +485,7 @@
             if (vPath.mFillColor != 0) {
                 if (mFillPaint == null) {
                     mFillPaint = new Paint();
+                    mFillPaint.setColorFilter(mColorFilter);
                     mFillPaint.setStyle(Paint.Style.FILL);
                     mFillPaint.setAntiAlias(true);
                 }
@@ -481,6 +497,7 @@
             if (vPath.mStrokeColor != 0) {
                 if (mStrokePaint == null) {
                     mStrokePaint = new Paint();
+                    mStrokePaint.setColorFilter(mColorFilter);
                     mStrokePaint.setStyle(Paint.Style.STROKE);
                     mStrokePaint.setAntiAlias(true);
                 }
@@ -516,24 +533,34 @@
         private void parseViewport(Resources r, AttributeSet attrs)
                 throws XmlPullParserException {
             final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableViewport);
-            mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, 0);
-            mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, 0);
-            if (mViewportWidth == 0 || mViewportHeight == 0) {
-                throw new XmlPullParserException(a.getPositionDescription()+
-                        "<viewport> tag requires viewportWidth & viewportHeight to be set");
+            mViewportWidth = a.getFloat(R.styleable.VectorDrawableViewport_viewportWidth, mViewportWidth);
+            mViewportHeight = a.getFloat(R.styleable.VectorDrawableViewport_viewportHeight, mViewportHeight);
+
+            if (mViewportWidth <= 0) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        "<viewport> tag requires viewportWidth > 0");
+            } else if (mViewportHeight <= 0) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        "<viewport> tag requires viewportHeight > 0");
             }
+
             a.recycle();
         }
 
         private void parseSize(Resources r, AttributeSet attrs)
                 throws XmlPullParserException  {
             final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableSize);
-            mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, 0);
-            mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, 0);
-            if (mBaseWidth == 0 || mBaseHeight == 0) {
-                throw new XmlPullParserException(a.getPositionDescription()+
-                        "<size> tag requires width & height to be set");
+            mBaseWidth = a.getDimension(R.styleable.VectorDrawableSize_width, mBaseWidth);
+            mBaseHeight = a.getDimension(R.styleable.VectorDrawableSize_height, mBaseHeight);
+
+            if (mBaseWidth <= 0) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        "<size> tag requires width > 0");
+            } else if (mBaseHeight <= 0) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        "<size> tag requires height > 0");
             }
+
             a.recycle();
         }
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 2cadf09..442f327 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -23,6 +23,7 @@
 		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		Dither.cpp \
+		DrawProfiler.cpp \
 		Extensions.cpp \
 		FboCache.cpp \
 		GradientCache.cpp \
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/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
new file mode 100644
index 0000000..971a66e
--- /dev/null
+++ b/libs/hwui/DrawProfiler.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "DrawProfiler.h"
+
+#include <cutils/compiler.h>
+
+#include "OpenGLRenderer.h"
+#include "Properties.h"
+
+#define DEFAULT_MAX_FRAMES 128
+
+#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return
+
+#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
+
+#define PROFILE_DRAW_WIDTH 3
+#define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
+#define PROFILE_DRAW_DP_PER_MS 7
+
+// Number of floats we want to display from FrameTimingData
+// If this is changed make sure to update the indexes below
+#define NUM_ELEMENTS 4
+
+#define RECORD_INDEX 0
+#define PREPARE_INDEX 1
+#define PLAYBACK_INDEX 2
+#define SWAPBUFFERS_INDEX 3
+
+// Must be NUM_ELEMENTS in size
+static const SkColor ELEMENT_COLORS[] = { 0xcf3e66cc, 0xcf8f00ff, 0xcfdc3912, 0xcfe69800 };
+static const SkColor CURRENT_FRAME_COLOR = 0xcf5faa4d;
+static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
+
+// We could get this from TimeLord and use the actual frame interval, but
+// this is good enough
+#define FRAME_THRESHOLD 16
+
+namespace android {
+namespace uirenderer {
+
+static int dpToPx(int dp, float density) {
+    return (int) (dp * density + 0.5f);
+}
+
+DrawProfiler::DrawProfiler()
+        : mType(kNone)
+        , mDensity(0)
+        , mData(NULL)
+        , mDataSize(0)
+        , mCurrentFrame(-1)
+        , mPreviousTime(0)
+        , mVerticalUnit(0)
+        , mHorizontalUnit(0)
+        , mThresholdStroke(0) {
+    setDensity(1);
+}
+
+DrawProfiler::~DrawProfiler() {
+    destroyData();
+}
+
+void DrawProfiler::setDensity(float density) {
+    if (CC_UNLIKELY(mDensity != density)) {
+        mDensity = density;
+        mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+        mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+        mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+    }
+}
+
+void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
+    RETURN_IF_DISABLED();
+    mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
+    mPreviousTime = systemTime(CLOCK_MONOTONIC);
+}
+
+void DrawProfiler::markPlaybackStart() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+}
+
+void DrawProfiler::markPlaybackEnd() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+}
+
+void DrawProfiler::finishFrame() {
+    RETURN_IF_DISABLED();
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
+    mPreviousTime = now;
+    mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
+}
+
+void DrawProfiler::unionDirty(Rect* dirty) {
+    RETURN_IF_DISABLED();
+    // Not worth worrying about minimizing the dirty region for debugging, so just
+    // dirty the entire viewport.
+    if (dirty) {
+        dirty->setEmpty();
+    }
+}
+
+void DrawProfiler::draw(OpenGLRenderer* canvas) {
+    if (CC_LIKELY(mType != kBars)) {
+        return;
+    }
+
+    prepareShapes(canvas->getViewportHeight());
+    drawGraph(canvas);
+    drawCurrentFrame(canvas);
+    drawThreshold(canvas);
+}
+
+void DrawProfiler::createData() {
+    if (mData) return;
+
+    mDataSize = property_get_int32(PROPERTY_PROFILE_MAXFRAMES, DEFAULT_MAX_FRAMES);
+    if (mDataSize <= 0) mDataSize = 1;
+    if (mDataSize > 4096) mDataSize = 4096; // Reasonable maximum
+    mData = (FrameTimingData*) calloc(mDataSize, sizeof(FrameTimingData));
+    mRects = new float*[NUM_ELEMENTS];
+    for (int i = 0; i < NUM_ELEMENTS; i++) {
+        // 4 floats per rect
+        mRects[i] = (float*) calloc(mDataSize, 4 * sizeof(float));
+    }
+    mCurrentFrame = 0;
+}
+
+void DrawProfiler::destroyData() {
+    delete mData;
+    mData = NULL;
+}
+
+void DrawProfiler::addRect(Rect& r, float data, float* shapeOutput) {
+    r.top = r.bottom - (data * mVerticalUnit);
+    shapeOutput[0] = r.left;
+    shapeOutput[1] = r.top;
+    shapeOutput[2] = r.right;
+    shapeOutput[3] = r.bottom;
+    r.bottom = r.top;
+}
+
+void DrawProfiler::prepareShapes(const int baseline) {
+    Rect r;
+    r.right = mHorizontalUnit;
+    for (int i = 0; i < mDataSize; i++) {
+        const int shapeIndex = i * 4;
+        r.bottom = baseline;
+        addRect(r, mData[i].record, mRects[RECORD_INDEX] + shapeIndex);
+        addRect(r, mData[i].prepare, mRects[PREPARE_INDEX] + shapeIndex);
+        addRect(r, mData[i].playback, mRects[PLAYBACK_INDEX] + shapeIndex);
+        addRect(r, mData[i].swapBuffers, mRects[SWAPBUFFERS_INDEX] + shapeIndex);
+        r.translate(mHorizontalUnit, 0);
+    }
+}
+
+void DrawProfiler::drawGraph(OpenGLRenderer* canvas) {
+    SkPaint paint;
+    for (int i = 0; i < NUM_ELEMENTS; i++) {
+        paint.setColor(ELEMENT_COLORS[i]);
+        canvas->drawRects(mRects[i], mDataSize * 4, &paint);
+    }
+}
+
+void DrawProfiler::drawCurrentFrame(OpenGLRenderer* canvas) {
+    // This draws a solid rect over the entirety of the current frame's shape
+    // To do so we use the bottom of mRects[0] and the top of mRects[NUM_ELEMENTS-1]
+    // which will therefore fully overlap the previously drawn rects
+    SkPaint paint;
+    paint.setColor(CURRENT_FRAME_COLOR);
+    const int i = mCurrentFrame * 4;
+    canvas->drawRect(mRects[0][i], mRects[NUM_ELEMENTS-1][i+1], mRects[0][i+2],
+            mRects[0][i+3], &paint);
+}
+
+void DrawProfiler::drawThreshold(OpenGLRenderer* canvas) {
+    SkPaint paint;
+    paint.setColor(THRESHOLD_COLOR);
+    paint.setStrokeWidth(mThresholdStroke);
+
+    float pts[4];
+    pts[0] = 0.0f;
+    pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+    pts[2] = canvas->getViewportWidth();
+    canvas->drawLines(pts, 4, &paint);
+}
+
+DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
+    ProfileType type = kNone;
+    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+        if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) {
+            type = kBars;
+        } else if (!strcmp(buf, "true")) {
+            type = kConsole;
+        }
+    }
+    return type;
+}
+
+bool DrawProfiler::loadSystemProperties() {
+    ProfileType newType = loadRequestedProfileType();
+    if (newType != mType) {
+        mType = newType;
+        if (mType == kNone) {
+            destroyData();
+        } else {
+            createData();
+        }
+        return true;
+    }
+    return false;
+}
+
+void DrawProfiler::dumpData(int fd) {
+    RETURN_IF_DISABLED();
+
+    // This method logs the last N frames (where N is <= mDataSize) since the
+    // last call to dumpData(). In other words if there's a dumpData(), draw frame,
+    // dumpData(), the last dumpData() should only log 1 frame.
+
+    const FrameTimingData emptyData = {0, 0, 0, 0};
+
+    FILE *file = fdopen(fd, "a");
+    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+
+    for (int frameOffset = 1; frameOffset <= mDataSize; frameOffset++) {
+        int i = (mCurrentFrame + frameOffset) % mDataSize;
+        if (!memcmp(mData + i, &emptyData, sizeof(FrameTimingData))) {
+            continue;
+        }
+        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+                mData[i].record, mData[i].prepare, mData[i].playback, mData[i].swapBuffers);
+    }
+    // reset the buffer
+    memset(mData, 0, sizeof(FrameTimingData) * mDataSize);
+    mCurrentFrame = 0;
+
+    fflush(file);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
new file mode 100644
index 0000000..c1aa1c6
--- /dev/null
+++ b/libs/hwui/DrawProfiler.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DRAWPROFILER_H
+#define DRAWPROFILER_H
+
+#include <utils/Timers.h>
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class OpenGLRenderer;
+
+class DrawProfiler {
+public:
+    DrawProfiler();
+    ~DrawProfiler();
+
+    bool loadSystemProperties();
+    void setDensity(float density);
+
+    void startFrame(nsecs_t recordDurationNanos = 0);
+    void markPlaybackStart();
+    void markPlaybackEnd();
+    void finishFrame();
+
+    void unionDirty(Rect* dirty);
+    void draw(OpenGLRenderer* canvas);
+
+    void dumpData(int fd);
+
+private:
+    enum ProfileType {
+        kNone,
+        kConsole,
+        kBars,
+    };
+
+    typedef struct {
+        float record;
+        float prepare;
+        float playback;
+        float swapBuffers;
+    } FrameTimingData;
+
+    void createData();
+    void destroyData();
+
+    void addRect(Rect& r, float data, float* shapeOutput);
+    void prepareShapes(const int baseline);
+    void drawGraph(OpenGLRenderer* canvas);
+    void drawCurrentFrame(OpenGLRenderer* canvas);
+    void drawThreshold(OpenGLRenderer* canvas);
+
+    ProfileType loadRequestedProfileType();
+
+    ProfileType mType;
+    float mDensity;
+
+    FrameTimingData* mData;
+    int mDataSize;
+
+    int mCurrentFrame;
+    nsecs_t mPreviousTime;
+
+    int mVerticalUnit;
+    int mHorizontalUnit;
+    int mThresholdStroke;
+
+    /*
+     * mRects represents an array of rect shapes, divided into NUM_ELEMENTS
+     * groups such that each group is drawn with the same paint.
+     * For example mRects[0] is the array of rect floats suitable for
+     * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
+     * information.
+     */
+    float** mRects;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DRAWPROFILER_H */
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..71836dd5 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/Properties.h b/libs/hwui/Properties.h
index 20b8f2f..12241b8 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -89,6 +89,36 @@
 #define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
 
 /**
+ *  System property used to enable or disable hardware rendering profiling.
+ * The default value of this property is assumed to be false.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "true", to enable profiling
+ * "visual_bars", to enable profiling and visualize the results on screen
+ * "false", to disable profiling
+ */
+#define PROPERTY_PROFILE "debug.hwui.profile"
+#define PROPERTY_PROFILE_VISUALIZE_BARS "visual_bars"
+
+/**
+ * System property used to specify the number of frames to be used
+ * when doing hardware rendering profiling.
+ * The default value of this property is #PROFILE_MAX_FRAMES.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "60", to set the limit of frames to 60
+ */
+#define PROPERTY_PROFILE_MAXFRAMES "debug.hwui.profile.maxframes"
+
+/**
  * Used to enable/disable non-rectangular clipping debugging.
  *
  * The accepted values are:
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index df74f31..d964efc 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -93,6 +93,17 @@
     ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
 }
 
+int RenderNode::getDebugSize() {
+    int size = sizeof(RenderNode);
+    if (mStagingDisplayListData) {
+        size += mStagingDisplayListData->allocator.usedSize();
+    }
+    if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
+        size += mDisplayListData->allocator.usedSize();
+    }
+    return size;
+}
+
 void RenderNode::prepareTree(TreeInfo& info) {
     ATRACE_CALL();
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1811a7b..1a5377b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -119,6 +119,7 @@
     void replayNodeInParent(ReplayStateStruct& replayStruct, const int level);
 
     ANDROID_API void output(uint32_t level = 1);
+    ANDROID_API int getDebugSize();
 
     bool isRenderable() const {
         return mDisplayListData && mDisplayListData->hasDrawOps;
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/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index d4a23b8..8355f83 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -52,6 +52,7 @@
             : hasFunctors(false)
             , hasAnimations(false)
             , requiresUiRedraw(false)
+            , canDrawThisFrame(true)
         {}
         bool hasFunctors;
         // This is only updated if evaluateAnimations is true
@@ -60,6 +61,13 @@
         // animate itself, such as if hasFunctors is true
         // This is only set if hasAnimations is true
         bool requiresUiRedraw;
+        // This is set to true if draw() can be called this frame
+        // false means that we must delay until the next vsync pulse as frame
+        // production is outrunning consumption
+        // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+        // *OR* will post itself for the next vsync automatically, use this
+        // only to avoid calling draw()
+        bool canDrawThisFrame;
     } out;
 
     // TODO: Damage calculations
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 839ef91..f91e90e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,6 @@
 
 #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
 #define GLES_VERSION 2
-#define USE_TEXTURE_ATLAS false
 
 // Android-specific addition that is used to show when frames began in systrace
 EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
@@ -229,7 +228,7 @@
 }
 
 void GlobalContext::initAtlas() {
-    if (USE_TEXTURE_ATLAS) {
+    if (mAtlasBuffer.get()) {
         Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
     }
 }
@@ -360,7 +359,9 @@
     setSurface(NULL);
 }
 
-void CanvasContext::setSurface(EGLNativeWindowType window) {
+void CanvasContext::setSurface(ANativeWindow* window) {
+    mNativeWindow = window;
+
     if (mEglSurface != EGL_NO_SURFACE) {
         mGlobalContext->destroySurface(mEglSurface);
         mEglSurface = EGL_NO_SURFACE;
@@ -393,7 +394,7 @@
     makeCurrent();
 }
 
-bool CanvasContext::initialize(EGLNativeWindowType window) {
+bool CanvasContext::initialize(ANativeWindow* window) {
     if (mCanvas) return false;
     setSurface(window);
     mCanvas = new OpenGLRenderer();
@@ -401,11 +402,11 @@
     return true;
 }
 
-void CanvasContext::updateSurface(EGLNativeWindowType window) {
+void CanvasContext::updateSurface(ANativeWindow* window) {
     setSurface(window);
 }
 
-void CanvasContext::pauseSurface(EGLNativeWindowType window) {
+void CanvasContext::pauseSurface(ANativeWindow* window) {
     // TODO: For now we just need a fence, in the future suspend any animations
     // and such to prevent from trying to render into this surface
 }
@@ -456,7 +457,15 @@
     info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
     mRootRenderNode->prepareTree(info);
 
-    if (info.out.hasAnimations) {
+    int runningBehind = 0;
+    // TODO: This query is moderately expensive, investigate adding some sort
+    // of fast-path based off when we last called eglSwapBuffers() as well as
+    // last vsync time. Or something.
+    mNativeWindow->query(mNativeWindow.get(),
+            NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+    info.out.canDrawThisFrame = !runningBehind;
+
+    if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
         if (info.out.hasFunctors) {
             info.out.requiresUiRedraw = true;
         } else if (!info.out.requiresUiRedraw) {
@@ -467,10 +476,17 @@
     }
 }
 
+void CanvasContext::notifyFramePending() {
+    ATRACE_CALL();
+    mRenderThread.pushBackFrameCallback(this);
+}
+
 void CanvasContext::draw(Rect* dirty) {
     LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
             "drawDisplayList called on a context with no canvas or surface!");
 
+    profiler().markPlaybackStart();
+
     EGLint width, height;
     mGlobalContext->beginFrame(mEglSurface, &width, &height);
     if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
@@ -478,6 +494,8 @@
         dirty = NULL;
     } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
         dirty = NULL;
+    } else {
+        profiler().unionDirty(dirty);
     }
 
     status_t status;
@@ -491,14 +509,17 @@
     Rect outBounds;
     status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
 
-    // TODO: Draw debug info
-    // TODO: Performance tracking
+    profiler().draw(mCanvas);
 
     mCanvas->finish();
 
+    profiler().markPlaybackEnd();
+
     if (status & DrawGlInfo::kStatusDrew) {
         swapBuffers();
     }
+
+    profiler().finishFrame();
 }
 
 // Called by choreographer to do an RT-driven animation
@@ -509,13 +530,17 @@
 
     ATRACE_CALL();
 
+    profiler().startFrame();
+
     TreeInfo info;
     info.evaluateAnimations = true;
     info.performStagingPush = false;
     info.prepareTextures = false;
 
     prepareTree(info);
-    draw(NULL);
+    if (info.out.canDrawThisFrame) {
+        draw(NULL);
+    }
 }
 
 void CanvasContext::invokeFunctor(Functor* functor) {
@@ -539,6 +564,13 @@
     return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
 }
 
+void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
+    if (mGlobalContext->hasContext()) {
+        requireGlContext();
+        Caches::getInstance().flush(flushMode);
+    }
+}
+
 void CanvasContext::runWithGlContext(RenderTask* task) {
     requireGlContext();
     task->run();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4232f27..a04269b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -23,6 +23,7 @@
 #include <utils/Functor.h>
 #include <utils/Vector.h>
 
+#include "../DrawProfiler.h"
 #include "../RenderNode.h"
 #include "RenderTask.h"
 #include "RenderThread.h"
@@ -48,9 +49,9 @@
     CanvasContext(bool translucent, RenderNode* rootRenderNode);
     virtual ~CanvasContext();
 
-    bool initialize(EGLNativeWindowType window);
-    void updateSurface(EGLNativeWindowType window);
-    void pauseSurface(EGLNativeWindowType window);
+    bool initialize(ANativeWindow* window);
+    void updateSurface(ANativeWindow* window);
+    void pauseSurface(ANativeWindow* window);
     void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
     void setOpaque(bool opaque);
     void makeCurrent();
@@ -63,6 +64,8 @@
 
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
+    void flushCaches(Caches::FlushMode flushMode);
+
     void invokeFunctor(Functor* functor);
 
     void runWithGlContext(RenderTask* task);
@@ -73,11 +76,17 @@
     ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
             int64_t* map, size_t mapSize);
 
+    void notifyFramePending();
+
+    DrawProfiler& profiler() { return mProfiler; }
+
 private:
+    friend class RegisterFrameCallbackTask;
+
     void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
     void prepareTree(TreeInfo& info);
 
-    void setSurface(EGLNativeWindowType window);
+    void setSurface(ANativeWindow* window);
     void swapBuffers();
     void requireSurface();
 
@@ -85,6 +94,7 @@
 
     GlobalContext* mGlobalContext;
     RenderThread& mRenderThread;
+    sp<ANativeWindow> mNativeWindow;
     EGLSurface mEglSurface;
     bool mDirtyRegionsEnabled;
 
@@ -93,6 +103,8 @@
     bool mHaveNewSurface;
 
     const sp<RenderNode> mRootRenderNode;
+
+    DrawProfiler mProfiler;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 3b8786c..7ea358f 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -34,6 +34,8 @@
         : mRenderThread(NULL)
         , mContext(NULL)
         , mFrameTimeNanos(0)
+        , mRecordDurationNanos(0)
+        , mDensity(1.0f) // safe enough default
         , mSyncResult(kSync_OK) {
 }
 
@@ -64,15 +66,17 @@
     mDirty.set(left, top, right, bottom);
 }
 
-int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
+int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
     mSyncResult = kSync_OK;
     mFrameTimeNanos = frameTimeNanos;
+    mRecordDurationNanos = recordDurationNanos;
     postAndWait();
 
     // Reset the single-frame data
     mFrameTimeNanos = 0;
+    mRecordDurationNanos = 0;
     mDirty.setEmpty();
 
     return mSyncResult;
@@ -87,7 +91,16 @@
 void DrawFrameTask::run() {
     ATRACE_NAME("DrawFrame");
 
-    bool canUnblockUiThread = syncFrameState();
+    mContext->profiler().setDensity(mDensity);
+    mContext->profiler().startFrame(mRecordDurationNanos);
+
+    bool canUnblockUiThread;
+    bool canDrawThisFrame;
+    {
+        TreeInfo info;
+        canUnblockUiThread = syncFrameState(info);
+        canDrawThisFrame = info.out.canDrawThisFrame;
+    }
 
     // Grab a copy of everything we need
     Rect dirty(mDirty);
@@ -98,7 +111,9 @@
         unblockUiThread();
     }
 
-    context->draw(&dirty);
+    if (CC_LIKELY(canDrawThisFrame)) {
+        context->draw(&dirty);
+    }
 
     if (!canUnblockUiThread) {
         unblockUiThread();
@@ -111,12 +126,11 @@
     info.evaluateAnimations = true;
 }
 
-bool DrawFrameTask::syncFrameState() {
+bool DrawFrameTask::syncFrameState(TreeInfo& info) {
     ATRACE_CALL();
     mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
     mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse();
-    TreeInfo info;
     initTreeInfo(info);
     mContext->prepareDraw(&mLayers, info);
     if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index b9307e1..30c8880 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -24,6 +24,7 @@
 #include "RenderTask.h"
 
 #include "../Rect.h"
+#include "../TreeInfo.h"
 
 namespace android {
 namespace uirenderer {
@@ -59,13 +60,14 @@
     void removeLayer(DeferredLayerUpdater* layer);
 
     void setDirty(int left, int top, int right, int bottom);
-    int drawFrame(nsecs_t frameTimeNanos);
+    void setDensity(float density) { mDensity = density; }
+    int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
 
     virtual void run();
 
 private:
     void postAndWait();
-    bool syncFrameState();
+    bool syncFrameState(TreeInfo& info);
     void unblockUiThread();
 
     Mutex mLock;
@@ -79,6 +81,8 @@
      *********************************************/
     Rect mDirty;
     nsecs_t mFrameTimeNanos;
+    nsecs_t mRecordDurationNanos;
+    float mDensity;
 
     int mSyncResult;
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ef8e45b..77c0aa7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -99,20 +99,24 @@
     post(task);
 }
 
-CREATE_BRIDGE0(loadSystemProperties) {
+CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
     bool needsRedraw = false;
     if (Caches::hasInstance()) {
         needsRedraw = Caches::getInstance().initProperties();
     }
+    if (args->context->profiler().loadSystemProperties()) {
+        needsRedraw = true;
+    }
     return (void*) needsRedraw;
 }
 
 bool RenderProxy::loadSystemProperties() {
     SETUP_TASK(loadSystemProperties);
+    args->context = mContext;
     return (bool) postAndWait(task);
 }
 
-CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
     return (void*) args->context->initialize(args->window);
 }
 
@@ -123,7 +127,7 @@
     return (bool) postAndWait(task);
 }
 
-CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) {
     args->context->updateSurface(args->window);
     return NULL;
 }
@@ -135,7 +139,7 @@
     postAndWait(task);
 }
 
-CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
     args->context->pauseSurface(args->window);
     return NULL;
 }
@@ -175,10 +179,11 @@
     post(task);
 }
 
-int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
-        int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
+int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+        float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
-    return mDrawFrameTask.drawFrame(frameTimeNanos);
+    mDrawFrameTask.setDensity(density);
+    return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
 }
 
 CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
@@ -282,6 +287,18 @@
     post(task);
 }
 
+CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
+    args->context->flushCaches(args->flushMode);
+    return NULL;
+}
+
+void RenderProxy::flushCaches(Caches::FlushMode flushMode) {
+    SETUP_TASK(flushCaches);
+    args->context = mContext;
+    args->flushMode = flushMode;
+    post(task);
+}
+
 CREATE_BRIDGE0(fence) {
     // Intentionally empty
     return NULL;
@@ -292,6 +309,29 @@
     postAndWait(task);
 }
 
+CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
+    args->context->notifyFramePending();
+    return NULL;
+}
+
+void RenderProxy::notifyFramePending() {
+    SETUP_TASK(notifyFramePending);
+    args->context = mContext;
+    mRenderThread.queueAtFront(task);
+}
+
+CREATE_BRIDGE2(dumpProfileInfo, CanvasContext* context, int fd) {
+    args->context->profiler().dumpData(args->fd);
+    return NULL;
+}
+
+void RenderProxy::dumpProfileInfo(int fd) {
+    SETUP_TASK(dumpProfileInfo);
+    args->context = mContext;
+    args->fd = fd;
+    postAndWait(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 4d3499e..c8d42ec 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,6 +29,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
+#include "../Caches.h"
 #include "DrawFrameTask.h"
 
 namespace android {
@@ -68,8 +69,8 @@
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
     ANDROID_API void setOpaque(bool opaque);
-    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
-            int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
+            float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
 
     ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
@@ -81,7 +82,12 @@
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
     ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
 
+    ANDROID_API void flushCaches(Caches::FlushMode flushMode);
+
     ANDROID_API void fence();
+    ANDROID_API void notifyFramePending();
+
+    ANDROID_API void dumpProfileInfo(int fd);
 
 private:
     RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 35a3eab..4a4e254 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -37,7 +37,7 @@
 static const size_t EVENT_BUFFER_SIZE = 100;
 
 // Slight delay to give the UI time to push us a new frame before we replay
-static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 4;
 
 TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
 
@@ -91,6 +91,15 @@
     }
 }
 
+void TaskQueue::queueAtFront(RenderTask* task) {
+    if (mTail) {
+        task->mNext = mHead;
+        mHead = task;
+    } else {
+        mTail = mHead = task;
+    }
+}
+
 void TaskQueue::remove(RenderTask* task) {
     // TaskQueue is strict here to enforce that users are keeping track of
     // their RenderTasks due to how their memory is managed
@@ -188,20 +197,22 @@
     return latest;
 }
 
-void RenderThread::drainDisplayEventQueue() {
+void RenderThread::drainDisplayEventQueue(bool skipCallbacks) {
+    ATRACE_CALL();
     nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
     if (vsyncEvent > 0) {
         mVsyncRequested = false;
         mTimeLord.vsyncReceived(vsyncEvent);
-        if (!mFrameCallbackTaskPending) {
+        if (!skipCallbacks && !mFrameCallbackTaskPending) {
+            ATRACE_NAME("queue mFrameCallbackTask");
             mFrameCallbackTaskPending = true;
-            //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
-            queue(mFrameCallbackTask);
+            queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
         }
     }
 }
 
 void RenderThread::dispatchFrameCallbacks() {
+    ATRACE_CALL();
     mFrameCallbackTaskPending = false;
 
     std::set<IFrameCallback*> callbacks;
@@ -212,6 +223,15 @@
     }
 }
 
+void RenderThread::requestVsync() {
+    if (!mVsyncRequested) {
+        mVsyncRequested = true;
+        status_t status = mDisplayEventReceiver->requestNextVsync();
+        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+                "requestNextVsync failed with status: %d", status);
+    }
+}
+
 bool RenderThread::threadLoop() {
     initializeDisplayEventReceiver();
 
@@ -236,6 +256,14 @@
                 timeoutMillis = 0;
             }
         }
+
+        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
+            drainDisplayEventQueue(true);
+            mFrameCallbacks.insert(
+                    mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
+            mPendingRegistrationFrameCallbacks.clear();
+            requestVsync();
+        }
     }
 
     return false;
@@ -250,6 +278,12 @@
     }
 }
 
+void RenderThread::queueAtFront(RenderTask* task) {
+    AutoMutex _lock(mLock);
+    mQueue.queueAtFront(task);
+    mLooper->wake();
+}
+
 void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     task->mRunAt = now + milliseconds_to_nanoseconds(delayMs);
@@ -262,17 +296,18 @@
 }
 
 void RenderThread::postFrameCallback(IFrameCallback* callback) {
-    mFrameCallbacks.insert(callback);
-    if (!mVsyncRequested) {
-        mVsyncRequested = true;
-        status_t status = mDisplayEventReceiver->requestNextVsync();
-        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
-                "requestNextVsync failed with status: %d", status);
-    }
+    mPendingRegistrationFrameCallbacks.insert(callback);
 }
 
 void RenderThread::removeFrameCallback(IFrameCallback* callback) {
     mFrameCallbacks.erase(callback);
+    mPendingRegistrationFrameCallbacks.erase(callback);
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
+    if (mFrameCallbacks.erase(callback)) {
+        mPendingRegistrationFrameCallbacks.insert(callback);
+    }
 }
 
 RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
@@ -281,11 +316,13 @@
     if (!next) {
         mNextWakeup = LLONG_MAX;
     } else {
+        mNextWakeup = next->mRunAt;
         // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
         if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
             next = mQueue.next();
+        } else {
+            next = 0;
         }
-        mNextWakeup = next->mRunAt;
     }
     if (nextWakeup) {
         *nextWakeup = mNextWakeup;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 215d294..4412584 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -44,6 +44,7 @@
 
     RenderTask* next();
     void queue(RenderTask* task);
+    void queueAtFront(RenderTask* task);
     RenderTask* peek();
     void remove(RenderTask* task);
 
@@ -66,12 +67,16 @@
     // RenderThread takes complete ownership of tasks that are queued
     // and will delete them after they are run
     ANDROID_API void queue(RenderTask* task);
+    ANDROID_API void queueAtFront(RenderTask* task);
     void queueDelayed(RenderTask* task, int delayMs);
     void remove(RenderTask* task);
 
     // Mimics android.view.Choreographer
     void postFrameCallback(IFrameCallback* callback);
     void removeFrameCallback(IFrameCallback* callback);
+    // If the callback is currently registered, it will be pushed back until
+    // the next vsync. If it is not currently registered this does nothing.
+    void pushBackFrameCallback(IFrameCallback* callback);
 
     TimeLord& timeLord() { return mTimeLord; }
 
@@ -87,8 +92,9 @@
 
     void initializeDisplayEventReceiver();
     static int displayEventReceiverCallback(int fd, int events, void* data);
-    void drainDisplayEventQueue();
+    void drainDisplayEventQueue(bool skipCallbacks = false);
     void dispatchFrameCallbacks();
+    void requestVsync();
 
     // Returns the next task to be run. If this returns NULL nextWakeup is set
     // to the time to requery for the nextTask to run. mNextWakeup is also
@@ -104,6 +110,11 @@
     DisplayEventReceiver* mDisplayEventReceiver;
     bool mVsyncRequested;
     std::set<IFrameCallback*> mFrameCallbacks;
+    // We defer the actual registration of these callbacks until
+    // both mQueue *and* mDisplayEventReceiver have been drained off all
+    // immediate events. This makes sure that we catch the next vsync, not
+    // the previous one
+    std::set<IFrameCallback*> mPendingRegistrationFrameCallbacks;
     bool mFrameCallbackTaskPending;
     DispatchFrameCallbacks* mFrameCallbackTask;
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bb23a36..5a3aaab 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -26,6 +26,7 @@
 import java.util.Set;
 
 /**
+ * @hide
  * A class to encapsulate a collection of attributes describing information about an audio
  * player or recorder.
  */
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
new file mode 100644
index 0000000..c088906
--- /dev/null
+++ b/media/java/android/media/AudioDevicePort.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioDevicePort is a specialized type of AudioPort
+ * describing an input (e.g microphone) or output device (e.g speaker)
+ * of the system.
+ * An AudioDevicePort is an AudioPort controlled by the audio HAL, almost always a physical
+ * device at the boundary of the audio system.
+ * In addition to base audio port attributes, the device descriptor contains:
+ * - the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+ * - the device address (e.g MAC adddress for AD2P sink).
+ * @see AudioPort
+ * @hide
+ */
+
+public class AudioDevicePort extends AudioPort {
+
+    private final int mType;
+    private final String mAddress;
+
+    AudioDevicePort(AudioHandle handle, int[] samplingRates, int[] channelMasks,
+            int[] formats, AudioGain[] gains, int type, String address) {
+        super(handle,
+             (AudioManager.isInputDevice(type) == true)  ?
+                        AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
+             samplingRates, channelMasks, formats, gains);
+        mType = type;
+        mAddress = address;
+    }
+
+    /**
+     * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER)
+     */
+    public int type() {
+        return mType;
+    }
+
+    /**
+     * Get the device address. Address format varies with the device type.
+     * - USB devices ({@link AudioManager#DEVICE_OUT_USB_DEVICE},
+     * {@link AudioManager#DEVICE_IN_USB_DEVICE}) use an address composed of the ALSA card number
+     * and device number: "card=2;device=1"
+     * - Bluetooth devices ({@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO},
+     * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP})
+     * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by
+     * {@link BluetoothDevice#getAddress()}.
+     * - Deivces that do not have an address will indicate an empty string "".
+     */
+    public String address() {
+        return mAddress;
+    }
+
+    /**
+     * Build a specific configuration of this audio device port for use by methods
+     * like AudioManager.connectAudioPatch().
+     */
+    public AudioDevicePortConfig buildConfig(int samplingRate, int channelMask, int format,
+                                          AudioGainConfig gain) {
+        return new AudioDevicePortConfig(this, samplingRate, channelMask, format, gain);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioDevicePort)) {
+            return false;
+        }
+        return super.equals(o);
+    }
+}
diff --git a/media/java/android/media/AudioDevicePortConfig.java b/media/java/android/media/AudioDevicePortConfig.java
new file mode 100644
index 0000000..a381e10
--- /dev/null
+++ b/media/java/android/media/AudioDevicePortConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * An AudioDevicePortConfig describes a possible configuration of an output or input device
+ * (speaker, headphone, microphone ...).
+ * It is used to specify a sink or source when creating a connection with
+ * AudioManager.connectAudioPatch().
+ * An AudioDevicePortConfig is obtained from AudioDevicePort.buildConfig().
+ * @hide
+ */
+
+public class AudioDevicePortConfig extends AudioPortConfig {
+    AudioDevicePortConfig(AudioDevicePort devicePort, int samplingRate, int channelMask,
+            int format, AudioGainConfig gain) {
+        super((AudioPort)devicePort, samplingRate, channelMask, format, gain);
+    }
+
+    /**
+     * Returns the audio device port this AudioDevicePortConfig is issued from.
+     */
+    public AudioDevicePort port() {
+        return (AudioDevicePort)mPort;
+    }
+}
+
diff --git a/media/java/android/media/AudioGain.java b/media/java/android/media/AudioGain.java
new file mode 100644
index 0000000..57709d5
--- /dev/null
+++ b/media/java/android/media/AudioGain.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.media;
+
+/**
+ * The AudioGain describes a gain controller. Gain controllers are exposed by
+ * audio ports when the gain is configurable at this port's input or output.
+ * Gain values are expressed in millibels.
+ * A gain controller has the following attributes:
+ * - mode: defines modes of operation or features
+ *    MODE_JOINT: all channel gains are controlled simultaneously
+ *    MODE_CHANNELS: each channel gain is controlled individually
+ *    MODE_RAMP: ramps can be applied when gain changes
+ * - channel mask: indicates for which channels the gain can be controlled
+ * - min value: minimum gain value in millibel
+ * - max value: maximum gain value in millibel
+ * - default value: gain value after reset in millibel
+ * - step value: granularity of gain control in millibel
+ * - min ramp duration: minimum ramp duration in milliseconds
+ * - max ramp duration: maximum ramp duration in milliseconds
+ *
+ * This object is always created by the framework and read only by applications.
+ * Applications get a list of AudioGainDescriptors from AudioPortDescriptor.gains() and can build a
+ * valid gain configuration from AudioGain.buildConfig()
+ * @hide
+ */
+public class AudioGain {
+
+    /**
+     * Bit of AudioGain.mode() field indicating that
+     * all channel gains are controlled simultaneously
+     */
+    public static final int MODE_JOINT = 1;
+    /**
+     * Bit of AudioGain.mode() field indicating that
+     * each channel gain is controlled individually
+     */
+    public static final int MODE_CHANNELS = 2;
+    /**
+     * Bit of AudioGain.mode() field indicating that
+     * ramps can be applied when gain changes. The type of ramp (linear, log etc...) is
+     * implementation specific.
+     */
+    public static final int MODE_RAMP = 4;
+
+    private final int mIndex;
+    private final int mMode;
+    private final int mChannelMask;
+    private final int mMinValue;
+    private final int mMaxValue;
+    private final int mDefaultValue;
+    private final int mStepValue;
+    private final int mRampDurationMinMs;
+    private final int mRampDurationMaxMs;
+
+    // The channel mask passed to the constructor is as specified in AudioFormat
+    // (e.g. AudioFormat.CHANNEL_OUT_STEREO)
+    AudioGain(int index, int mode, int channelMask,
+                        int minValue, int maxValue, int defaultValue, int stepValue,
+                        int rampDurationMinMs, int rampDurationMaxMs) {
+        mIndex = index;
+        mMode = mode;
+        mChannelMask = channelMask;
+        mMinValue = minValue;
+        mMaxValue = maxValue;
+        mDefaultValue = defaultValue;
+        mStepValue = stepValue;
+        mRampDurationMinMs = rampDurationMinMs;
+        mRampDurationMaxMs = rampDurationMaxMs;
+    }
+
+    /**
+     * Bit field indicating supported modes of operation
+     */
+    public int mode() {
+        return mMode;
+    }
+
+    /**
+     * Indicates for which channels the gain can be controlled
+     * (e.g. AudioFormat.CHANNEL_OUT_STEREO)
+     */
+    public int channelMask() {
+        return mChannelMask;
+    }
+
+    /**
+     * Minimum gain value in millibel
+     */
+    public int minValue() {
+        return mMinValue;
+    }
+
+    /**
+     * Maximum gain value in millibel
+     */
+    public int maxValue() {
+        return mMaxValue;
+    }
+
+    /**
+     * Default gain value in millibel
+     */
+    public int defaultValue() {
+        return mDefaultValue;
+    }
+
+    /**
+     * Granularity of gain control in millibel
+     */
+    public int stepValue() {
+        return mStepValue;
+    }
+
+    /**
+     * Minimum ramp duration in milliseconds
+     * 0 if MODE_RAMP not set
+     */
+    public int rampDurationMinMs() {
+        return mRampDurationMinMs;
+    }
+
+    /**
+     * Maximum ramp duration in milliseconds
+     * 0 if MODE_RAMP not set
+     */
+    public int rampDurationMaxMs() {
+        return mRampDurationMaxMs;
+    }
+
+    /**
+     * Build a valid gain configuration for this gain controller for use by
+     * AudioPortDescriptor.setGain()
+     * @param mode: desired mode of operation
+     * @param channelMask: channels of which the gain should be modified.
+     * @param values: gain values for each channels.
+     * @param rampDurationMs: ramp duration if mode MODE_RAMP is set.
+     * ignored if MODE_JOINT.
+     */
+    public AudioGainConfig buildConfig(int mode, int channelMask,
+                                       int[] values, int rampDurationMs) {
+        //TODO: check params here
+        return new AudioGainConfig(mIndex, this, mode, channelMask, values, rampDurationMs);
+    }
+}
diff --git a/media/java/android/media/AudioGainConfig.java b/media/java/android/media/AudioGainConfig.java
new file mode 100644
index 0000000..ea61679
--- /dev/null
+++ b/media/java/android/media/AudioGainConfig.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioGainConfig is used by APIs setting or getting values on a given gain
+ * controller. It contains a valid configuration (value, channels...) for a gain controller
+ * exposed by an audio port.
+ * @see AudioGain
+ * @see AudioPort
+ * @hide
+ */
+public class AudioGainConfig {
+    AudioGain mGain;
+    private final int mIndex;
+    private final int mMode;
+    private final int mChannelMask;
+    private final int mValues[];
+    private final int mRampDurationMs;
+
+    AudioGainConfig(int index, AudioGain gain, int mode, int channelMask,
+            int[] values, int rampDurationMs) {
+        mIndex = index;
+        mGain = gain;
+        mMode = mode;
+        mChannelMask = channelMask;
+        mValues = values;
+        mRampDurationMs = rampDurationMs;
+    }
+
+    /**
+     * get the index of the parent gain.
+     * frameworks use only.
+     */
+    int index() {
+        return mIndex;
+    }
+
+    /**
+     * Bit field indicating requested modes of operation. See {@link AudioGain#MODE_JOINT},
+     * {@link AudioGain#MODE_CHANNELS}, {@link AudioGain#MODE_RAMP}
+     */
+    public int mode() {
+        return mMode;
+    }
+
+    /**
+     * Indicates for which channels the gain is set.
+     * See {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_MONO} ...
+     */
+    public int channelMask() {
+        return mChannelMask;
+    }
+
+    /**
+     * Gain values for each channel in the order of bits set in
+     * channelMask() from LSB to MSB
+     */
+    public int[] values() {
+        return mValues;
+    }
+
+    /**
+     * Ramp duration in milliseconds. N/A if mode() does not
+     * specify MODE_RAMP.
+     */
+    public int rampDurationMs() {
+        return mRampDurationMs;
+    }
+}
diff --git a/media/java/android/media/AudioHandle.java b/media/java/android/media/AudioHandle.java
new file mode 100644
index 0000000..b58e7a3
--- /dev/null
+++ b/media/java/android/media/AudioHandle.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioHandle is used by the audio framework implementation to
+ * uniquely identify a particular component of the routing topology
+ * (AudioPort or AudioPatch)
+ * It is not visible or used at the API.
+ */
+class AudioHandle {
+    private final int mId;
+
+    AudioHandle(int id) {
+        mId = id;
+    }
+
+    int id() {
+        return mId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioHandle)) {
+            return false;
+        }
+        AudioHandle ah = (AudioHandle)o;
+        return mId == ah.id();
+    }
+
+    @Override
+    public int hashCode() {
+        return mId;
+    }
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9803161..f4affa0 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -39,6 +39,7 @@
 import android.view.KeyEvent;
 
 import java.util.HashMap;
+import java.util.ArrayList;
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -2966,4 +2967,151 @@
             Log.w(TAG, "Error disabling safe media volume", e);
         }
     }
+
+    /**
+     * Return codes for listAudioPorts(), createAudioPatch() ...
+     */
+
+    /** @hide
+     */
+    public static final int SUCCESS = AudioSystem.SUCCESS;
+    /** @hide
+     */
+    public static final int ERROR = AudioSystem.ERROR;
+    /** @hide
+     */
+    public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
+    /** @hide
+     */
+    public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
+    /** @hide
+     */
+    public static final int ERROR_PERMISSION_DENIED = AudioSystem.PERMISSION_DENIED;
+    /** @hide
+     */
+    public static final int ERROR_NO_INIT = AudioSystem.NO_INIT;
+    /** @hide
+     */
+    public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT;
+
+    /**
+     * Returns a list of descriptors for all audio ports managed by the audio framework.
+     * Audio ports are nodes in the audio framework or audio hardware that can be configured
+     * or connected and disconnected with createAudioPatch() or releaseAudioPatch().
+     * See AudioPort for a list of attributes of each audio port.
+     * @param ports An AudioPort ArrayList where the list will be returned.
+     * @hide
+     */
+    public int listAudioPorts(ArrayList<AudioPort> ports) {
+        return ERROR_INVALID_OPERATION;
+    }
+
+    /**
+     * Specialized version of listAudioPorts() listing only audio devices (AudioDevicePort)
+     * @see listAudioPorts(ArrayList<AudioPort>)
+     * @hide
+     */
+    public int listAudioDevicePorts(ArrayList<AudioPort> devices) {
+        return ERROR_INVALID_OPERATION;
+    }
+
+    /**
+     * Create a connection between two or more devices. The framework will reject the request if
+     * device types are not compatible or the implementation does not support the requested
+     * configuration.
+     * NOTE: current implementation is limited to one source and one sink per patch.
+     * @param patch AudioPatch array where the newly created patch will be returned.
+     *              As input, if patch[0] is not null, the specified patch will be replaced by the
+     *              new patch created. This avoids calling releaseAudioPatch() when modifying a
+     *              patch and allows the implementation to optimize transitions.
+     * @param sources List of source audio ports. All must be AudioPort.ROLE_SOURCE.
+     * @param sinks   List of sink audio ports. All must be AudioPort.ROLE_SINK.
+     *
+     * @return - {@link #SUCCESS} if connection is successful.
+     *         - {@link #ERROR_BAD_VALUE} if incompatible device types are passed.
+     *         - {@link #ERROR_INVALID_OPERATION} if the requested connection is not supported.
+     *         - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to create
+     *         a patch.
+     *         - {@link #ERROR_DEAD_OBJECT} if the server process is dead
+     *         - {@link #ERROR} if patch cannot be connected for any other reason.
+     *
+     *         patch[0] contains the newly created patch
+     * @hide
+     */
+    public int createAudioPatch(AudioPatch[] patch,
+                                 AudioPortConfig[] sources,
+                                 AudioPortConfig[] sinks) {
+        return ERROR_INVALID_OPERATION;
+    }
+
+    /**
+     * Releases an existing audio patch connection.
+     * @param patch The audio patch to disconnect.
+     * @return - {@link #SUCCESS} if disconnection is successful.
+     *         - {@link #ERROR_BAD_VALUE} if the specified patch does not exist.
+     *         - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to release
+     *         a patch.
+     *         - {@link #ERROR_DEAD_OBJECT} if the server process is dead
+     *         - {@link #ERROR} if patch cannot be released for any other reason.
+     * @hide
+     */
+    public int releaseAudioPatch(AudioPatch patch) {
+        return  ERROR_INVALID_OPERATION;
+    }
+
+    /**
+     * List all existing connections between audio ports.
+     * @param patches An AudioPatch array where the list will be returned.
+     * @hide
+     */
+    public int listAudioPatches(ArrayList<AudioPatch> patches) {
+        return ERROR_INVALID_OPERATION;
+    }
+
+    /**
+     * Set the gain on the specified AudioPort. The AudioGainConfig config is build by
+     * AudioGain.buildConfig()
+     * @hide
+     */
+    public int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
+        return ERROR_INVALID_OPERATION;
+    }
+
+    /**
+     * Listener registered by client to be notified upon new audio port connections,
+     * disconnections or attributes update.
+     * @hide
+     */
+    public interface OnAudioPortUpdateListener {
+        /**
+         * Callback method called upon audio port list update.
+         * @param portList the updated list of audio ports
+         */
+        public void OnAudioPortListUpdate(AudioPort[] portList);
+
+        /**
+         * Callback method called upon audio patch list update.
+         * @param patchList the updated list of audio patches
+         */
+        public void OnAudioPatchListUpdate(AudioPatch[] patchList);
+
+        /**
+         * Callback method called when the mediaserver dies
+         */
+        public void OnServiceDied();
+    }
+
+    /**
+     * Register an audio port update listener.
+     * @hide
+     */
+    public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+    }
+
+    /**
+     * Unregister an audio port update listener.
+     * @hide
+     */
+    public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+    }
 }
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
new file mode 100644
index 0000000..1500a43
--- /dev/null
+++ b/media/java/android/media/AudioMixPort.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The AudioMixPort is a specialized type of AudioPort
+ * describing an audio mix or stream at an input or output stream of the audio
+ * framework.
+ * @see AudioPort
+ * @hide
+ */
+
+public class AudioMixPort extends AudioPort {
+
+    AudioMixPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+            int[] formats, AudioGain[] gains) {
+        super(handle, role, samplingRates, channelMasks, formats, gains);
+    }
+
+    /**
+     * Build a specific configuration of this audio mix port for use by methods
+     * like AudioManager.connectAudioPatch().
+     */
+    public AudioMixPortConfig buildConfig(int samplingRate, int channelMask, int format,
+                                       AudioGainConfig gain) {
+        return new AudioMixPortConfig(this, samplingRate, channelMask, format, gain);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioMixPort)) {
+            return false;
+        }
+        return super.equals(o);
+    }
+
+}
diff --git a/media/java/android/media/AudioMixPortConfig.java b/media/java/android/media/AudioMixPortConfig.java
new file mode 100644
index 0000000..8eb9ef46
--- /dev/null
+++ b/media/java/android/media/AudioMixPortConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * An AudioMixPortConfig describes a possible configuration of an output or input mixer.
+ * It is used to specify a sink or source when creating a connection with
+ * AudioManager.connectAudioPatch().
+ * An AudioMixPortConfig is obtained from AudioMixPort.buildConfig().
+ * @hide
+ */
+
+public class AudioMixPortConfig extends AudioPortConfig {
+
+    AudioMixPortConfig(AudioMixPort mixPort, int samplingRate, int channelMask, int format,
+                AudioGainConfig gain) {
+        super((AudioPort)mixPort, samplingRate, channelMask, format, gain);
+    }
+
+    /**
+     * Returns the audio mix port this AudioMixPortConfig is issued from.
+     */
+    public AudioMixPort port() {
+        return (AudioMixPort)mPort;
+    }
+}
+
diff --git a/media/java/android/media/AudioPatch.java b/media/java/android/media/AudioPatch.java
new file mode 100644
index 0000000..72291f6
--- /dev/null
+++ b/media/java/android/media/AudioPatch.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+
+/**
+ * An AudioPatch describes a connection between audio sources and audio sinks.
+ * An audio source can be an output mix (playback AudioBus) or an input device (microphone).
+ * An audio sink can be an output device (speaker) or an input mix (capture AudioBus).
+ * An AudioPatch is created by AudioManager.connectAudioPatch() and released by
+ * AudioManager.disconnectAudioPatch()
+ * It contains the list of source and sink AudioPortConfig showing audio port configurations
+ * being connected.
+ * @hide
+ */
+public class AudioPatch {
+
+    private final AudioHandle mHandle;
+    private final AudioPortConfig[] mSources;
+    private final AudioPortConfig[] mSinks;
+
+    AudioPatch(AudioHandle patchHandle, AudioPortConfig[] sources, AudioPortConfig[] sinks) {
+        mHandle = patchHandle;
+        mSources = sources;
+        mSinks = sinks;
+    }
+
+    /**
+     * Retrieve the list of sources of this audio patch.
+     */
+    public AudioPortConfig[] sources() {
+        return mSources;
+    }
+
+    /**
+     * Retreive the list of sinks of this audio patch.
+     */
+    public AudioPortConfig[] sinks() {
+        return mSinks;
+    }
+}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
new file mode 100644
index 0000000..9aeddef
--- /dev/null
+++ b/media/java/android/media/AudioPort.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+
+/**
+ * An audio port is a node of the audio framework or hardware that can be connected to or
+ * disconnect from another audio node to create a specific audio routing configuration.
+ * Examples of audio ports are an output device (speaker) or an output mix (see AudioMixPort).
+ * All attributes that are relevant for applications to make routing selection are decribed
+ * in an AudioPort,  in particular:
+ * - possible channel mask configurations.
+ * - audio format (PCM 16bit, PCM 24bit...)
+ * - gain: a port can be associated with one or more gain controllers (see AudioGain).
+ *
+ * This object is always created by the framework and read only by applications.
+ * A list of all audio port descriptors currently available for applications to control
+ * is obtained by AudioManager.listAudioPorts().
+ * An application can obtain an AudioPortConfig for a valid configuration of this port
+ * by calling AudioPort.buildConfig() and use this configuration
+ * to create a connection between audio sinks and sources with AudioManager.connectAudioPatch()
+ *
+ * @hide
+ */
+public class AudioPort {
+
+    /**
+     * For use by the audio framework.
+     */
+    public static final int ROLE_NONE = 0;
+    /**
+     * The audio port is a source (produces audio)
+     */
+    public static final int ROLE_SOURCE = 1;
+    /**
+     * The audio port is a sink (consumes audio)
+     */
+    public static final int ROLE_SINK = 2;
+
+    /**
+     * audio port type for use by audio framework implementation
+     */
+    public static final int TYPE_NONE = 0;
+    /**
+     */
+    public static final int TYPE_DEVICE = 1;
+    /**
+     */
+    public static final int TYPE_SUBMIX = 2;
+    /**
+     */
+    public static final int TYPE_SESSION = 3;
+
+
+    AudioHandle mHandle;
+    private final int mRole;
+    private final int[] mSamplingRates;
+    private final int[] mChannelMasks;
+    private final int[] mFormats;
+    private final AudioGain[] mGains;
+    private AudioPortConfig mActiveConfig;
+
+    AudioPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks,
+            int[] formats, AudioGain[] gains) {
+        mHandle = handle;
+        mRole = role;
+        mSamplingRates = samplingRates;
+        mChannelMasks = channelMasks;
+        mFormats = formats;
+        mGains = gains;
+    }
+
+    AudioHandle handle() {
+        return mHandle;
+    }
+
+    /**
+     * Get the audio port role
+     */
+    public int role() {
+        return mRole;
+    }
+
+    /**
+     * Get the list of supported sampling rates
+     * Empty array if sampling rate is not relevant for this audio port
+     */
+    public int[] samplingRates() {
+        return mSamplingRates;
+    }
+
+    /**
+     * Get the list of supported channel mask configurations
+     * (e.g AudioFormat.CHANNEL_OUT_STEREO)
+     * Empty array if channel mask is not relevant for this audio port
+     */
+    public int[] channelMasks() {
+        return mChannelMasks;
+    }
+
+    /**
+     * Get the list of supported audio format configurations
+     * (e.g AudioFormat.ENCODING_PCM_16BIT)
+     * Empty array if format is not relevant for this audio port
+     */
+    public int[] formats() {
+        return mFormats;
+    }
+
+    /**
+     * Get the list of gain descriptors
+     * Empty array if this port does not have gain control
+     */
+    public AudioGain[] gains() {
+        return mGains;
+    }
+
+    /**
+     * Get the gain descriptor at a given index
+     */
+    AudioGain gain(int index) {
+        return mGains[index];
+    }
+
+    /**
+     * Build a specific configuration of this audio port for use by methods
+     * like AudioManager.connectAudioPatch().
+     * @param channelMask The desired channel mask. AudioFormat.CHANNEL_OUT_DEFAULT if no change
+     * from active configuration requested.
+     * @param format The desired audio format. AudioFormat.ENCODING_DEFAULT if no change
+     * from active configuration requested.
+     * @param gain The desired gain. null if no gain changed requested.
+     */
+    public AudioPortConfig buildConfig(int samplingRate, int channelMask, int format,
+                                        AudioGainConfig gain) {
+        return new AudioPortConfig(this, samplingRate, channelMask, format, gain);
+    }
+
+    /**
+     * Get currently active configuration of this audio port.
+     */
+    public AudioPortConfig activeConfig() {
+        return mActiveConfig;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof AudioPort)) {
+            return false;
+        }
+        AudioPort ap = (AudioPort)o;
+        return mHandle.equals(ap.handle());
+    }
+
+    @Override
+    public int hashCode() {
+        return mHandle.hashCode();
+    }
+}
+
diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java
new file mode 100644
index 0000000..5dc768d
--- /dev/null
+++ b/media/java/android/media/AudioPortConfig.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * An AudioPortConfig contains a possible configuration of an audio port chosen
+ * among all possible attributes described by an AudioPort.
+ * An AudioPortConfig is created by AudioPort.buildConfiguration().
+ * AudioPorts are used to specify the sources and sinks of a patch created
+ * with AudioManager.connectAudioPatch().
+ * Several specialized versions of AudioPortConfig exist to handle different categories of
+ * audio ports and their specific attributes:
+ * - AudioDevicePortConfig for input (e.g micropohone) and output devices (e.g speaker)
+ * - AudioMixPortConfig for input or output streams of the audio framework.
+ * @hide
+ */
+
+public class AudioPortConfig {
+    final AudioPort mPort;
+    private final int mSamplingRate;
+    private final int mChannelMask;
+    private final int mFormat;
+    private final AudioGainConfig mGain;
+
+    // mConfigMask indicates which fields in this configuration should be
+    // taken into account. Used with AudioSystem.setAudioPortConfig()
+    // framework use only.
+    static final int SAMPLE_RATE  = 0x1;
+    static final int CHANNEL_MASK = 0x2;
+    static final int FORMAT       = 0x4;
+    static final int GAIN         = 0x8;
+    int mConfigMask;
+
+    AudioPortConfig(AudioPort port, int samplingRate, int channelMask, int format,
+            AudioGainConfig gain) {
+        mPort = port;
+        mSamplingRate = samplingRate;
+        mChannelMask = channelMask;
+        mFormat = format;
+        mGain = gain;
+        mConfigMask = 0;
+    }
+
+    /**
+     * Returns the audio port this AudioPortConfig is issued from.
+     */
+    public AudioPort port() {
+        return mPort;
+    }
+
+    /**
+     * Sampling rate configured for this AudioPortConfig.
+     */
+    public int samplingRate() {
+        return mSamplingRate;
+    }
+
+    /**
+     * Channel mask configuration (e.g AudioFormat.CHANNEL_CONFIGURATION_STEREO).
+     */
+    public int channelMask() {
+        return mChannelMask;
+    }
+
+    /**
+     * Audio format configuration (e.g AudioFormat.ENCODING_PCM_16BIT).
+     */
+    public int format() {
+        return mFormat;
+    }
+
+    /**
+     * The gain configuration if this port supports gain control, null otherwise
+     * @see AudioGainConfig.
+     */
+    public AudioGainConfig gain() {
+        return mGain;
+    }
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index c736fc7..5b620fd 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,6 +112,9 @@
     private static final boolean USE_SESSIONS = true;
     private static final boolean DEBUG_SESSIONS = true;
 
+    /** Allow volume changes to set ringer mode to silent? */
+    private static final boolean VOLUME_SETS_RINGER_MODE_SILENT = false;
+
     /** How long to delay before persisting a change in volume/ringer mode. */
     private static final int PERSIST_DELAY = 500;
 
@@ -1019,7 +1022,8 @@
             int newRingerMode;
             if (index == 0) {
                 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
-                                              : AudioManager.RINGER_MODE_SILENT;
+                        : VOLUME_SETS_RINGER_MODE_SILENT ? AudioManager.RINGER_MODE_SILENT
+                        : AudioManager.RINGER_MODE_NORMAL;
             } else {
                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
             }
@@ -2552,7 +2556,9 @@
                     }
                 } else {
                     // (oldIndex < step) is equivalent to (old UI index == 0)
-                    if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+                    if ((oldIndex < step)
+                            && VOLUME_SETS_RINGER_MODE_SILENT
+                            && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
                         ringerMode = RINGER_MODE_SILENT;
                     }
                 }
@@ -2565,7 +2571,8 @@
                 break;
             }
             if ((direction == AudioManager.ADJUST_LOWER)) {
-                if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
+                if (VOLUME_SETS_RINGER_MODE_SILENT
+                        && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
                     ringerMode = RINGER_MODE_SILENT;
                 }
             } else if (direction == AudioManager.ADJUST_RAISE) {
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index d658654..90fe695 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,7 +2,6 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    android_media_DngCreator.cpp \
     android_media_ImageReader.cpp \
     android_media_MediaCrypto.cpp \
     android_media_MediaCodec.cpp \
@@ -42,7 +41,6 @@
     libjhead \
     libexif \
     libstagefright_amrnb_common \
-    libimg_utils \
 
 LOCAL_REQUIRED_MODULES := \
     libjhead_jni
@@ -55,7 +53,6 @@
     external/tremor/Tremor \
     frameworks/base/core/jni \
     frameworks/av/media/libmedia \
-    frameworks/av/media/img_utils/include \
     frameworks/av/media/libstagefright \
     frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
     frameworks/av/media/libstagefright/codecs/amrnb/common \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 9d03cc38..6f42057 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -884,7 +884,6 @@
                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
 }
 
-extern int register_android_media_DngCreator(JNIEnv *env);
 extern int register_android_media_ImageReader(JNIEnv *env);
 extern int register_android_media_Crypto(JNIEnv *env);
 extern int register_android_media_Drm(JNIEnv *env);
@@ -914,11 +913,6 @@
     }
     assert(env != NULL);
 
-    if (register_android_media_DngCreator(env) < 0) {
-        ALOGE("ERROR: ImageReader native registration failed");
-        goto bail;
-    }
-
     if (register_android_media_ImageReader(env) < 0) {
         ALOGE("ERROR: ImageReader native registration failed");
         goto bail;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index a77b647..afc2931 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -17,6 +17,7 @@
 package com.android.mediaframeworktest.unit;
 
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
 import android.util.Range;
 import android.util.Rational;
 import android.util.SizeF;
@@ -26,6 +27,8 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Size;
 import android.hardware.camera2.impl.CameraMetadataNative;
@@ -38,6 +41,7 @@
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.TonemapCurve;
 import android.hardware.camera2.utils.TypeReference;
 
 import static android.hardware.camera2.impl.CameraMetadataNative.*;
@@ -46,6 +50,8 @@
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * <pre>
@@ -56,6 +62,10 @@
  */
 public class CameraMetadataTest extends junit.framework.TestCase {
 
+    private static final boolean VERBOSE = false;
+    private static final String TAG = "CameraMetadataTest";
+
+
     CameraMetadataNative mMetadata;
 
     // Sections
@@ -940,7 +950,7 @@
         };
         int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
 
-        Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS;
+        Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey();
 
         validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
                 expectedIntValues, availableFormatTag);
@@ -1007,6 +1017,29 @@
             assertNull(resultSimpleFaces[i].getMouthPosition());
         }
 
+        /**
+         * Read/Write TonemapCurve
+         */
+        float[] red = new float[] {0.0f, 0.0f, 1.0f, 1.0f};
+        float[] green = new float[] {0.0f, 1.0f, 1.0f, 0.0f};
+        float[] blue = new float[] {
+                0.0000f, 0.0000f, 0.0667f, 0.2920f, 0.1333f, 0.4002f, 0.2000f, 0.4812f,
+                0.2667f, 0.5484f, 0.3333f, 0.6069f, 0.4000f, 0.6594f, 0.4667f, 0.7072f,
+                0.5333f, 0.7515f, 0.6000f, 0.7928f, 0.6667f, 0.8317f, 0.7333f, 0.8685f,
+                0.8000f, 0.9035f, 0.8667f, 0.9370f, 0.9333f, 0.9691f, 1.0000f, 1.0000f};
+        TonemapCurve tcIn = new TonemapCurve(red, green, blue);
+        mMetadata.set(CaptureResult.TONEMAP_CURVE, tcIn);
+        float[] redOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_RED);
+        float[] greenOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_GREEN);
+        float[] blueOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_BLUE);
+        assertArrayEquals(red, redOut);
+        assertArrayEquals(green, greenOut);
+        assertArrayEquals(blue, blueOut);
+        TonemapCurve tcOut = mMetadata.get(CaptureResult.TONEMAP_CURVE);
+        assertEquals(tcIn, tcOut);
+        mMetadata.set(CaptureResult.TONEMAP_CURVE_GREEN, null);
+        // If any of channel has null curve, return a null TonemapCurve
+        assertNull(mMetadata.get(CaptureResult.TONEMAP_CURVE));
     }
 
     /**
@@ -1046,7 +1079,7 @@
                 0x20, 320, 240, INPUT,   // RAW16
         };
         Key<StreamConfiguration[]> configKey =
-                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS.getNativeKey();
         mMetadata.writeValues(configKey.getTag(),
                 toByteArray(rawAvailableStreamConfigs));
 
@@ -1074,7 +1107,7 @@
                 0x21, 1920, 1080, 33333338  // BLOB
         };
         Key<StreamConfigurationDuration[]> durationKey =
-                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS.getNativeKey();
         mMetadata.writeValues(durationKey.getTag(),
                 toByteArray(rawAvailableMinDurations));
 
@@ -1100,7 +1133,7 @@
                 0x21, 1920, 1080, 33333338  // BLOB
         };
         Key<StreamConfigurationDuration[]> stallDurationKey =
-                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS;
+                CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS.getNativeKey();
         mMetadata.writeValues(stallDurationKey.getTag(),
                 toByteArray(rawAvailableStallDurations));
 
@@ -1158,6 +1191,73 @@
         }
     }
 
+    private <T> void assertKeyValueEquals(T expected, CameraCharacteristics.Key<T> key) {
+        assertKeyValueEquals(expected, key.getNativeKey());
+    }
+
+    private <T> void assertKeyValueEquals(T expected, Key<T> key) {
+        T actual = mMetadata.get(key);
+
+        assertEquals("Expected value for key " + key + " to match", expected, actual);
+    }
+
+    @SmallTest
+    public void testOverrideMaxRegions() {
+        // All keys are null before doing any writes.
+        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
+        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
+        assertKeyValueEquals(null, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+
+        mMetadata.set(CameraCharacteristics.CONTROL_MAX_REGIONS,
+                new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
+
+        // All keys are the expected value after doing a write
+        assertKeyValueEquals(1, CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
+        assertKeyValueEquals(2, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
+        assertKeyValueEquals(3, CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+    }
+
+    @SmallTest
+    public void testOverrideMaxNumOutputStreams() {
+        // All keys are null before doing any writes.
+        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
+        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
+        assertKeyValueEquals(null, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
+
+        mMetadata.set(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS,
+                new int[] { /*AE*/1, /*AWB*/2, /*AF*/3 });
+
+        // All keys are the expected value after doing a write
+        assertKeyValueEquals(1, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
+        assertKeyValueEquals(2, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
+        assertKeyValueEquals(3, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
+    }
+
+    @SmallTest
+    public void testCaptureResult() {
+        mMetadata.set(CaptureRequest.CONTROL_AE_MODE,
+                CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH);
+
+        if (VERBOSE) mMetadata.dumpToLog();
+
+        CaptureResult captureResult = new CaptureResult(mMetadata, /*sequenceId*/0);
+
+        List<CaptureResult.Key<?>> allKeys = captureResult.getKeys();
+        if (VERBOSE) Log.v(TAG, "testCaptureResult: key list size " + allKeys);
+        for (CaptureResult.Key<?> key : captureResult.getKeys()) {
+            if (VERBOSE) {
+                Log.v(TAG,
+                    "testCaptureResult: key " + key + " value" + captureResult.get(key));
+            }
+        }
+
+        assertTrue(allKeys.size() >= 1); // FIXME: android.statistics.faces counts as a key
+        assertTrue(allKeys.contains(CaptureResult.CONTROL_AE_MODE));
+
+        assertEquals(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH,
+                (int)captureResult.get(CaptureResult.CONTROL_AE_MODE));
+    }
+
     private static void checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap,
             int format, int width, int height,
             boolean output) {
diff --git a/packages/Keyguard/res/layout/keyguard_bouncer.xml b/packages/Keyguard/res/layout/keyguard_bouncer.xml
index 975a139..489c5f5 100644
--- a/packages/Keyguard/res/layout/keyguard_bouncer.xml
+++ b/packages/Keyguard/res/layout/keyguard_bouncer.xml
@@ -18,12 +18,6 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <View android:id="@+id/bouncer_background"
-        android:background="#cc000000"
-        android:clickable="true"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
     <include
         style="@style/BouncerSecurityContainer"
         layout="@layout/keyguard_simple_host_view"
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index b2d0219..78b5746 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -37,6 +37,7 @@
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="@dimen/kg_status_line_font_size"
         android:textColor="?android:attr/textColorSecondary"
+        android:visibility="gone"
         androidprv:allCaps="@bool/kg_use_all_caps" />
 
     <LinearLayout
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml
index a804c8c..a8e330b 100644
--- a/packages/Keyguard/res/layout/keyguard_pin_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml
@@ -41,28 +41,16 @@
        android:layout_weight="1"
        android:layoutDirection="ltr"
        >
-       <LinearLayout
+       <RelativeLayout
+          android:id="@+id/row0"
           android:layout_width="match_parent"
           android:layout_height="0dp"
-          android:orientation="horizontal"
           android:layout_weight="1"
           >
-          <TextView android:id="@+id/pinEntry"
-               android:editable="true"
-               android:layout_width="0dip"
-               android:layout_height="match_parent"
-               android:layout_weight="1"
-               android:gravity="center"
-               android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
-               android:singleLine="true"
-               android:cursorVisible="false"
-               android:background="@null"
-               android:textAppearance="@style/TextAppearance.NumPadKey"
-               android:imeOptions="flagForceAscii|actionDone"
-               />
-           <ImageButton android:id="@+id/delete_button"
+          <ImageButton android:id="@+id/delete_button"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
+               android:layout_alignParentEnd="true"
                android:gravity="center_vertical"
                android:src="@drawable/ic_input_delete"
                android:clickable="true"
@@ -73,13 +61,30 @@
                android:background="?android:attr/selectableItemBackground"
                android:contentDescription="@string/keyboardview_keycode_delete"
                />
-       </LinearLayout>
-       <View
-           android:layout_width="wrap_content"
-           android:layout_height="1dp"
-           android:background="#55FFFFFF"
-           />
+           <TextView android:id="@+id/pinEntry"
+               android:editable="true"
+               android:layout_width="match_parent"
+               android:layout_height="match_parent"
+               android:layout_toStartOf="@+id/delete_button"
+               android:layout_alignParentStart="true"
+               android:gravity="center"
+               android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+               android:singleLine="true"
+               android:cursorVisible="false"
+               android:background="@null"
+               android:textAppearance="@style/TextAppearance.NumPadKey"
+               android:imeOptions="flagForceAscii|actionDone"
+               />
+            <View
+               android:id="@+id/divider"
+               android:layout_width="match_parent"
+               android:layout_height="1dp"
+               android:layout_alignParentBottom="true"
+               android:background="#55FFFFFF"
+               />
+       </RelativeLayout>
        <LinearLayout
+           android:id="@+id/row1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
@@ -114,6 +119,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
@@ -148,6 +154,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row3"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
@@ -182,6 +189,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row4"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index f79819f..112e371a 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -39,13 +39,12 @@
             android:id="@+id/clock_view"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal|top"
+            android:layout_gravity="center_horizontal"
             android:textColor="@color/clock_white"
             android:singleLine="true"
             style="@style/widget_big_thin"
             android:format12Hour="@string/keyguard_widget_12_hours_format"
             android:format24Hour="@string/keyguard_widget_24_hours_format"
-            android:baselineAligned="true"
             android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
 
         <include layout="@layout/keyguard_status_area" />
diff --git a/packages/Keyguard/res/values-sw600dp-land/dimens.xml b/packages/Keyguard/res/values-sw600dp-land/dimens.xml
index 5615ff7..13a6f62 100644
--- a/packages/Keyguard/res/values-sw600dp-land/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp-land/dimens.xml
@@ -26,5 +26,4 @@
 
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">88dp</dimen>
-    <dimen name="bottom_text_spacing_digital">-24dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index a5e93dc..b954792 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -65,7 +65,7 @@
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">96dp</dimen>
     <dimen name="widget_label_font_size">16sp</dimen>
-    <dimen name="bottom_text_spacing_digital">-24dp</dimen>
+    <dimen name="bottom_text_spacing_digital">-8dp</dimen>
 
     <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
          Should be 0 on devices with plenty of room (e.g. tablets) -->
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 6224aed..01d9ab3 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -155,10 +155,12 @@
     <dimen name="eca_overlap">-10dip</dimen>
 
     <!-- Default clock parameters -->
-    <dimen name="bottom_text_spacing_digital">-18dp</dimen>
+    <dimen name="bottom_text_spacing_digital">-6dp</dimen>
     <dimen name="label_font_size">14dp</dimen>
     <dimen name="widget_label_font_size">14sp</dimen>
     <dimen name="widget_big_font_size">68dp</dimen>
     <dimen name="big_font_size">120dp</dimen>
 
+    <!-- The y translation to apply at the start in appear animations. -->
+    <dimen name="appear_y_translation_start">24dp</dimen>
 </resources>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index d20b269..8cf07fa 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -97,9 +97,9 @@
     <string name="keyguard_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
 
     <!-- Time format strings for fall-back clock widget -->
-    <string name="keyguard_widget_12_hours_format" translatable="false">h&#58;mm</string>
+    <string name="keyguard_widget_12_hours_format" translatable="false">h\uee01mm</string>
     <!-- Time format strings for fall-back clock widget -->
-    <string name="keyguard_widget_24_hours_format" translatable="false">kk&#58;mm</string>
+    <string name="keyguard_widget_24_hours_format" translatable="false">kk\uee01mm</string>
 
     <!-- Accessibility description sent when user changes the current lock screen widget. [CHAR_LIMIT=none] -->
     <string name="keyguard_accessibility_widget_changed">%1$s. Widget %2$d of %3$d.</string>
diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml
index 5ab00d2..11142cf 100644
--- a/packages/Keyguard/res/values/styles.xml
+++ b/packages/Keyguard/res/values/styles.xml
@@ -59,8 +59,6 @@
 
     <!-- Built-in clock widget stuff -->
     <style name="widget_label">
-        <item name="android:textStyle">bold</item>
-        <item name="android:fontFamily">sans-serif-light</item>
         <item name="android:textSize">@dimen/widget_label_font_size</item>
     </style>
     <style name="big_thin">
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
new file mode 100644
index 0000000..ea896d5
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -0,0 +1,99 @@
+/*
+ * 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.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * A class to make nice appear transitions for views in a tabular layout.
+ */
+public class AppearAnimationUtils {
+
+    public static final long APPEAR_DURATION = 220;
+
+    private final Interpolator mLinearOutSlowIn;
+    private final float mStartTranslation;
+
+    public AppearAnimationUtils(Context ctx) {
+        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+                ctx, android.R.interpolator.linear_out_slow_in);
+        mStartTranslation =
+                ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start);
+    }
+
+    public void startAppearAnimation(View[][] views, final Runnable finishListener) {
+        long maxDelay = 0;
+        ViewPropertyAnimator maxDelayAnimator = null;
+        for (int row = 0; row < views.length; row++) {
+            View[] columns = views[row];
+            for (int col = 0; col < columns.length; col++) {
+                long delay = calculateDelay(row, col);
+                ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay);
+                if (animator != null && delay > maxDelay) {
+                    maxDelay = delay;
+                    maxDelayAnimator = animator;
+                }
+            }
+        }
+        if (maxDelayAnimator != null) {
+            maxDelayAnimator.setListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    finishListener.run();
+                }
+            });
+        } else {
+            finishListener.run();
+        }
+    }
+
+    private ViewPropertyAnimator startAppearAnimation(View view, long delay) {
+        if (view == null) return null;
+        view.setAlpha(0f);
+        view.setTranslationY(mStartTranslation);
+        view.animate()
+                .alpha(1f)
+                .translationY(0)
+                .setInterpolator(mLinearOutSlowIn)
+                .setDuration(APPEAR_DURATION)
+                .setStartDelay(delay)
+                .setListener(null);
+        if (view.hasOverlappingRendering()) {
+            view.animate().withLayer();
+        }
+        return view.animate();
+    }
+
+    private long calculateDelay(int row, int col) {
+        return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20);
+    }
+
+    public TimeInterpolator getInterpolator() {
+        return mLinearOutSlowIn;
+    }
+
+    public float getStartTranslation() {
+        return mStartTranslation;
+    }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java b/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
index 6d392fc..a592db9 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyCarrierArea.java
@@ -17,13 +17,12 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
 
-import com.android.keyguard.R;
-
 public class EmergencyCarrierArea extends LinearLayout {
 
     private CarrierText mCarrierText;
@@ -48,6 +47,7 @@
         mEmergencyButton.setOnTouchListener(new OnTouchListener(){
             @Override
             public boolean onTouch(View v, MotionEvent event) {
+                if (mCarrierText.getVisibility() != View.VISIBLE) return false;
                 switch(event.getAction()) {
                     case MotionEvent.ACTION_DOWN:
                         mCarrierText.animate().alpha(0);
@@ -59,4 +59,8 @@
                 return false;
             }});
     }
+
+    public void setCarrierTextVisible(boolean visible) {
+        mCarrierText.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
index f69fa5f..69abc7a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java
@@ -328,5 +328,10 @@
     @Override
     public void hideBouncer(int duration) {
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // TODO.
+    }
 }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
index 701d15f..c9fe93c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java
@@ -349,4 +349,8 @@
                 hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
     }
 
+    @Override
+    public void startAppearAnimation() {
+        // TODO.
+    }
 }
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/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index ede23ef..d589283 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -48,35 +48,19 @@
      */
     private static final long ANNOUNCEMENT_DELAY = 250;
 
-    static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
-    static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
-
     static final int SECURITY_MESSAGE_DURATION = 5000;
     protected static final int FADE_DURATION = 750;
 
     private static final String TAG = "KeyguardMessageArea";
 
-    // are we showing battery information?
-    boolean mShowingBatteryInfo = false;
-
     // is the bouncer up?
     boolean mShowingBouncer = false;
 
-    // last known plugged in state
-    boolean mCharging = false;
-
-    // last known battery level
-    int mBatteryLevel = 100;
-
     KeyguardUpdateMonitor mUpdateMonitor;
 
     // Timeout before we reset the message to show charging/owner info
     long mTimeout = SECURITY_MESSAGE_DURATION;
 
-    // Shadowed text values
-    protected boolean mBatteryCharged;
-    protected boolean mBatteryIsLow;
-
     private Handler mHandler;
 
     CharSequence mMessage;
@@ -146,16 +130,6 @@
     }
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-        @Override
-        public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
-            mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
-            mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
-                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
-            mBatteryLevel = status.level;
-            mBatteryCharged = status.isCharged();
-            mBatteryIsLow = status.isBatteryLow();
-            update();
-        }
         public void onScreenTurnedOff(int why) {
             setSelected(false);
         };
@@ -212,7 +186,7 @@
      */
     void update() {
         MutableInt icon = new MutableInt(0);
-        CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
+        CharSequence status = concat(getOwnerInfo(), getCurrentMessage());
         setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
         setText(status);
     }
@@ -248,25 +222,6 @@
         return info;
     }
 
-    private CharSequence getChargeInfo(MutableInt icon) {
-        CharSequence string = null;
-        if (mShowingBatteryInfo && !mShowingMessage) {
-            // Battery status
-            if (mCharging) {
-                // Charging, charged or waiting to charge.
-                string = getContext().getString(mBatteryCharged
-                        ? R.string.keyguard_charged
-                        : R.string.keyguard_plugged_in, mBatteryLevel);
-                icon.value = CHARGING_ICON;
-            } else if (mBatteryIsLow) {
-                // Battery is low
-                string = getContext().getString(R.string.keyguard_low_battery);
-                icon.value = BATTERY_LOW_ICON;
-            }
-        }
-        return string;
-    }
-
     private void hideMessage(int duration, boolean thenUpdate) {
         if (duration > 0) {
             Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index ca2d615..1f3c176 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -22,6 +22,7 @@
 import android.text.method.DigitsKeyListener;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView.OnEditorActionListener;
 
 /**
@@ -30,12 +31,21 @@
 public class KeyguardPINView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
 
+    private final AppearAnimationUtils mAppearAnimationUtils;
+    private ViewGroup mKeyguardBouncerFrame;
+    private ViewGroup mRow0;
+    private ViewGroup mRow1;
+    private ViewGroup mRow2;
+    private ViewGroup mRow3;
+    private View mDivider;
+
     public KeyguardPINView(Context context) {
         this(context, null);
     }
 
     public KeyguardPINView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mAppearAnimationUtils = new AppearAnimationUtils(context);
     }
 
     protected void resetState() {
@@ -56,6 +66,12 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
+        mRow0 = (ViewGroup) findViewById(R.id.row0);
+        mRow1 = (ViewGroup) findViewById(R.id.row1);
+        mRow2 = (ViewGroup) findViewById(R.id.row2);
+        mRow3 = (ViewGroup) findViewById(R.id.row3);
+        mDivider = findViewById(R.id.divider);
         final View ok = findViewById(R.id.key_enter);
         if (ok != null) {
             ok.setOnClickListener(new View.OnClickListener() {
@@ -114,4 +130,48 @@
     public int getWrongPasswordStringId() {
         return R.string.kg_wrong_pin;
     }
+
+    @Override
+    public void startAppearAnimation() {
+        enableClipping(false);
+        setTranslationY(mAppearAnimationUtils.getStartTranslation());
+        animate()
+                .setDuration(500)
+                .setInterpolator(mAppearAnimationUtils.getInterpolator())
+                .translationY(0);
+        mAppearAnimationUtils.startAppearAnimation(new View[][] {
+                new View[] {
+                        mRow0, null, null
+                },
+                new View[] {
+                        findViewById(R.id.key1), findViewById(R.id.key2), findViewById(R.id.key3)
+                },
+                new View[] {
+                        findViewById(R.id.key4), findViewById(R.id.key5), findViewById(R.id.key6)
+                },
+                new View[] {
+                        findViewById(R.id.key7), findViewById(R.id.key8), findViewById(R.id.key9)
+                },
+                new View[] {
+                        null, findViewById(R.id.key0), findViewById(R.id.key_enter)
+                },
+                new View[] {
+                        null, mEcaView, null
+                }},
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        enableClipping(true);
+                    }
+                });
+    }
+
+    private void enableClipping(boolean enable) {
+        mKeyguardBouncerFrame.setClipToPadding(enable);
+        mKeyguardBouncerFrame.setClipChildren(enable);
+        mRow1.setClipToPadding(enable);
+        mRow2.setClipToPadding(enable);
+        mRow3.setClipToPadding(enable);
+        setClipChildren(enable);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index e733afc..0c385da 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -198,4 +198,11 @@
     public int getWrongPasswordStringId() {
         return R.string.kg_wrong_password;
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // TODO: Fancy animation.
+        setAlpha(0);
+        animate().alpha(1).withLayer().setDuration(200);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 98122fc..5853ff9 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -400,4 +400,11 @@
         KeyguardSecurityViewHelper.
                 hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration);
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // TODO: Fancy animation.
+        setAlpha(0);
+        animate().alpha(1).withLayer().setDuration(200);
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 94edc07..382cbec 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -81,6 +81,10 @@
         getSecurityView(mCurrentSecuritySelection).onPause();
     }
 
+    public void startAppearAnimation() {
+        getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
+    }
+
     void updateSecurityViews(boolean isBouncing) {
         int children = mSecurityViewFlipper.getChildCount();
         for (int i = 0; i < children; i++) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index dfeacf3..86bd877 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -84,4 +84,9 @@
      * @param duration millisends for the transisiton animation.
      */
     void hideBouncer(int duration);
+
+    /**
+     * Starts the animation which should run when the security view appears.
+     */
+    void startAppearAnimation();
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 07239d1..178ca5e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -159,6 +159,14 @@
     }
 
     @Override
+    public void startAppearAnimation() {
+        KeyguardSecurityView ksv = getSecurityView();
+        if (ksv != null) {
+            ksv.startAppearAnimation();
+        }
+    }
+
+    @Override
     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
         return p instanceof LayoutParams;
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
index 03e7b07..98baa04 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java
@@ -244,4 +244,9 @@
         KeyguardSecurityViewHelper.
                 hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // noop.
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
index 4791956..09c4e7c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java
@@ -135,6 +135,9 @@
         mPasswordEntry.requestFocus();
 
         mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
+        if (mEcaView instanceof EmergencyCarrierArea) {
+            ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
+        }
     }
 
     @Override
@@ -270,5 +273,10 @@
             mCheckSimPinThread.start();
         }
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // noop.
+    }
 }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
index b9c7f51..6215d34 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java
@@ -186,6 +186,9 @@
         mPasswordEntry.requestFocus();
 
         mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
+        if (mEcaView instanceof EmergencyCarrierArea) {
+            ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
+        }
     }
 
     @Override
@@ -339,6 +342,11 @@
     protected void verifyPasswordAndUnlock() {
         mStateMachine.next();
     }
+
+    @Override
+    public void startAppearAnimation() {
+        // noop.
+    }
 }
 
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
index 5d5168c..3f6ced6 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
@@ -39,7 +39,7 @@
 
     @Override
     public void cleanUp() {
-        // TODO Auto-generated method stub
+        getSecurityContainer().onPause();
     }
 
     @Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index ae55c4a..bef94fa 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -95,6 +95,10 @@
         final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
         setEnableMarquee(screenOn);
         refresh();
+
+        // Disable elegant text height because our fancy colon makes the ymin value huge for no
+        // reason.
+        mClockView.setElegantTextHeight(false);
     }
 
     protected void refresh() {
@@ -164,6 +168,10 @@
 
             clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
 
+            // Use fancy colon.
+            clockView24 = clockView24.replace(':', '\uee01');
+            clockView12 = clockView12.replace(':', '\uee01');
+
             cacheKey = key;
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index a9206e7..3e444fa 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -237,15 +237,17 @@
         if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
         mSecurityContainer.showPrimarySecurityScreen(false);
         mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
-
-        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
-        // layout is blank but forcing a layout causes it to reappear (e.g. with with
-        // hierarchyviewer).
-        requestLayout();
         requestFocus();
     }
 
     /**
+     * Starts the animation when the Keyguard gets shown.
+     */
+    public void startAppearAnimation() {
+        mSecurityContainer.startAppearAnimation();
+    }
+
+    /**
      * Verify that the user can get past the keyguard securely.  This is called,
      * for example, when the phone disables the keyguard but then wants to launch
      * something else that requires secure access.
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
deleted file mode 100644
index c779437..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png
deleted file mode 100644
index 18257e0..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 8ddb375..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 57a3b99..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light.png b/packages/SystemUI/res/drawable-hdpi/search_light.png
deleted file mode 100644
index 3c0dc4e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/search_light_land.png b/packages/SystemUI/res/drawable-hdpi/search_light_land.png
deleted file mode 100644
index 731f19b5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
deleted file mode 100644
index 98ba690..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png
deleted file mode 100644
index a35c30d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 71e1303..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 1de0a3a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light.png b/packages/SystemUI/res/drawable-mdpi/search_light.png
deleted file mode 100644
index 8010ce7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/search_light_land.png b/packages/SystemUI/res/drawable-mdpi/search_light_land.png
deleted file mode 100644
index a4d82f0..0000000
--- a/packages/SystemUI/res/drawable-mdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/heads_up_window_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/heads_up_window_bg.9.png
deleted file mode 100644
index b30cf15..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/heads_up_window_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 8014b70..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 41a34e2..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 9c623e5..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index a011aa1..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/heads_up_window_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/heads_up_window_bg.9.png
deleted file mode 100644
index 31eb8f7..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/heads_up_window_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 61a36e3..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 52bf290..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/heads_up_window_bg.9.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/heads_up_window_bg.9.png
deleted file mode 100644
index c76d0e1..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/heads_up_window_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index e5d4273..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 1cc5009..0000000
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
deleted file mode 100644
index 61947ea..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_open_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_open_24dp.png
deleted file mode 100644
index 467d558..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_lock_open_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png
deleted file mode 100644
index d14a67f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index c44aafc..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index 05da6da..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light.png b/packages/SystemUI/res/drawable-xhdpi/search_light.png
deleted file mode 100644
index 6d46fdd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/search_light_land.png b/packages/SystemUI/res/drawable-xhdpi/search_light_land.png
deleted file mode 100644
index b62c74e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
deleted file mode 100644
index 0b563b1..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png
deleted file mode 100644
index 07f16c3..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_default_user.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight.png
deleted file mode 100644
index 0df6203..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight_land.png
deleted file mode 100644
index b400b14..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_highlight_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light.png b/packages/SystemUI/res/drawable-xxhdpi/search_light.png
deleted file mode 100644
index 7742207..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/search_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png b/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png
deleted file mode 100644
index f364577..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/search_light_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
deleted file mode 100644
index 3600ee6..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_open_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_open_24dp.png
deleted file mode 100644
index e7d2a9a..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_open_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml
new file mode 100644
index 0000000..a7e8514
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_account_circle.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="24dp"
+        android:height="24dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_24dp.xml
new file mode 100644
index 0000000..b2e486c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="24.0dp"
+        android:height="24.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="@color/keyguard_affordance"
+        android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_open_24dp.xml
new file mode 100644
index 0000000..28b16dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_open_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="24.0dp"
+        android:height="24.0dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="@color/keyguard_affordance"
+        android:pathData="M12.0,17.0c1.1,0.0 2.0,-0.9 2.0,-2.0s-0.9,-2.0 -2.0,-2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0S10.9,17.0 12.0,17.0zM18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l1.9,0.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM18.0,20.0L6.0,20.0L6.0,10.0l12.0,0.0L18.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
index 3a20c58..787eec5 100644
--- a/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_ringer_audible.xml
@@ -24,5 +24,5 @@
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M6.6,3.6L5.2,2.2C2.8,4.0 1.2,6.8 1.0,10.0l2.0,0.0C3.2,7.3 4.5,5.0 6.6,3.6zM20.0,10.0l2.0,0.0c-0.2,-3.2 -1.7,-6.0 -4.1,-7.8l-1.4,1.4C18.5,5.0 19.8,7.3 20.0,10.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.0,10.5zM11.5,22.0c0.1,0.0 0.3,0.0 0.4,0.0c0.7,-0.1 1.2,-0.6 1.4,-1.2c0.1,-0.2 0.2,-0.5 0.2,-0.8l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0z" />
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_ringer_audible.xml
new file mode 100644
index 0000000..2969948
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ringer_audible.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_silent.xml b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
new file mode 100644
index 0000000..b5837f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ringer_silent.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7C9.5,4.3 9.0,4.5 8.6,4.7l9.4,9.4L18.0,10.5zM17.7,19.0l2.0,2.0l1.3,-1.3L4.3,3.0L3.0,4.3l2.9,2.9C5.3,8.2 5.0,9.3 5.0,10.5L5.0,16.0l-2.0,2.0l0.0,1.0L17.7,19.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
new file mode 100644
index 0000000..d8ded58
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_off.xml b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
new file mode 100644
index 0000000..ea5ab70
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_off.xml
@@ -0,0 +1,30 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#00000000"
+        android:stroke="#CCCCCC"
+        android:strokeWidth="1.0"
+        android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vol_zen_on.xml b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
new file mode 100644
index 0000000..44024f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vol_zen_on.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="32dp"
+        android:height="32dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM4.0,12.0c0.0,-4.4 3.6,-8.0 8.0,-8.0c1.8,0.0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4.0,13.8 4.0,12.0zM12.0,20.0c-1.8,0.0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20.0,10.2 20.0,12.0C20.0,16.4 16.4,20.0 12.0,20.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
index 09d0d7d..5daec20 100644
--- a/packages/SystemUI/res/drawable/notification_header_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -19,13 +19,11 @@
     <item android:state_pressed="true">
         <shape>
             <solid android:color="@color/background_color_1_press" />
-            <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
         </shape>
     </item>
     <item>
         <shape>
             <solid android:color="@color/background_color_1" />
-            <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
         </shape>
     </item>
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_panel_background.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml
index c324976..a1a5362 100644
--- a/packages/SystemUI/res/drawable/qs_panel_background.xml
+++ b/packages/SystemUI/res/drawable/qs_panel_background.xml
@@ -13,11 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:insetLeft="@dimen/notification_side_padding"
-        android:insetRight="@dimen/notification_side_padding">
-    <shape>
-        <solid android:color="@color/system_primary_color" />
-        <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
-    </shape>
-</inset>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/system_primary_color" />
+    <corners
+        android:radius="@*android:dimen/notification_quantum_rounded_rect_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/ripple_drawable.xml b/packages/SystemUI/res/drawable/ripple_drawable.xml
new file mode 100644
index 0000000..d2bff42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ripple_drawable.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:tint="?android:attr/colorControlHighlight"
+        android:tintMode="src_over"
+        android:pinned="true" />
diff --git a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
index 5f12706..a291495 100644
--- a/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-ldrtl/navigation_bar.xml
@@ -55,7 +55,7 @@
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:scaleType="center"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_back"
                 />
             <View 
@@ -71,7 +71,7 @@
                 systemui:keyCode="3"
                 systemui:keyRepeat="false"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_home"
                 />
             <View 
@@ -85,7 +85,7 @@
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_recent"
                 />
             <FrameLayout
@@ -99,7 +99,7 @@
                     android:contentDescription="@string/accessibility_menu"
                     android:src="@drawable/ic_sysbar_menu"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                    android:background="@drawable/ripple_drawable"
                     systemui:keyCode="82" />
                 <com.android.systemui.statusbar.policy.KeyButtonView
                     android:id="@+id/ime_switcher"
@@ -109,7 +109,7 @@
                     android:scaleType="centerInside"
                     android:src="@drawable/ic_ime_switcher_default"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+                    android:background="@drawable/ripple_drawable" />
             </FrameLayout>
         </LinearLayout>
 
@@ -158,17 +158,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:layout_width="80dp"
-            android:id="@+id/search_light"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
@@ -212,7 +201,7 @@
                     android:layout_weight="0"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_menu"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight_land" />
+                    android:background="@drawable/ripple_drawable" />
                 <com.android.systemui.statusbar.policy.KeyButtonView
                     android:id="@+id/ime_switcher"
                     android:layout_height="@dimen/navigation_extra_key_width"
@@ -221,7 +210,7 @@
                     android:scaleType="centerInside"
                     android:src="@drawable/ic_ime_switcher_default"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+                    android:background="@drawable/ripple_drawable" />
             </FrameLayout>
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
                 android:layout_height="80dp"
@@ -231,7 +220,7 @@
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_back"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                android:background="@drawable/ripple_drawable"
                 />
             <View
                 android:layout_height="match_parent"
@@ -247,7 +236,7 @@
                 systemui:keyRepeat="false"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_home"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                android:background="@drawable/ripple_drawable"
                 />
             <View 
                 android:layout_height="match_parent"
@@ -261,7 +250,7 @@
                 android:src="@drawable/ic_sysbar_recent_land"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_recent"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                android:background="@drawable/ripple_drawable"
                 />
             <View
                 android:layout_height="40dp"
@@ -316,17 +305,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:id="@+id/search_light"
-            android:layout_height="80dp"
-            android:layout_width="match_parent"
-            android:layout_gravity="center_vertical"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout-sw600dp/heads_up.xml b/packages/SystemUI/res/layout-sw600dp/heads_up.xml
deleted file mode 100644
index f7035fe..0000000
--- a/packages/SystemUI/res/layout-sw600dp/heads_up.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<com.android.systemui.statusbar.policy.HeadsUpNotificationView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
-    android:layout_width="match_parent"
-    >
-    <FrameLayout
-        android:id="@+id/content_holder"
-        android:layout_height="wrap_content"
-        android:layout_width="@dimen/notification_panel_width"
-        android:background="@drawable/heads_up_window_bg"
-        />
-</com.android.systemui.statusbar.policy.HeadsUpNotificationView>
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 6a2e3c6..f8b7bae 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -59,7 +59,7 @@
                 android:src="@drawable/ic_sysbar_back"
                 systemui:keyCode="4"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_back"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
@@ -69,7 +69,7 @@
                 systemui:keyCode="3"
                 systemui:keyRepeat="true"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_home"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
@@ -77,7 +77,7 @@
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_recent"
                 />
             <Space 
@@ -98,7 +98,7 @@
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_menu"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                    android:background="@drawable/ripple_drawable"
                     />
                 <com.android.systemui.statusbar.policy.KeyButtonView
                     android:id="@+id/ime_switcher"
@@ -109,7 +109,7 @@
                     android:src="@drawable/ic_ime_switcher_default"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_ime_switch_button"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+                    android:background="@drawable/ripple_drawable" />
             </FrameLayout>
         </LinearLayout>
 
@@ -156,17 +156,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:layout_width="128dp"
-            android:id="@+id/search_light"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
@@ -216,7 +205,7 @@
                 android:src="@drawable/ic_sysbar_back"
                 systemui:keyCode="4"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_back"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
@@ -226,7 +215,7 @@
                 systemui:keyCode="3"
                 systemui:keyRepeat="true"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_home"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
@@ -234,7 +223,7 @@
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_recent"
                 />
             <Space 
@@ -255,7 +244,7 @@
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_menu"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                    android:background="@drawable/ripple_drawable"
                     />
                 <com.android.systemui.statusbar.policy.KeyButtonView
                     android:id="@+id/ime_switcher"
@@ -266,7 +255,7 @@
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_ime_switch_button"
                     android:scaleType="centerInside"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+                    android:background="@drawable/ripple_drawable" />
             </FrameLayout>
         </LinearLayout>
 
@@ -313,17 +302,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:layout_width="162dp"
-            android:id="@+id/search_light"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:src="@drawable/search_light"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index 7d9cfa1..236fdc3 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -1,26 +1,30 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** Copyright 2006, 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.
-*/
+     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.
 -->
 <com.android.systemui.statusbar.policy.HeadsUpNotificationView
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:id="@+id/content_holder"
-        android:background="@drawable/notification_panel_bg"
-        />
\ No newline at end of file
+        android:layout_height="match_parent"
+        android:layout_width="match_parent">
+
+        <FrameLayout
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/notification_side_padding"
+                android:layout_marginEnd="@dimen/notification_side_padding"
+                android:elevation="16dp"
+                android:id="@+id/content_holder"
+                style="@style/NotificationsQuickSettings" />
+
+</com.android.systemui.statusbar.policy.HeadsUpNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 2ec3766..9bf42b2 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,7 +22,7 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     >
-    <com.android.systemui.statusbar.phone.SwipeAffordanceView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/camera_button"
         android:layout_height="64dp"
         android:layout_width="64dp"
@@ -30,11 +30,9 @@
         android:tint="#ffffffff"
         android:src="@drawable/ic_camera_alt_24dp"
         android:scaleType="center"
-        android:contentDescription="@string/accessibility_camera_button"
-        systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
-        systemui:swipeDirection="start"/>
+        android:contentDescription="@string/accessibility_camera_button" />
 
-    <com.android.systemui.statusbar.phone.SwipeAffordanceView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/phone_button"
         android:layout_height="64dp"
         android:layout_width="64dp"
@@ -42,9 +40,7 @@
         android:tint="#ffffffff"
         android:src="@drawable/ic_phone_24dp"
         android:scaleType="center"
-        android:contentDescription="@string/accessibility_phone_button"
-        systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
-        systemui:swipeDirection="end"/>
+        android:contentDescription="@string/accessibility_phone_button" />
 
     <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
         android:id="@+id/keyguard_indication_text"
@@ -53,17 +49,16 @@
         android:layout_marginBottom="100dp"
         android:layout_gravity="bottom|center_horizontal"
         android:textStyle="italic"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
+        android:textColor="#ffffff"
+        android:textAppearance="?android:attr/textAppearanceSmall"/>
 
-    <ImageView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/lock_icon"
         android:layout_width="64dp"
         android:layout_height="64dp"
         android:layout_gravity="bottom|center_horizontal"
         android:src="@drawable/ic_lock_24dp"
         android:scaleType="center"
-        android:alpha="0.7"
-        android:layerType="hardware"
-        android:tint="#ffffffff"/>
+        android:tint="#ffffffff" />
 
-</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
\ No newline at end of file
+</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 7470409..7616cb1 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -55,7 +55,7 @@
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:scaleType="center"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_back"
                 />
             <View 
@@ -71,7 +71,7 @@
                 systemui:keyCode="3"
                 systemui:keyRepeat="false"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_home"
                 />
             <View 
@@ -85,7 +85,7 @@
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:background="@drawable/ripple_drawable"
                 android:contentDescription="@string/accessibility_recent"
                 />
             <FrameLayout
@@ -99,7 +99,7 @@
                     android:contentDescription="@string/accessibility_menu"
                     android:src="@drawable/ic_sysbar_menu"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                    android:background="@drawable/ripple_drawable"
                     systemui:keyCode="82" />
 
                 <com.android.systemui.statusbar.policy.KeyButtonView
@@ -110,7 +110,7 @@
                     android:scaleType="centerInside"
                     android:src="@drawable/ic_ime_switcher_default"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+                    android:background="@drawable/ripple_drawable" />
             </FrameLayout>
 
         </LinearLayout>
@@ -160,22 +160,6 @@
                 />
         </LinearLayout>
 
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-
-            <com.android.systemui.statusbar.policy.KeyButtonView
-                android:layout_width="80dp"
-                android:id="@+id/search_light"
-                android:layout_height="match_parent"
-                android:layout_gravity="center"
-                android:src="@drawable/search_light"
-                android:scaleType="center"
-                android:visibility="gone"
-                android:contentDescription="@string/accessibility_search_light"
-                />
-        </FrameLayout>
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
@@ -219,7 +203,7 @@
                     android:scaleType="centerInside"
                     android:src="@drawable/ic_ime_switcher_default"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight" />
+                    android:background="@drawable/ripple_drawable" />
 
                 <com.android.systemui.statusbar.policy.KeyButtonView
                     android:id="@+id/menu"
@@ -228,7 +212,7 @@
                     android:contentDescription="@string/accessibility_menu"
                     android:src="@drawable/ic_sysbar_menu_land"
                     android:visibility="invisible"
-                    systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                    android:background="@drawable/ripple_drawable"
                     systemui:keyCode="82" />
             </FrameLayout>
 
@@ -238,7 +222,7 @@
                 android:src="@drawable/ic_sysbar_recent_land"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_recent"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                android:background="@drawable/ripple_drawable"
                 />
             <View 
                 android:layout_height="match_parent"
@@ -254,7 +238,7 @@
                 systemui:keyRepeat="false"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_home"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                android:background="@drawable/ripple_drawable"
                 />
             <View 
                 android:layout_height="match_parent"
@@ -270,7 +254,7 @@
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_back"
-                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
+                android:background="@drawable/ripple_drawable"
                 />
             <View
                 android:layout_height="40dp"
@@ -325,19 +309,6 @@
                 />
         </LinearLayout>
 
-        <com.android.systemui.statusbar.policy.KeyButtonView
-            android:id="@+id/search_light"
-            android:layout_height="80dp"
-            android:layout_width="match_parent"
-            android:layout_gravity="center_vertical"
-            android:src="@drawable/search_light_land"
-            android:scaleType="center"
-            android:visibility="gone"
-            android:contentDescription="@string/accessibility_search_light"
-            />
-
-        <!-- No camera button in landscape mode -->
-
         <com.android.systemui.statusbar.policy.DeadZone
             android:id="@+id/deadzone"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
new file mode 100644
index 0000000..e73b431
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/system_primary_color" >
+
+    <ImageView
+        android:id="@android:id/button1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_alignParentStart="true"
+        android:contentDescription="@string/accessibility_quick_settings_close"
+        android:padding="@dimen/qs_panel_padding"
+        android:src="@drawable/ic_qs_close" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:layout_alignParentTop="true"
+        android:layout_toEndOf="@android:id/button1"
+        android:layout_toStartOf="@android:id/checkbox"
+        android:gravity="center_vertical"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+    <ImageView
+        android:id="@android:id/custom"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="16dip"
+        android:scaleType="fitXY"
+        android:src="?android:attr/dividerHorizontal" />
+
+    <FrameLayout
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_below="@android:id/custom" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 85de645..398787f 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -16,11 +16,10 @@
 <FrameLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/quick_settings_container"
-        android:paddingLeft="@dimen/notification_side_padding"
-        android:paddingRight="@dimen/notification_side_padding"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@drawable/qs_panel_background" >
+        android:background="@drawable/qs_panel_background"
+        android:elevation="2dp">
     <com.android.systemui.qs.QSPanel
             android:id="@+id/quick_settings_panel"
             android:background="#0000"
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
deleted file mode 100644
index 85b294d..0000000
--- a/packages/SystemUI/res/layout/qs_zen_mode_detail.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<com.android.systemui.qs.tiles.ZenModeDetail xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@color/system_secondary_color" >
-
-    <ImageView
-        android:id="@android:id/button1"
-        android:src="@drawable/ic_qs_close"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentStart="true"
-        android:padding="@dimen/qs_panel_padding" />
-
-    <Switch
-        android:id="@android:id/checkbox"
-        android:layout_width="wrap_content"
-        android:layout_height="64dp"
-        android:layout_alignParentEnd="true"
-        android:gravity="center"
-        android:padding="@dimen/qs_panel_padding" />
-
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="match_parent"
-        android:layout_height="64dp"
-        android:layout_toEndOf="@android:id/button1"
-        android:layout_toStartOf="@android:id/checkbox"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:gravity="center_vertical"
-        android:paddingStart="@dimen/qs_panel_padding"
-        android:text="@string/zen_mode_title" />
-
-    <View
-        android:id="@android:id/custom"
-        android:layout_width="match_parent"
-        android:layout_height="2dp"
-        android:layout_below="@android:id/title"
-        android:background="#888" />
-
-    <ListView
-        android:id="@android:id/content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_above="@android:id/button2"
-        android:layout_below="@android:id/custom"
-        android:divider="#00000000"
-        android:dividerHeight="0px" />
-
-    <TextView
-        android:id="@android:id/button2"
-        style="@style/QSBorderless"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_alignParentEnd="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:padding="@dimen/qs_panel_padding"
-        android:text="@string/quick_settings_more_settings"
-        android:textAllCaps="true" />
-
-</com.android.systemui.qs.tiles.ZenModeDetail>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml b/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
deleted file mode 100644
index fd27aaf..0000000
--- a/packages/SystemUI/res/layout/qs_zen_mode_detail_condition.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <RadioButton
-        android:id="@android:id/checkbox"
-        android:layout_width="32dp"
-        android:layout_height="64dp"
-        android:layout_alignParentStart="true"
-        android:layout_marginStart="@dimen/qs_panel_padding"
-        android:gravity="center" />
-
-    <TextView
-        android:id="@android:id/title"
-        android:layout_width="match_parent"
-        android:layout_height="64dp"
-        android:layout_toEndOf="@android:id/checkbox"
-        android:layout_toStartOf="@android:id/button1"
-        android:ellipsize="end"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-        android:gravity="center_vertical"
-        android:maxLines="1"
-        android:text="@string/accessibility_back" />
-
-    <ImageView
-        android:id="@android:id/button1"
-        android:src="@drawable/ic_qs_minus"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentEnd="true"
-        android:layout_marginEnd="48dp"
-        android:padding="@dimen/qs_panel_padding"
-        android:paddingRight="0px" />
-
-    <ImageView
-        android:id="@android:id/button2"
-        android:src="@drawable/ic_qs_plus"
-        android:layout_width="64dp"
-        android:layout_height="64dp"
-        android:layout_alignParentEnd="true"
-        android:padding="@dimen/qs_panel_padding" />
-
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 1efda8c..97ed9a0 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -15,7 +15,8 @@
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
-    style="@style/BrightnessDialogContainer">
+    style="@style/BrightnessDialogContainer"
+    android:clickable="true">
 
     <ImageView
         android:id="@+id/brightness_icon"
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 7de421c..85d2f16 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -25,36 +25,37 @@
     <com.android.systemui.recents.views.TaskBarView
         android:id="@+id/task_view_bar"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="56dp"
         android:layout_gravity="top|center_horizontal"
         android:background="@color/recents_task_bar_default_background_color">
         <ImageView
             android:id="@+id/application_icon"
             android:layout_width="@dimen/recents_task_view_application_icon_size"
             android:layout_height="@dimen/recents_task_view_application_icon_size"
-            android:layout_gravity="center_vertical|start"
-            android:padding="8dp" />
+            android:layout_marginStart="16dp"
+            android:layout_gravity="center_vertical|start" />
         <TextView
             android:id="@+id/activity_description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical|left"
-            android:layout_marginStart="@dimen/recents_task_view_application_icon_size"
-            android:layout_marginEnd="@dimen/recents_task_view_application_icon_size"
-            android:textSize="22sp"
+            android:layout_gravity="center_vertical|start"
+            android:layout_marginStart="64dp"
+            android:layout_marginEnd="64dp"
+            android:textSize="16sp"
             android:textColor="#ffffffff"
             android:text="@string/recents_empty_message"
-            android:fontFamily="sans-serif-light"
+            android:fontFamily="sans-serif-medium"
             android:singleLine="true"
             android:maxLines="2"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
         <ImageView
             android:id="@+id/dismiss_task"
-            android:layout_width="@dimen/recents_task_view_application_icon_size"
-            android:layout_height="@dimen/recents_task_view_application_icon_size"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_marginEnd="4dp"
             android:layout_gravity="center_vertical|end"
-            android:padding="23dp"
+            android:padding="18dp"
             android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2ec9935..cde83bf 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -59,8 +59,8 @@
             android:id="@+id/scroll_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:visibility="invisible"
             android:scrollbars="none"
+            android:overScrollMode="never"
             android:fillViewport="true">
             <LinearLayout
                 android:layout_width="match_parent"
@@ -70,7 +70,9 @@
                     layout="@layout/qs_panel"
                     android:layout_marginTop="@dimen/status_bar_header_height_expanded"
                     android:layout_width="match_parent"
-                    android:layout_height="wrap_content"/>
+                    android:layout_height="wrap_content"
+                    android:layout_marginLeft="@dimen/notification_side_padding"
+                    android:layout_marginRight="@dimen/notification_side_padding"/>
 
                 <!-- A view to reserve space for the collapsed stack -->
                 <View
@@ -79,7 +81,6 @@
             </LinearLayout>
         </com.android.systemui.statusbar.phone.ObservableScrollView>
 
-
         <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 89fa988..7f34041 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -25,7 +25,7 @@
     android:paddingStart="@dimen/notification_side_padding"
     android:paddingEnd="@dimen/notification_side_padding"
     android:baselineAligned="false"
-    android:elevation="10dp"
+    android:elevation="4dp"
     >
 
     <View
@@ -65,22 +65,13 @@
             />
     </RelativeLayout>
 
-    <com.android.keyguard.CarrierText
-        android:id="@+id/keyguard_carrier_text"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/status_bar_header_height_keyguard"
-        android:layout_marginLeft="8dp"
-        android:gravity="center_vertical"
-        android:ellipsize="marquee"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
-
     <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
         android:layout_width="40dp"
         android:layout_height="@dimen/status_bar_header_height"
         android:layout_alignParentEnd="true"
         android:background="@null"
         android:scaleType="centerInside"
-        android:padding="6dp"
+        android:padding="8dp"
         />
 
     <ImageButton android:id="@+id/settings_button"
@@ -98,6 +89,18 @@
         android:layout_marginEnd="4dp"
         />
 
+    <com.android.keyguard.CarrierText
+        android:id="@+id/keyguard_carrier_text"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/status_bar_header_height_keyguard"
+        android:layout_marginLeft="8dp"
+        android:layout_toStartOf="@id/system_icons_container"
+        android:gravity="center_vertical"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="#ffffff"
+        android:singleLine="true" />
+
     <include
         layout="@layout/quick_settings_brightness_dialog"
         android:id="@+id/brightness_container"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 26616cd..e84300d 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -26,6 +26,10 @@
     android:fitsSystemWindows="true"
     android:descendantFocusability="afterDescendants">
 
+    <View android:id="@+id/scrim_behind"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
     <include layout="@layout/status_bar"
         android:layout_width="match_parent"
         android:layout_height="@dimen/status_bar_height" />
@@ -40,4 +44,8 @@
             android:visibility="gone" />
     </com.android.systemui.statusbar.phone.PanelHolder>
 
+    <View android:id="@+id/scrim_in_front"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
 </com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index 70c5042..816af57 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -27,7 +27,7 @@
     <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@*android:dimen/volume_panel_top"
+            android:layout_marginTop="@dimen/volume_panel_top"
             android:background="@*android:drawable/dialog_full_holo_dark">
         <ListView android:id="@android:id/list"
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
new file mode 100644
index 0000000..046862f
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2007 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/visible_panel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal" >
+
+    <FrameLayout
+        android:id="@+id/slider_panel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="64dip"
+        android:layout_toLeftOf="@+id/expand_button_divider" />
+
+    <ImageView
+        android:id="@+id/expand_button_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="32dip"
+        android:layout_gravity="top"
+        android:layout_marginBottom="16dip"
+        android:layout_marginTop="16dip"
+        android:layout_toLeftOf="@+id/expand_button"
+        android:scaleType="fitXY"
+        android:src="?android:attr/dividerVertical" />
+
+    <ImageView
+        android:id="@+id/expand_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_gravity="top"
+        style="@style/BorderlessButton.Tiny"
+        android:padding="16dip" />
+
+    <ImageView
+        android:id="@+id/zen_panel_divider"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/slider_panel"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="16dip"
+        android:scaleType="fitXY"
+        android:src="?android:attr/dividerHorizontal" />
+
+    <ViewStub
+        android:id="@+id/zen_panel_stub"
+        android:layout_below="@+id/zen_panel_divider"
+        android:inflatedId="@+id/zen_panel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout="@layout/zen_mode_panel" />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/volume_adjust_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
similarity index 94%
rename from core/res/res/layout/volume_adjust_item.xml
rename to packages/SystemUI/res/layout/volume_panel_item.xml
index 57cecf4..98cb8f4 100644
--- a/core/res/res/layout/volume_adjust_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -27,7 +27,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:paddingLeft="16dip"
-        android:background="?attr/selectableItemBackground"
+        android:background="?android:attr/selectableItemBackground"
         android:contentDescription="@null" />
 
     <SeekBar
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
new file mode 100644
index 0000000..8b344000
--- /dev/null
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" >
+
+    <RadioButton
+        android:id="@android:id/checkbox"
+        android:layout_width="32dp"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_alignParentStart="true"
+        android:gravity="center" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_toEndOf="@android:id/checkbox"
+        android:layout_toStartOf="@android:id/button1"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:maxLines="1"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+    <ImageView
+        android:id="@android:id/button1"
+        style="@style/BorderlessButton"
+        android:layout_width="@dimen/zen_mode_condition_height"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="@dimen/zen_mode_condition_height"
+        android:contentDescription="@string/accessibility_quick_settings_less_time"
+        android:padding="@dimen/zen_mode_condition_detail_button_padding"
+        android:src="@drawable/ic_qs_minus" />
+
+    <ImageView
+        android:id="@android:id/button2"
+        style="@style/BorderlessButton"
+        android:layout_width="@dimen/zen_mode_condition_height"
+        android:layout_height="@dimen/zen_mode_condition_height"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:contentDescription="@string/accessibility_quick_settings_more_time"
+        android:padding="@dimen/zen_mode_condition_detail_button_padding"
+        android:src="@drawable/ic_qs_plus" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
new file mode 100644
index 0000000..0936cc2
--- /dev/null
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<!-- extends LinearLayout -->
+<com.android.systemui.volume.ZenModePanel xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/zen_mode_panel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@color/system_primary_color"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/qs_panel_padding"
+    android:paddingLeft="@dimen/qs_panel_padding"
+    android:paddingRight="@dimen/qs_panel_padding" >
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_marginBottom="8dp"
+        android:text="@string/zen_mode_title"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+    <LinearLayout
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" />
+
+    <TextView
+        android:id="@android:id/button2"
+        style="@style/BorderlessButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_gravity="end"
+        android:text="@string/quick_settings_more_settings"
+        android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+</com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 22815f3..5750faa 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -57,6 +57,6 @@
 
     <!-- The margin between the clock and the notifications on Keyguard. See
          keyguard_clock_height_fraction_* for the difference between min and max.-->
-    <dimen name="keyguard_clock_notifications_margin_min">32dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">32dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 8fd1206..c453618 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -20,8 +20,6 @@
         <attr name="keyCode" format="integer" />
         <!-- does this button generate longpress / repeat events? -->
         <attr name="keyRepeat" format="boolean" />
-        <!-- drawable to use for a swelling, glowing background on press -->
-        <attr name="glowBackground" format="reference" />
     </declare-styleable>
     <declare-styleable name="ToggleSlider">
         <attr name="text" format="string" />
@@ -45,12 +43,6 @@
     <declare-styleable name="BatteryMeterView">
         <attr name="frameColor" format="color" />
     </declare-styleable>
-    <declare-styleable name="SwipeAffordanceView">
-        <attr name="swipeDirection" format="enum">
-            <enum name="start" value="0" />
-            <enum name="end" value="1" />
-        </attr>
-    </declare-styleable>
     <declare-styleable name="Clock">
         <attr name="amPmStyle" format="enum">
             <enum name="normal" value="0" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e5ed3d6..e6fa535 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -31,8 +31,6 @@
     <drawable name="recents_callout_line">#99ffffff</drawable>
     <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
     <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
-    <color name="notification_panel_scrim_color">#A0000000</color>
-    <color name="notification_panel_scrim_color_keyguard">#80000000</color>
     <color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
     <color name="batterymeter_charge_color">#FFFFFFFF</color>
     <color name="batterymeter_bolt_color">#FFFFFFFF</color>
@@ -69,12 +67,14 @@
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
-    <color name="recents_task_bar_dark_text_color">#ff222222</color>
+    <color name="recents_task_bar_dark_text_color">#ff333333</color>
     <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
     <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
     <color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
 
+    <color name="keyguard_affordance">#ffffffff</color>
+
     <!-- Our quantum color palette (deep teal) -->
     <color name="primary_color">#ff7fcac3</color>
     <color name="background_color_1">#ff384248</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0bd4f18..0184df2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -113,9 +113,9 @@
     <!-- The min animation duration for animating views that are newly visible. -->
     <integer name="recents_filter_animate_new_views_min_duration">125</integer>
     <!-- The min animation duration for animating the task bar in. -->
-    <integer name="recents_animate_task_bar_enter_duration">225</integer>
+    <integer name="recents_animate_task_bar_enter_duration">300</integer>
     <!-- The min animation duration for animating the task bar out. -->
-    <integer name="recents_animate_task_bar_exit_duration">175</integer>
+    <integer name="recents_animate_task_bar_exit_duration">150</integer>
     <!-- The animation duration for animating in the info pane. -->
     <integer name="recents_animate_task_view_info_pane_duration">150</integer>
     <!-- The animation duration for animating the removal of a task view. -->
@@ -135,5 +135,8 @@
     <!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can
          be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
     <string name="velocity_tracker_impl" translatable="false">platform</string>
+
+    <!-- Wait on the touch feedback this long before performing an action. -->
+    <integer name="feedback_start_delay">300</integer>
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e9dcea2..e65d9a6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -196,6 +196,12 @@
     <dimen name="qs_dual_tile_height">109dp</dimen>
     <dimen name="qs_dual_tile_padding">12dp</dimen>
 
+    <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
+    <dimen name="qs_peek_height">8dp</dimen>
+
+    <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
+    <dimen name="zen_mode_condition_height">48dp</dimen>
+
     <!-- used by DessertCase -->
     <dimen name="dessert_case_cell_size">192dp</dimen>
 
@@ -206,7 +212,7 @@
     <dimen name="glowpadview_inner_radius">15dip</dimen>
 
     <!-- The size of the application icon in the recents task view. -->
-    <dimen name="recents_task_view_application_icon_size">60dp</dimen>
+    <dimen name="recents_task_view_application_icon_size">32dp</dimen>
 
     <!-- The size of the activity icon in the recents task view. -->
     <dimen name="recents_task_view_activity_icon_size">60dp</dimen>
@@ -215,10 +221,13 @@
     <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
 
     <!-- The min translation in the Z index for the last task. -->
-    <dimen name="recents_task_view_z_min">3dp</dimen>
+    <dimen name="recents_task_view_z_min">5dp</dimen>
 
     <!-- The translation in the Z index for each task above the last task. -->
-    <dimen name="recents_task_view_z_increment">5dp</dimen>
+    <dimen name="recents_task_view_z_increment">10dp</dimen>
+
+    <!-- The amount of bottom inset in the shadow outline. -->
+    <dimen name="recents_task_view_shadow_outline_bottom_inset">5dp</dimen>
 
     <!-- The amount to translate when animating the removal of a task. -->
     <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
@@ -288,4 +297,12 @@
     <dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
     <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
 
+    <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
+    <dimen name="keyguard_min_swipe_amount">75dp</dimen>
+
+    <!-- Volume panel dialog y offset -->
+    <dimen name="volume_panel_top">16dp</dimen>
+
+    <!-- Volume panel dialog width -->
+    <dimen name="volume_panel_width">300dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f5bc353..ef3956e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -388,6 +388,12 @@
     <string name="accessibility_quick_settings_location">Location <xliff:g id="state" example="Off">%s</xliff:g>.</string>
     <!-- Content description of the alarm tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_alarm">Alarm set for <xliff:g id="time" example="Wed 3:30 PM">%s</xliff:g>.</string>
+    <!-- Content description of quick settings detail panel close button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_close">Close panel</string>
+    <!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_more_time">More time</string>
+    <!-- Content description of zen mode time condition minus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_less_time">Less time</string>
 
     <!-- Title of dialog shown when 2G-3G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
     <string name="data_usage_disabled_dialog_3g_title">2G-3G data disabled</string>
@@ -512,6 +518,8 @@
     <string name="quick_settings_tethering_label">Tethering</string>
     <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_hotspot_label">Hotspot</string>
+    <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_notifications_label">Notifications</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">RECENTS</string>
@@ -563,4 +571,19 @@
     <string name="keyguard_unlock">Swipe up to unlock</string>
 
     <string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
+
+    <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_forever">Until you turn this off</string>
+
+    <!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_duration_minutes">
+        <item quantity="one">For one minute</item>
+        <item quantity="other">For %d minutes</item>
+    </plurals>
+
+    <!-- Zen mode condition: time duration in hours. [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_duration_hours">
+        <item quantity="one">For one hour</item>
+        <item quantity="other">For %d hours</item>
+    </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 19888a8..6a12232 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -133,6 +133,32 @@
         <item name="android:fadingEdge">horizontal</item>
     </style>
 
+    <style name="TextAppearance.QS">
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#ffffff</item>
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailHeader">
+        <item name="android:textSize">20sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailItemPrimary">
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailItemSecondary">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">#7fcac3</item>
+    </style>
+
+    <style name="TextAppearance.QS.DetailButton">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -192,9 +218,9 @@
         <item name="android:colorControlActivated">@color/system_accent_color</item>
     </style>
 
-    <style name="QSBorderless" parent="@android:style/Widget.Quantum.Button.Borderless" />
+    <style name="BorderlessButton" parent="@android:style/Widget.Quantum.Button.Borderless" />
 
-    <style name="QSBorderless.Tiny">
+    <style name="BorderlessButton.Tiny">
         <item name="android:minHeight">12dip</item>
         <item name="android:minWidth">12dip</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 6387a92..1b12cb0 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -56,6 +56,7 @@
     static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
                                               // beyond which alpha->0
     private float mMinAlpha = 0f;
+    private float mMaxAlpha = 1f;
 
     private float mPagingTouchSlop;
     private Callback mCallback;
@@ -140,6 +141,10 @@
         mMinAlpha = minAlpha;
     }
 
+    public void setMaxAlpha(float maxAlpha) {
+        mMaxAlpha = maxAlpha;
+    }
+
     private float getAlphaForOffset(View view) {
         float viewSize = getSize(view);
         final float fadeSize = ALPHA_FADE_END * viewSize;
@@ -150,7 +155,7 @@
         } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
             result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
         }
-        return Math.max(mMinAlpha, result);
+        return Math.min(Math.max(mMinAlpha, result), mMaxAlpha);
     }
 
     private void updateAlphaFromOffset(View animView, boolean dismissable) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index d7ce255..630ba13 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,12 +42,12 @@
     private final Class<?>[] SERVICES = new Class[] {
             com.android.systemui.keyguard.KeyguardViewMediator.class,
             com.android.systemui.recent.Recents.class,
+            com.android.systemui.volume.VolumeUI.class,
             com.android.systemui.statusbar.SystemBars.class,
             com.android.systemui.usb.StorageNotification.class,
             com.android.systemui.power.PowerUI.class,
             com.android.systemui.media.RingtonePlayer.class,
             com.android.systemui.settings.SettingsUI.class,
-            com.android.systemui.volume.VolumeUI.class,
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 41c0e78..4c7f3df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -202,6 +202,12 @@
             checkPermission();
             mKeyguardViewMediator.onBootCompleted();
         }
+
+        @Override
+        public void startKeyguardExitAnimation(long fadeoutDuration) {
+            checkPermission();
+            mKeyguardViewMediator.startKeyguardExitAnimation(fadeoutDuration);
+        }
     };
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e73e904..7110d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -46,7 +46,8 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.ViewGroup;
-import android.view.WindowManager;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.policy.IKeyguardExitCallback;
@@ -62,6 +63,7 @@
 import com.android.keyguard.analytics.Session;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 
@@ -136,6 +138,7 @@
     private static final int SET_OCCLUDED = 12;
     private static final int KEYGUARD_TIMEOUT = 13;
     private static final int DISMISS = 17;
+    private static final int START_KEYGUARD_EXIT_ANIM = 18;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -179,6 +182,9 @@
     /** High level access to the power manager for WakeLocks */
     private PowerManager mPM;
 
+    /** High level access to the window manager for dismissing keyguard animation */
+    private IWindowManager mWM;
+
     /** UserManager for querying number of users */
     private UserManager mUserManager;
 
@@ -439,6 +445,7 @@
 
     private void setup() {
         mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWM = WindowManagerGlobal.getWindowManagerService();
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
         mShowKeyguardWakeLock.setReferenceCounted(false);
@@ -1075,6 +1082,9 @@
                 case DISMISS:
                     handleDismiss();
                     break;
+                case START_KEYGUARD_EXIT_ANIM:
+                    handleStartKeyguardExitAnimation((Long) msg.obj);
+                    break;
             }
         }
     };
@@ -1206,6 +1216,19 @@
     private void handleHide() {
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleHide");
+            try {
+
+                // Don't actually hide the Keyguard at the moment, wait for window manager until
+                // it tells us it's safe to do so with startKeyguardExitAnimation.
+                mWM.keyguardGoingAway();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error while calling WindowManager", e);
+            }
+        }
+    }
+
+    private void handleStartKeyguardExitAnimation(long fadeoutDuration) {
+        synchronized (KeyguardViewMediator.this) {
 
             // only play "unlock" noises if not on a call (since the incall UI
             // disables the keyguard)
@@ -1316,12 +1339,18 @@
     }
 
     public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
-            ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
+            ViewGroup container, StatusBarWindowManager statusBarWindowManager,
+            ScrimController scrimController) {
         mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
-                statusBarWindowManager);
+                statusBarWindowManager, scrimController);
         return mStatusBarKeyguardViewManager;
     }
 
+    public void startKeyguardExitAnimation(long fadeoutDuration) {
+        Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM, fadeoutDuration);
+        mHandler.sendMessage(msg);
+    }
+
     public ViewMediatorCallback getViewMediatorCallback() {
         return mViewMediatorCallback;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bdac7a0..626fc0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -191,15 +191,23 @@
             final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
             record.tileView.measure(exactly(cw), exactly(ch));
         }
-        final int actualHeight = rows == 0 ? 0 : getRowTop(rows);
-        mDetail.measure(exactly(width), exactly(actualHeight));
-        setMeasuredDimension(width, actualHeight);
+        int h = rows == 0 ? 0 : getRowTop(rows);
+        mDetail.measure(exactly(width), unspecified());
+        if (mDetail.getVisibility() == VISIBLE && mDetail.getChildCount() > 0) {
+            final int dmh = mDetail.getMeasuredHeight();
+            if (dmh > 0) h = dmh;
+        }
+        setMeasuredDimension(width, h);
     }
 
     private static int exactly(int size) {
         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
     }
 
+    private static int unspecified() {
+        return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int w = getWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 835a5c4..c76ee8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.systemui.R;
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -35,6 +36,7 @@
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.TetheringController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
 
 import java.util.List;
 import java.util.Objects;
@@ -49,12 +51,12 @@
 public abstract class QSTile<TState extends State> implements Listenable {
     protected final String TAG = "QSTile." + getClass().getSimpleName();
     protected static final boolean DEBUG = false;
-    public static final int FEEDBACK_START_DELAY = 400;
 
     protected final Host mHost;
     protected final Context mContext;
     protected final H mHandler;
     protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
+    private final int mFeedbackStartDelay;
 
     private Callback mCallback;
     protected final TState mState = newTileState();
@@ -68,6 +70,7 @@
         mHost = host;
         mContext = host.getContext();
         mHandler = new H(host.getLooper());
+        mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
     }
 
     public boolean supportsDualTargets() {
@@ -116,6 +119,10 @@
         mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget();
     }
 
+    protected void postAfterFeedback(Runnable runnable) {
+        mHandler.postDelayed(runnable, mFeedbackStartDelay);
+    }
+
     // call only on tile worker looper
 
     private void handleSetCallback(Callback callback) {
@@ -213,6 +220,7 @@
         ZenModeController getZenModeController();
         TetheringController getTetheringController();
         CastController getCastController();
+        VolumeComponent getVolumeComponent();
     }
 
     public static class State {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 5eecc20..2edd8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -84,7 +84,8 @@
             removeView(mLabel);
         }
         final Resources res = mContext.getResources();
-        mLabel = new TextView(mDual ? new ContextThemeWrapper(mContext, R.style.QSBorderless_Tiny)
+        mLabel = new TextView(mDual
+                ? new ContextThemeWrapper(mContext, R.style.BorderlessButton_Tiny)
                 : mContext);
         mLabel.setId(android.R.id.title);
         mLabel.setTextColor(res.getColor(R.color.qs_tile_text));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
index fa41837..07ea825 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BugreportTile.java
@@ -58,13 +58,13 @@
 
     @Override
     protected void handleClick() {
-        mHandler.postDelayed(new Runnable() {
+        postAfterFeedback(new Runnable() {
             @Override
             public void run() {
                 mHost.collapsePanels();
                 mUiHandler.post(mShowDialog);
             }
-        }, FEEDBACK_START_DELAY);
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 907c77e..6793051 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -65,12 +65,12 @@
 
     @Override
     protected void handleClick() {
-        mHandler.postDelayed(new Runnable() {
+        postAfterFeedback(new Runnable() {
             public void run() {
                 mHost.collapsePanels();
                 mUiHandler.post(mShowDialog);
             }
-        }, FEEDBACK_START_DELAY);
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
new file mode 100644
index 0000000..20bbf8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -0,0 +1,168 @@
+/*
+ * 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.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.volume.VolumePanel;
+import com.android.systemui.volume.ZenModePanel;
+
+/** Quick settings tile: Notifications **/
+public class NotificationsTile extends QSTile<NotificationsTile.NotificationsState> {
+    private final ZenModeController mZenController;
+    private final AudioManager mAudioManager;
+
+    public NotificationsTile(Host host) {
+        super(host);
+        mZenController = host.getZenModeController();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+    }
+
+    @Override
+    public View createDetailView(Context context, ViewGroup root) {
+        final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
+        final View v = LayoutInflater.from(themedContext).inflate(R.layout.qs_detail, root, false);
+        final TextView title = (TextView) v.findViewById(android.R.id.title);
+        title.setText(R.string.quick_settings_notifications_label);
+        final View close = v.findViewById(android.R.id.button1);
+        close.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showDetail(false);
+            }
+        });
+        final ViewGroup content = (ViewGroup) v.findViewById(android.R.id.content);
+        final VolumeComponent volumeComponent = mHost.getVolumeComponent();
+        final VolumePanel vp = new VolumePanel(mContext, content, mZenController);
+        v.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                volumeComponent.setVolumePanel(null);
+            }
+
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                volumeComponent.setVolumePanel(vp);
+            }
+        });
+        vp.setZenModePanelCallback(new ZenModePanel.Callback() {
+            @Override
+            public void onMoreSettings() {
+                mHost.startSettingsActivity(ZenModePanel.ZEN_SETTINGS);
+            }
+
+            @Override
+            public void onInteraction() {
+                // noop
+            }
+        });
+        vp.postVolumeChanged(AudioManager.STREAM_RING, AudioManager.FLAG_SHOW_UI);
+        return v;
+    }
+
+    @Override
+    protected NotificationsState newTileState() {
+        return new NotificationsState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mZenController.addCallback(mCallback);
+            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+            mContext.registerReceiver(mReceiver, filter);
+        } else {
+            mZenController.removeCallback(mCallback);
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    @Override
+    protected void handleClick() {
+        showDetail(true);
+    }
+
+    @Override
+    protected void handleUpdateState(NotificationsState state, Object arg) {
+        state.visible = true;
+        state.zen = arg instanceof Boolean ? (Boolean) arg : mZenController.isZen();
+        state.ringerMode = mAudioManager.getRingerMode();
+        if (state.zen) {
+            state.iconId = R.drawable.ic_qs_zen_on;
+        } else if (state.ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            state.iconId = R.drawable.ic_qs_ringer_vibrate;
+        } else if (state.ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            state.iconId = R.drawable.ic_qs_ringer_silent;
+        } else {
+            state.iconId = R.drawable.ic_qs_ringer_audible;
+        }
+        state.label = mContext.getString(R.string.quick_settings_notifications_label);
+    }
+
+    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
+        @Override
+        public void onZenChanged(boolean zen) {
+            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
+            refreshState(zen);
+        }
+    };
+
+    public static final class NotificationsState extends QSTile.State {
+        public boolean zen;
+        public int ringerMode;
+
+        @Override
+        public boolean copyTo(State other) {
+            final NotificationsState o = (NotificationsState) other;
+            final boolean changed = o.zen != zen || o.ringerMode != ringerMode;
+            o.zen = zen;
+            o.ringerMode = ringerMode;
+            return super.copyTo(other) || changed;
+        }
+
+        @Override
+        protected StringBuilder toStringBuilder() {
+            final StringBuilder rt = super.toStringBuilder();
+            rt.insert(rt.length() - 1, ",zen=" + zen + ",ringerMode=" + ringerMode);
+            return rt;
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
+                refreshState();
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
deleted file mode 100644
index c5e9b52..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RingerModeTile.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-
-/** Quick settings tile: Ringer mode **/
-public class RingerModeTile extends QSTile<RingerModeTile.IntState> {
-
-    private final AudioManager mAudioManager;
-
-    public RingerModeTile(Host host) {
-        super(host);
-        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-    }
-
-    @Override
-    protected IntState newTileState() {
-        return new IntState();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
-            mContext.registerReceiver(mReceiver, filter);
-        } else {
-            mContext.unregisterReceiver(mReceiver);
-        }
-    }
-
-    @Override
-    protected void handleClick() {
-        final int oldValue = (Integer) mState.value;
-        final int newValue =
-                oldValue == AudioManager.RINGER_MODE_NORMAL ? AudioManager.RINGER_MODE_VIBRATE
-              : oldValue == AudioManager.RINGER_MODE_VIBRATE ? AudioManager.RINGER_MODE_SILENT
-              : AudioManager.RINGER_MODE_NORMAL;
-
-        mAudioManager.setRingerMode(newValue);
-    }
-
-    @Override
-    protected void handleUpdateState(IntState state, Object arg) {
-        final int ringerMode = mAudioManager.getRingerMode();
-        state.visible = true;
-        state.value = ringerMode;
-        if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            state.iconId = R.drawable.ic_qs_ringer_vibrate;
-            state.label = "Vibrate";
-        } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-            state.iconId = R.drawable.ic_qs_ringer_silent;
-            state.label = "Silent";
-        } else {
-            state.iconId = R.drawable.ic_qs_ringer_audible;
-            state.label = "Audible";
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
-                refreshState();
-            }
-        }
-    };
-
-    public static class IntState extends QSTile.State {
-        public int value;
-
-        @Override
-        public boolean copyTo(State other) {
-            final IntState o = (IntState) other;
-            final boolean changed = o.value != value;
-            o.value = value;
-            return super.copyTo(other) || changed;
-        }
-
-        @Override
-        protected StringBuilder toStringBuilder() {
-            final StringBuilder rt = super.toStringBuilder();
-            rt.insert(rt.length() - 1, ",value=" + value);
-            return rt;
-        }
-    }
-}
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/qs/tiles/ZenModeDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
deleted file mode 100644
index f30f791..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeDetail.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RadioButton;
-import android.widget.RelativeLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.util.HashSet;
-
-/** Quick settings control panel: Zen mode **/
-public class ZenModeDetail extends RelativeLayout {
-    private static final String TAG = "ZenModeDetail";
-    private static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
-    private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
-
-    private final H mHandler = new H();
-
-    private int mMinutesIndex = 3;
-    private Context mContext;
-    private ZenModeTile mTile;
-    private QSTile.Host mHost;
-    private ZenModeController mController;
-
-    private Switch mSwitch;
-    private ConditionAdapter mAdapter;
-
-    public ZenModeDetail(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public void init(ZenModeTile tile) {
-        mTile = tile;
-        mHost = mTile.getHost();
-        mContext = getContext();
-        mController = mHost.getZenModeController();
-
-        final ImageView close = (ImageView) findViewById(android.R.id.button1);
-        close.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mTile.showDetail(false);
-            }
-        });
-        mSwitch = (Switch) findViewById(android.R.id.checkbox);
-        mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                mController.setZen(isChecked);
-            }
-        });
-        mSwitch.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                final boolean isChecked = mSwitch.isChecked();
-                mController.setZen(isChecked);
-                if (!isChecked) {
-                    mTile.showDetail(false);
-                }
-            }
-        });
-
-        final View moreSettings = findViewById(android.R.id.button2);
-        moreSettings.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mHost.startSettingsActivity(ZEN_SETTINGS);
-                mTile.showDetail(false);
-            }
-        });
-        final ListView conditions = (ListView) findViewById(android.R.id.content);
-        mAdapter = new ConditionAdapter(mContext);
-        conditions.setAdapter(mAdapter);
-        mAdapter.add(updateTimeCondition());
-
-        updateZen(mController.isZen());
-    }
-
-    private Condition updateTimeCondition() {
-        final int minutes = MINUTES[mMinutesIndex];
-        final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
-        final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
-                .appendPath("countdown").appendPath(Long.toString(millis)).build();
-        final int num = minutes < 60 ? minutes : minutes / 60;
-        final String units = minutes < 60 ? "minutes" : minutes == 60 ? "hour" : "hours";
-        return new Condition(id, "For " + num + " " + units, "", "", 0, Condition.STATE_TRUE,
-                Condition.FLAG_RELEVANT_NOW);
-    }
-
-    private void editTimeCondition(int delta) {
-        final int i = mMinutesIndex + delta;
-        if (i < 0 || i >= MINUTES.length) return;
-        mMinutesIndex = i;
-        mAdapter.remove(mAdapter.getItem(0));
-        final Condition c = updateTimeCondition();
-        mAdapter.insert(c, 0);
-        select(c);
-    }
-
-    private void select(Condition condition) {
-        mController.select(condition);
-    }
-
-    private void updateZen(boolean zen) {
-        mHandler.obtainMessage(H.UPDATE_ZEN, zen ? 1 : 0, 0).sendToTarget();
-    }
-
-    private void updateConditions(Condition[] conditions) {
-        if (conditions == null) return;
-        mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
-    }
-
-    private void handleUpdateZen(boolean zen) {
-        mSwitch.setChecked(zen);
-    }
-
-    private void handleUpdateConditions(Condition[] conditions) {
-        for (int i = mAdapter.getCount() - 1; i > 0; i--) {
-            mAdapter.remove(mAdapter.getItem(i));
-        }
-        for (Condition condition : conditions) {
-            mAdapter.add(condition);
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mController.addCallback(mCallback);
-        mController.requestConditions(true);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mController.removeCallback(mCallback);
-        mController.requestConditions(false);
-    }
-
-    private final class H extends Handler {
-        private static final int UPDATE_ZEN = 1;
-        private static final int UPDATE_CONDITIONS = 2;
-
-        public H() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == UPDATE_ZEN) {
-                handleUpdateZen(msg.arg1 == 1);
-            } else if (msg.what == UPDATE_CONDITIONS) {
-                handleUpdateConditions((Condition[])msg.obj);
-            }
-        }
-    }
-
-    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenChanged(boolean zen) {
-            updateZen(zen);
-        }
-        public void onConditionsChanged(Condition[] conditions) {
-            updateConditions(conditions);
-        }
-    };
-
-    private final class ConditionAdapter extends ArrayAdapter<Condition> {
-        private final LayoutInflater mInflater;
-        private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
-
-        public ConditionAdapter(Context context) {
-            super(context, 0);
-            mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Condition condition = getItem(position);
-            final boolean enabled = condition.state == Condition.STATE_TRUE;
-
-            final View row = convertView != null ? convertView : mInflater
-                    .inflate(R.layout.qs_zen_mode_detail_condition, parent, false);
-            final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
-            mRadioButtons.add(rb);
-            rb.setEnabled(enabled);
-            rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-                @Override
-                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                    if (isChecked) {
-                        for (RadioButton otherButton : mRadioButtons) {
-                            if (otherButton == rb) continue;
-                            otherButton.setChecked(false);
-                        }
-                        select(condition);
-                    }
-                }
-            });
-            final TextView title = (TextView) row.findViewById(android.R.id.title);
-            title.setText(condition.summary);
-            title.setEnabled(enabled);
-            title.setAlpha(enabled ? 1 : .5f);
-            final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
-            button1.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    rb.setChecked(true);
-                    editTimeCondition(-1);
-                }
-            });
-
-            final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
-            button2.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    rb.setChecked(true);
-                    editTimeCondition(1);
-                }
-            });
-            title.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    rb.setChecked(true);
-                }
-            });
-            if (position != 0) {
-                button1.setVisibility(View.GONE);
-                button2.setVisibility(View.GONE);
-            }
-            if (position == 0 && mRadioButtons.size() == 1) {
-                rb.setChecked(true);
-            }
-            return row;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
deleted file mode 100644
index bfa9c19..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ZenModeTile.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-/** Quick settings tile: Zen mode **/
-public class ZenModeTile extends QSTile<QSTile.BooleanState> {
-    private final ZenModeController mController;
-
-    public ZenModeTile(Host host) {
-        super(host);
-        mController = host.getZenModeController();
-    }
-
-    @Override
-    public View createDetailView(Context context, ViewGroup root) {
-        final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
-        final ZenModeDetail v = (ZenModeDetail) LayoutInflater.from(themedContext)
-                .inflate(R.layout.qs_zen_mode_detail, root, false);
-        v.init(this);
-        return v;
-    }
-
-    @Override
-    protected BooleanState newTileState() {
-        return new BooleanState();
-    }
-
-    @Override
-    public void setListening(boolean listening) {
-        if (listening) {
-            mController.addCallback(mCallback);
-        } else {
-            mController.removeCallback(mCallback);
-        }
-    }
-
-    @Override
-    protected void handleClick() {
-        final boolean newZen = !mState.value;
-        mController.setZen(newZen);
-        if (newZen) {
-            showDetail(true);
-        }
-    }
-
-    @Override
-    protected void handleUpdateState(BooleanState state, Object arg) {
-        final boolean zen = arg instanceof Boolean ? (Boolean)arg : mController.isZen();
-        state.value = zen;
-        state.visible = true;
-        state.iconId = zen ? R.drawable.ic_qs_zen_on : R.drawable.ic_qs_zen_off;
-        state.label = mContext.getString(R.string.zen_mode_title);
-    }
-
-    private final ZenModeController.Callback mCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenChanged(boolean zen) {
-            if (DEBUG) Log.d(TAG, "onZenChanged " + zen);
-            refreshState(zen);
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index be1d47a..ca9bb94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -35,6 +35,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -49,7 +50,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /** A proxy implementation for the recents component */
-public class AlternateRecentsComponent {
+public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
 
     /** A handler for messages from the recents implementation */
     class RecentsMessageHandler extends Handler {
@@ -126,6 +127,7 @@
     final public static int MSG_SHOW_RECENTS = 4;
     final public static int MSG_HIDE_RECENTS = 5;
     final public static int MSG_TOGGLE_RECENTS = 6;
+    final public static int MSG_START_ENTER_ANIMATION = 7;
 
     final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail";
     final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab";
@@ -405,7 +407,7 @@
         }
 
         return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail,
-                taskRect.left, taskRect.top, null);
+                taskRect.left, taskRect.top, this);
     }
 
     /** Returns whether the recents is currently running */
@@ -529,4 +531,18 @@
             mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
         }
     }
+
+
+    /**** OnAnimationStartedListener Implementation ****/
+
+    @Override
+    public void onAnimationStarted() {
+        // Notify recents to start the enter animation
+        try {
+            Message msg = Message.obtain(null, MSG_START_ENTER_ANIMATION, 0, 0);
+            mService.send(msg);
+        } catch (RemoteException re) {
+            re.printStackTrace();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index e979afd..4db81bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -36,7 +36,7 @@
             // Enables the search bar layout
             public static final boolean EnableSearchLayout = true;
             // Enables the dynamic shadows behind each task
-            public static final boolean EnableShadows = false;
+            public static final boolean EnableShadows = true;
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
             // For debugging, this enables us to create mock recents tasks
@@ -113,4 +113,4 @@
             public static final int StackPeekNumCards = 3;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index befe8b4..df387c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -98,6 +98,9 @@
                     // If there are no filtered stacks, dismiss recents and launch the first task
                     dismissRecentsIfVisible();
                 }
+            } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
+                // Try and start the enter animation
+                mRecentsView.startOnEnterAnimation();
             }
         }
     };
@@ -345,6 +348,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
         filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
+        filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register the broadcast receiver to handle messages when the screen is turned off
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 03f7e36..1ae7a0d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -46,7 +46,9 @@
 
     public float animationPxMovementPerSecond;
 
-    public Interpolator defaultBezierInterpolator;
+    public Interpolator fastOutSlowInInterpolator;
+    public Interpolator fastOutLinearInInterpolator;
+    public Interpolator linearOutSlowInInterpolator;
 
     public int filteringCurrentViewsMinAnimDuration;
     public int filteringNewViewsMinAnimDuration;
@@ -59,6 +61,7 @@
     public int taskViewRemoveAnimTranslationXPx;
     public int taskViewTranslationZMinPx;
     public int taskViewTranslationZIncrementPx;
+    public int taskViewShadowOutlineBottomInsetPx;
     public int taskViewRoundedCornerRadiusPx;
     public int searchBarSpaceHeightPx;
 
@@ -130,6 +133,8 @@
         taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
         taskViewTranslationZIncrementPx =
                 res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
+        taskViewShadowOutlineBottomInsetPx =
+                res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
 
         taskBarViewDefaultBackgroundColor =
@@ -141,8 +146,12 @@
         taskBarViewDarkTextColor =
                 res.getColor(R.color.recents_task_bar_dark_text_color);
 
-        defaultBezierInterpolator = AnimationUtils.loadInterpolator(context,
+        fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                         com.android.internal.R.interpolator.fast_out_slow_in);
+        fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
+        linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.linear_out_slow_in);
 
         // Check if the developer options are enabled
         ContentResolver cr = context.getContentResolver();
@@ -167,6 +176,13 @@
                 appWidgetId).apply();
     }
 
+    /** Called when the configuration has changed, and we want to reset any configuration specific
+     * members. */
+    public void updateOnConfigurationChange() {
+        launchedFromAltTab = false;
+        launchedWithThumbnailAnimation = false;
+    }
+
     /** Returns whether the search bar app widget exists */
     public boolean hasSearchBarAppWidget() {
         return searchBarAppWidgetId >= 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index c4c910b..0c2c11d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -55,7 +55,8 @@
 
         if (msg.what == AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION) {
             RecentsTaskLoader.initialize(context);
-            RecentsConfiguration.reinitialize(context);
+            RecentsConfiguration config = RecentsConfiguration.reinitialize(context);
+            config.updateOnConfigurationChange();
 
             try {
                 Bundle data = msg.getData();
@@ -73,7 +74,6 @@
 
                 // Get the task stack and search bar bounds
                 Rect taskStackBounds = new Rect();
-                RecentsConfiguration config = RecentsConfiguration.getInstance();
                 config.getTaskStackBounds(windowRect.width(), windowRect.height(), taskStackBounds);
 
                 // Calculate the target task rect for when there is one task.
@@ -136,6 +136,11 @@
                     Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
             Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
                     Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+        } else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
+            // Send a broadcast to start the enter animation
+            Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
+            intent.setPackage(context.getPackageName());
+            context.sendBroadcast(intent);
         }
     }
 }
@@ -144,6 +149,7 @@
 public class RecentsService extends Service {
     final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
     final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+    final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
     final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
 
     Messenger mSystemUIMessenger = new Messenger(new SystemUIMessageHandler(this));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index cad9ce5..6005275 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -158,6 +158,18 @@
         return false;
     }
 
+    /** Requests all task stacks to start their enter-recents animation */
+    public void startOnEnterAnimation() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView) {
+                TaskStackView stackView = (TaskStackView) child;
+                stackView.startOnEnterAnimation();
+            }
+        }
+    }
+
     /** Adds the search bar */
     public void setSearchBar(View searchBar) {
         // Create the search bar (and hide it if we have no recent tasks)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 3ee0545..cae6bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -278,6 +278,7 @@
                 if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                     view.setAlpha(getAlphaForOffset(view));
                 }
+                mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
             }
         });
         anim.addListener(new AnimatorListenerAdapter() {
@@ -313,6 +314,7 @@
                 if (mCurrView != null) {
                     float delta = getPos(ev) - mInitialTouchPos;
                     setSwipeAmount(delta);
+                    mCallback.onSwipeChanged(mCurrView, delta);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -393,6 +395,8 @@
 
         void onBeginDrag(View v);
 
+        void onSwipeChanged(View v, float delta);
+
         void onChildDismissed(View v);
 
         void onSnapBackCompleted(View v);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 07caa1b..9e6c98e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.recents.views;
 
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
@@ -81,7 +80,7 @@
                     .alpha(toTransform.dismissAlpha)
                     .setStartDelay(0)
                     .setDuration(duration)
-                    .setInterpolator(config.defaultBezierInterpolator)
+                    .setInterpolator(config.fastOutSlowInInterpolator)
                     .withLayer()
                     .start();
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 75893aa..1fbaf87 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,7 +23,6 @@
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -88,6 +87,7 @@
     int mStackViewsAnimationDuration;
     boolean mStackViewsDirty = true;
     boolean mAwaitingFirstLayout = true;
+    boolean mStartEnterAnimationRequestedAfterLayout;
     int[] mTmpVisibleRange = new int[2];
     Rect mTmpRect = new Rect();
     Rect mTmpRect2 = new Rect();
@@ -353,7 +353,7 @@
         mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
         mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
                 curScroll, 250));
-        mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator);
+        mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().fastOutSlowInInterpolator);
         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -712,19 +712,6 @@
             setStackScrollToInitialState();
             requestSynchronizeStackViewsWithModel();
             synchronizeStackViewsWithModel();
-
-            // Update the focused task index to be the next item to the top task
-            if (config.launchedFromAltTab) {
-                focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
-            }
-
-            // Animate the task bar of the first task view
-            if (config.launchedWithThumbnailAnimation) {
-                TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
-                if (tv != null) {
-                    tv.animateOnEnterRecents();
-                }
-            }
         }
 
         // Measure each of the children
@@ -767,7 +754,47 @@
         }
 
         if (mAwaitingFirstLayout) {
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+
+            // Update the focused task index to be the next item to the top task
+            if (config.launchedFromAltTab) {
+                focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
+            }
+
+            // Prepare the first view for its enter animation
+            if (config.launchedWithThumbnailAnimation) {
+                TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+                if (tv != null) {
+                    tv.prepareAnimateOnEnterRecents();
+                }
+            }
+
+            // Mark that we have completely the first layout
             mAwaitingFirstLayout = false;
+
+            // If the enter animation started already and we haven't completed a layout yet, do the
+            // enter animation now
+            if (mStartEnterAnimationRequestedAfterLayout) {
+                startOnEnterAnimation();
+            }
+        }
+    }
+
+    /** Requests this task stacks to start it's enter-recents animation */
+    public void startOnEnterAnimation() {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        if (!config.launchedWithThumbnailAnimation) return;
+
+        // If we are still waiting to layout, then just defer until then
+        if (mAwaitingFirstLayout) {
+            mStartEnterAnimationRequestedAfterLayout = true;
+            return;
+        }
+
+        // Animate the task bar of the first task view
+        TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+        if (tv != null) {
+            tv.animateOnEnterRecents();
         }
     }
 
@@ -1538,6 +1565,11 @@
     }
 
     @Override
+    public void onSwipeChanged(View v, float delta) {
+        // Do nothing
+    }
+
+    @Override
     public void onChildDismissed(View v) {
         TaskView tv = (TaskView) v;
         mSv.onTaskDismissed(tv);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 8575661..ffa181d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Outline;
-import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -113,7 +112,8 @@
 
         // Update the outline
         Outline o = new Outline();
-        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), radius);
+        o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
+                config.taskViewShadowOutlineBottomInsetPx, radius);
         setOutline(o);
     }
 
@@ -167,7 +167,7 @@
                     .scaleY(toTransform.scale)
                     .alpha(toTransform.alpha)
                     .setDuration(duration)
-                    .setInterpolator(config.defaultBezierInterpolator)
+                    .setInterpolator(config.fastOutSlowInInterpolator)
                     .withLayer()
                     .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                         @Override
@@ -221,14 +221,21 @@
         fromTransform.alpha = 0f;
     }
 
+    /** Prepares this task view for the enter-recents animations.  This is called earlier in the
+     * first layout because the actual animation into recents may take a long time. */
+    public void prepareAnimateOnEnterRecents() {
+        mBarView.setVisibility(View.INVISIBLE);
+    }
+
     /** Animates this task view as it enters recents */
     public void animateOnEnterRecents() {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
-        mBarView.setAlpha(0f);
+        mBarView.setVisibility(View.VISIBLE);
+        mBarView.setTranslationY(-mBarView.getMeasuredHeight());
         mBarView.animate()
-                .alpha(1f)
-                .setStartDelay(300)
-                .setInterpolator(config.defaultBezierInterpolator)
+                .translationY(0)
+                .setStartDelay(200)
+                .setInterpolator(config.fastOutSlowInInterpolator)
                 .setDuration(config.taskBarEnterAnimDuration)
                 .withLayer()
                 .start();
@@ -238,9 +245,9 @@
     public void animateOnLeavingRecents(final Runnable r) {
         RecentsConfiguration config = RecentsConfiguration.getInstance();
         mBarView.animate()
-            .alpha(0f)
+            .translationY(-mBarView.getMeasuredHeight())
             .setStartDelay(0)
-            .setInterpolator(config.defaultBezierInterpolator)
+            .setInterpolator(config.fastOutLinearInInterpolator)
             .setDuration(config.taskBarExitAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
@@ -261,7 +268,7 @@
         animate().translationX(config.taskViewRemoveAnimTranslationXPx)
             .alpha(0f)
             .setStartDelay(0)
-            .setInterpolator(config.defaultBezierInterpolator)
+            .setInterpolator(config.fastOutSlowInInterpolator)
             .setDuration(config.taskViewRemoveAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 27881c4..65e1cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -72,8 +72,7 @@
         window.setGravity(Gravity.TOP);
         WindowManager.LayoutParams lp = window.getAttributes();
         // Offset from the top
-        lp.y = getContext().getResources().getDimensionPixelOffset(
-                com.android.internal.R.dimen.volume_panel_top);
+        lp.y = getContext().getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
         lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
         lp.privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index e3dac4a..dcd187c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -149,6 +149,7 @@
         super.onFinishInflate();
         mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
         mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
+        updateBackground();
         updateBackgroundResources();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
new file mode 100644
index 0000000..06dc4e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * An ImageView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaImageView extends ImageView {
+    public AlphaImageView(Context context) {
+        super(context);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 9435e85..21b41c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -48,6 +48,7 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
@@ -77,19 +78,22 @@
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SearchPanelView;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 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";
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
-    private static final boolean USE_NOTIFICATION_LISTENER = false;
+    private static final boolean USE_NOTIFICATION_LISTENER = true;
 
     protected static final int MSG_SHOW_RECENT_APPS = 1019;
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -178,6 +182,7 @@
      */
     protected int mState;
     protected boolean mBouncerShowing;
+    protected boolean mShowLockscreenNotifications;
 
     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
 
@@ -192,11 +197,14 @@
                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
             if (provisioned != mDeviceProvisioned) {
                 mDeviceProvisioned = provisioned;
-                updateNotificationIcons();
+                updateNotifications();
             }
             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);
         }
     };
 
@@ -207,39 +215,53 @@
             // so we just dump our cache ...
             mUsersAllowingPrivateNotifications.clear();
             // ... and refresh all the notifications
-            updateNotificationIcons();
+            updateNotifications();
         }
     };
 
     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);
         }
     };
 
@@ -264,11 +286,12 @@
         public void onListenerConnected() {
             if (DEBUG) Log.d(TAG, "onListenerConnected");
             final StatusBarNotification[] notifications = getActiveNotifications();
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     for (StatusBarNotification sbn : notifications) {
-                        addNotificationInternal(sbn);
+                        addNotificationInternal(sbn, currentRanking);
                     }
                 }
             });
@@ -277,13 +300,14 @@
         @Override
         public void onNotificationPosted(final StatusBarNotification sbn) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     if (mNotificationData.findByKey(sbn.getKey()) != null) {
-                        updateNotificationInternal(sbn);
+                        updateNotificationInternal(sbn, currentRanking);
                     } else {
-                        addNotificationInternal(sbn);
+                        addNotificationInternal(sbn, currentRanking);
                     }
                 }
             });
@@ -292,10 +316,24 @@
         @Override
         public void onNotificationRemoved(final StatusBarNotification sbn) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    removeNotificationInternal(sbn.getKey());
+                    removeNotificationInternal(sbn.getKey(), currentRanking);
+                }
+            });
+        }
+
+        @Override
+        public void onNotificationRankingUpdate() {
+            if (DEBUG) Log.d(TAG, "onRankingUpdate");
+            final Ranking currentRanking = getCurrentRanking();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mNotificationData.updateRanking(currentRanking);
+                    updateNotifications();
                 }
             });
         }
@@ -328,6 +366,9 @@
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
                 mSettingsObserver);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+                mSettingsObserver);
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -434,6 +475,14 @@
         }
     }
 
+    /**
+     * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
+     * @param action A dismiss action that is called if it's safe to start the activity.
+     */
+    protected void startNotificationActivity(OnDismissAction action) {
+        action.onDismiss();
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         final Locale locale = mContext.getResources().getConfiguration().locale;
@@ -999,47 +1048,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);
+            });
         }
     }
 
@@ -1081,19 +1138,13 @@
         }
     }
 
-    protected StatusBarNotification removeNotificationViews(String key) {
-        NotificationData.Entry entry = mNotificationData.remove(key);
+    protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
+        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
         if (entry == null) {
             Log.w(TAG, "removeNotification for unknown key: " + key);
             return null;
         }
-        // Remove the expanded view.
-        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
-        if (rowParent != null) rowParent.removeView(entry.row);
-        updateRowStates();
-        updateNotificationIcons();
-        updateSpeedBump();
-
+        updateNotifications();
         return entry.notification;
     }
 
@@ -1127,35 +1178,17 @@
         return entry;
     }
 
-    protected void addNotificationViews(NotificationData.Entry entry) {
+    protected void addNotificationViews(Entry entry, Ranking ranking) {
         if (entry == null) {
             return;
         }
         // Add the expanded view and icon.
-        int pos = mNotificationData.add(entry);
-        if (DEBUG) {
-            Log.d(TAG, "addNotificationViews: added at " + pos);
-        }
-        updateRowStates();
-        updateNotificationIcons();
-        updateSpeedBump();
+        mNotificationData.add(entry, ranking);
+        updateNotifications();
     }
 
-    protected void updateSpeedBump() {
-        int n = mNotificationData.size();
-        int speedBumpIndex = -1;
-        for (int i = n-1; i >= 0; i--) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
-                    && entry.row.isBelowSpeedBump() ) {
-                speedBumpIndex = n - 1 - i;
-            }
-        }
-        mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
-    }
-
-    private void addNotificationViews(StatusBarNotification notification) {
-        addNotificationViews(createNotificationViews(notification));
+    private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
+        addNotificationViews(createNotificationViews(notification), ranking);
     }
 
     /**
@@ -1169,17 +1202,17 @@
     protected void updateRowStates() {
         int maxKeyguardNotifications = getMaxKeyguardNotifications();
         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
-        int n = mNotificationData.size();
+        final int N = mNotificationData.size();
         int visibleNotifications = 0;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
-        for (int i = n-1; i >= 0; i--) {
+        for (int i = 0; i < N; i++) {
             NotificationData.Entry entry = mNotificationData.get(i);
             if (onKeyguard) {
                 entry.row.setExpansionDisabled(true);
             } else {
                 entry.row.setExpansionDisabled(false);
                 if (!entry.row.isUserLocked()) {
-                    boolean top = (i == n-1);
+                    boolean top = (i == 0);
                     entry.row.setSystemExpanded(top);
                 }
             }
@@ -1206,6 +1239,9 @@
         } else {
             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
         }
+        // Move overflow container to last position.
+        mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
+                mStackScroller.getChildCount() - 1);
     }
 
     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1215,46 +1251,46 @@
     protected void setZenMode(int mode) {
         if (!isDeviceProvisioned()) return;
         mZenMode = mode;
-        updateNotificationIcons();
+        updateNotifications();
+    }
+
+    protected void setShowLockscreenNotifications(boolean show) {
+        mShowLockscreenNotifications = show;
     }
 
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
-    protected abstract void updateNotificationIcons();
+    protected abstract void updateNotifications();
     protected abstract void tick(StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
     protected abstract boolean shouldDisableNavbarGestures();
 
-    protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
-        return parent != null && parent.indexOfChild(entry.row) == 0;
-    }
-
-
     @Override
     public void addNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
-            addNotificationInternal(notification);
+            addNotificationInternal(notification, null);
         }
     }
 
-    public abstract void addNotificationInternal(StatusBarNotification notification);
+    public abstract void addNotificationInternal(StatusBarNotification notification,
+            Ranking ranking);
 
     @Override
     public void removeNotification(String key) {
         if (!USE_NOTIFICATION_LISTENER) {
-            removeNotificationInternal(key);
+            removeNotificationInternal(key, null);
         }
     }
 
-    protected abstract void removeNotificationInternal(String key);
+    protected abstract void removeNotificationInternal(String key, Ranking ranking);
 
     public void updateNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
-            updateNotificationInternal(notification);
+            updateNotificationInternal(notification, null);
         }
     }
 
-    public void updateNotificationInternal(StatusBarNotification notification) {
+    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1326,18 +1362,12 @@
                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
 
-        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
-        boolean orderUnchanged =
-                   notification.getNotification().when == oldNotification.getNotification().when
-                && notification.getScore() == oldNotification.getScore();
-        // score now encompasses/supersedes isOngoing()
 
         boolean updateTicker = notification.getNotification().tickerText != null
                 && !TextUtils.equals(notification.getNotification().tickerText,
                 oldEntry.notification.getNotification().tickerText);
-        boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
-        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
-                && (orderUnchanged || isTopAnyway)) {
+        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
+                && publicUnchanged) {
             if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
             oldEntry.notification = notification;
             try {
@@ -1365,22 +1395,20 @@
                     handleNotificationError(notification, "Couldn't update icon: " + ic);
                     return;
                 }
-                updateRowStates();
-                updateSpeedBump();
+                mNotificationData.updateRanking(ranking);
+                updateNotifications();
             }
             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(notification.getKey());
-                addNotificationViews(notification);
+                removeNotificationViews(notification.getKey(), ranking);
+                addNotificationViews(notification, ranking);
             }
         } else {
             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(notification.getKey());
-            addNotificationViews(notification);  // will also replace the heads up
+            removeNotificationViews(notification.getKey(), ranking);
+            addNotificationViews(notification, ranking);  // will also replace the heads up
             final NotificationData.Entry newEntry = mNotificationData.findByKey(
                     notification.getKey());
             final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
@@ -1527,5 +1555,12 @@
             mWindowManager.removeViewImmediate(mSearchPanelView);
         }
         mContext.unregisterReceiver(mBroadcastReceiver);
+        if (USE_NOTIFICATION_LISTENER) {
+            try {
+                mNotificationListener.unregisterAsSystemService();
+            } catch (RemoteException e) {
+                // Ignore.
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 7d576cb..5f1325b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -18,6 +18,7 @@
 
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.view.ViewPropertyAnimator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
@@ -46,6 +47,8 @@
     private float mMaxLengthSeconds;
     private float mHighVelocityPxPerSecond;
 
+    private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
+
     public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
         mMaxLengthSeconds = maxLengthSeconds;
         mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
@@ -80,18 +83,59 @@
      * @param currValue the current value
      * @param endValue the end value of the animator
      * @param velocity the current velocity of the motion
+     */
+    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity) {
+        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
     public void apply(ValueAnimator animator, float currValue, float endValue, float velocity,
             float maxDistance) {
+        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     * @param maxDistance the maximum distance for this interaction; the maximum animation length
+     *                    gets multiplied by the ratio between the actual distance and this value
+     */
+    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity, float maxDistance) {
+        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    private AnimatorProperties getProperties(float currValue,
+            float endValue, float velocity, float maxDistance) {
         float maxLengthSeconds = (float) (mMaxLengthSeconds
                 * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
         float diff = Math.abs(endValue - currValue);
         float velAbs = Math.abs(velocity);
         float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            animator.setInterpolator(mLinearOutSlowIn);
+            mAnimatorProperties.interpolator = mLinearOutSlowIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
@@ -100,14 +144,15 @@
                     = new VelocityInterpolator(durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
-            animator.setInterpolator(superInterpolator);
+            mAnimatorProperties.interpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            animator.setInterpolator(mFastOutSlowIn);
+            mAnimatorProperties.interpolator = mFastOutSlowIn;
         }
-        animator.setDuration((long) (durationSeconds * 1000));
+        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        return mAnimatorProperties;
     }
 
     /**
@@ -124,6 +169,34 @@
      */
     public void applyDismissing(ValueAnimator animator, float currValue, float endValue,
             float velocity, float maxDistance) {
+        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion for the case when the animation is making something
+     * disappear.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     * @param maxDistance the maximum distance for this interaction; the maximum animation length
+     *                    gets multiplied by the ratio between the actual distance and this value
+     */
+    public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity, float maxDistance) {
+        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    private AnimatorProperties getDismissingProperties(float currValue, float endValue,
+            float velocity, float maxDistance) {
         float maxLengthSeconds = (float) (mMaxLengthSeconds
                 * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
         float diff = Math.abs(endValue - currValue);
@@ -135,7 +208,7 @@
         Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, 1, y2);
         float durationSeconds = startGradient * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            animator.setInterpolator(mLinearOutFasterIn);
+            mAnimatorProperties.interpolator = mLinearOutFasterIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between linear-out-faster-in and linear interpolator with current
@@ -145,14 +218,15 @@
                     = new VelocityInterpolator(durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutFasterIn, mLinearOutSlowIn);
-            animator.setInterpolator(superInterpolator);
+            mAnimatorProperties.interpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            animator.setInterpolator(mFastOutLinearIn);
+            mAnimatorProperties.interpolator = mFastOutLinearIn;
         }
-        animator.setDuration((long) (durationSeconds * 1000));
+        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        return mAnimatorProperties;
     }
 
     /**
@@ -221,4 +295,10 @@
             return time * mVelocity / mDiff;
         }
     }
+
+    private static class AnimatorProperties {
+        Interpolator interpolator;
+        long duration;
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 0555879..24da5c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -31,7 +31,6 @@
 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;
@@ -50,7 +49,7 @@
         for (int i = 0; i < n; i++) {
             final StatusBarNotification sbn = mIntercepted.valueAt(i);
             sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
-            mBar.addNotificationInternal(sbn);
+            mBar.addNotificationInternal(sbn, null);
         }
         mIntercepted.clear();
         updateSyntheticNotification();
@@ -71,7 +70,7 @@
     }
 
     public boolean isSyntheticEntry(Entry ent) {
-        return ent.key.equals(SYNTHETIC_KEY);
+        return ent.key.equals(mSynKey);
     }
 
     public void update(StatusBarNotification notification) {
@@ -88,7 +87,7 @@
     private void updateSyntheticNotification() {
         if (mIntercepted.isEmpty()) {
             if (mSynKey != null) {
-                mBar.removeNotificationInternal(mSynKey);
+                mBar.removeNotificationInternal(mSynKey, null);
                 mSynKey = null;
             }
             return;
@@ -107,9 +106,9 @@
                 mBar.getCurrentUserHandle());
         if (mSynKey == null) {
             mSynKey = sbn.getKey();
-            mBar.addNotificationInternal(sbn);
+            mBar.addNotificationInternal(sbn, null);
         } else {
-           mBar.updateNotificationInternal(sbn);
+           mBar.updateNotificationInternal(sbn, null);
         }
         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 5696246..d829ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,15 +16,19 @@
 
 package com.android.systemui.statusbar;
 
+import android.app.Notification;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
-import android.widget.ImageView;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 
 /**
  * The list of currently displaying notifications.
+ *
+ * TODO: Rename to NotificationList.
  */
 public class NotificationData {
     public static final class Entry {
@@ -34,7 +38,6 @@
         public ExpandableNotificationRow row; // the outer expanded view
         public View expanded; // the inflated RemoteViews
         public View expandedPublic; // for insecure lockscreens
-        public ImageView largeIcon;
         public View expandedBig;
         private boolean interruption;
         public Entry() {}
@@ -64,18 +67,23 @@
     }
 
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
-    private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
-        // sort first by score, then by when
+    private Ranking mRanking;
+    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
+        @Override
         public int compare(Entry a, Entry b) {
+            if (mRanking != null) {
+                return mRanking.getRank(a.key) - mRanking.getRank(b.key);
+            }
+
             final StatusBarNotification na = a.notification;
             final StatusBarNotification nb = b.notification;
-            int d = na.getScore() - nb.getScore();
+            int d = nb.getScore() - na.getScore();
             if (a.interruption != b.interruption) {
-                return a.interruption ? 1 : -1;
+                return a.interruption ? -1 : 1;
             } else if (d != 0) {
                 return d;
             } else {
-                return (int) (na.getNotification().when - nb.getNotification().when);
+                return (int) (nb.getNotification().when - na.getNotification().when);
             }
         }
     };
@@ -97,26 +105,47 @@
         return null;
     }
 
-    public int add(Entry entry) {
-        int i;
-        int N = mEntries.size();
-        for (i = 0; i < N; i++) {
-            if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
-                break;
-            }
-        }
-        mEntries.add(i, entry);
-        return i;
+    public void add(Entry entry, Ranking ranking) {
+        mEntries.add(entry);
+        updateRankingAndSort(ranking);
     }
 
-    public Entry remove(String key) {
+    public Entry remove(String key, Ranking ranking) {
         Entry e = findByKey(key);
-        if (e != null) {
-            mEntries.remove(e);
+        if (e == null) {
+            return null;
         }
+        mEntries.remove(e);
+        updateRankingAndSort(ranking);
         return e;
     }
 
+    public void updateRanking(Ranking ranking) {
+        updateRankingAndSort(ranking);
+    }
+
+    public boolean isAmbient(String key) {
+        // TODO: Remove when switching to NotificationListener.
+        if (mRanking == null) {
+            for (Entry entry : mEntries) {
+                if (key.equals(entry.key)) {
+                    return entry.notification.getNotification().priority ==
+                            Notification.PRIORITY_MIN;
+                }
+            }
+        } else {
+            return mRanking.isAmbient(key);
+        }
+        return false;
+    }
+
+    private void updateRankingAndSort(Ranking ranking) {
+        if (ranking != null) {
+            mRanking = ranking;
+        }
+        Collections.sort(mEntries, mRankingComparator);
+    }
+
     /**
      * Return whether there are any visible items (i.e. items without an error).
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index a2f8991..a84daef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -103,7 +103,11 @@
 
     @Override
     public int getIntrinsicHeight() {
-        return getActualHeight();
+        if (mCurrentAnimator != null) {
+            // expand animation is running
+            return getActualHeight();
+        }
+        return mIsExpanded ? getHeight() : mCollapsedHeight;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 5e9ce21..994b329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -23,46 +23,34 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
 
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
  * text.
  */
-public class KeyguardBottomAreaView extends FrameLayout
-        implements SwipeAffordanceView.AffordanceListener {
+public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
+        UnlockMethodCache.OnUnlockMethodChangedListener {
 
     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
 
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
 
-    private SwipeAffordanceView mCameraButton;
-    private SwipeAffordanceView mPhoneButton;
+    private ImageView mCameraImageView;
+    private ImageView mPhoneImageView;
     private ImageView mLockIcon;
 
-    private PowerManager mPowerManager;
     private ActivityStarter mActivityStarter;
-
-    private LockPatternUtils mLockPatternUtils;
-    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private UnlockMethodCache mUnlockMethodCache;
 
     public KeyguardBottomAreaView(Context context) {
         super(context);
@@ -84,20 +72,16 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mCameraButton = (SwipeAffordanceView) findViewById(R.id.camera_button);
-        mPhoneButton = (SwipeAffordanceView) findViewById(R.id.phone_button);
+        mCameraImageView = (ImageView) findViewById(R.id.camera_button);
+        mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
         mLockIcon = (ImageView) findViewById(R.id.lock_icon);
-        mCameraButton.setAffordanceListener(this);
-        mPhoneButton.setAffordanceListener(this);
-        mLockPatternUtils = new LockPatternUtils(getContext());
-        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
-        KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mCallback);
         watchForDevicePolicyChanges();
         watchForAccessibilityChanges();
         updateCameraVisibility();
         updatePhoneVisibility();
+        mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
+        mUnlockMethodCache.addListener(this);
         updateTrust();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
     }
 
     public void setActivityStarter(ActivityStarter activityStarter) {
@@ -106,12 +90,12 @@
 
     private void updateCameraVisibility() {
         boolean visible = !isCameraDisabledByDpm();
-        mCameraButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private void updatePhoneVisibility() {
         boolean visible = isPhoneVisible();
-        mPhoneButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private boolean isPhoneVisible() {
@@ -171,33 +155,31 @@
     }
 
     private void enableAccessibility(boolean touchExplorationEnabled) {
-        mCameraButton.enableAccessibility(touchExplorationEnabled);
-        mPhoneButton.enableAccessibility(touchExplorationEnabled);
+        mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+        mCameraImageView.setClickable(touchExplorationEnabled);
+        mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+        mPhoneImageView.setClickable(touchExplorationEnabled);
     }
 
-    private void launchCamera() {
+    @Override
+    public void onClick(View v) {
+        if (v == mCameraImageView) {
+            launchCamera();
+        } else if (v == mPhoneImageView) {
+            launchPhone();
+        }
+    }
+
+    public void launchCamera() {
         mContext.startActivityAsUser(
                 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
                 UserHandle.CURRENT);
     }
 
-    private void launchPhone() {
+    public void launchPhone() {
         mActivityStarter.startActivity(PHONE_INTENT);
     }
 
-    @Override
-    public void onUserActivity(long when) {
-        mPowerManager.userActivity(when, false);
-    }
-
-    @Override
-    public void onActionPerformed(SwipeAffordanceView view) {
-        if (view == mCameraButton) {
-            launchCamera();
-        } else if (view == mPhoneButton) {
-            launchPhone();
-        }
-    }
 
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
@@ -211,28 +193,26 @@
         if (getVisibility() != VISIBLE) {
             return;
         }
-        int user = mLockPatternUtils.getCurrentUser();
-        boolean trust = !mLockPatternUtils.isSecure() ||
-                mKeyguardUpdateMonitor.getUserHasTrust(user);
-
-        int iconRes = trust ? R.drawable.ic_lock_open_24dp : R.drawable.ic_lock_24dp;
+        int iconRes = mUnlockMethodCache.isMethodInsecure()
+                ? R.drawable.ic_lock_open_24dp
+                : R.drawable.ic_lock_24dp;
         mLockIcon.setImageResource(iconRes);
     }
 
-    final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
-        @Override
-        public void onScreenTurnedOn() {
-            updateTrust();
-        }
+    public ImageView getPhoneImageView() {
+        return mPhoneImageView;
+    }
 
-        @Override
-        public void onUserSwitchComplete(int userId) {
-            updateTrust();
-        }
+    public ImageView getCameraImageView() {
+        return mCameraImageView;
+    }
 
-        @Override
-        public void onTrustChanged(int userId) {
-            updateTrust();
-        }
-    };
+    public ImageView getLockIcon() {
+        return mLockIcon;
+    }
+
+    @Override
+    public void onMethodSecureChanged(boolean methodSecure) {
+        updateTrust();
+    }
 }
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..b7a7b0a 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.*;
 
 /**
@@ -53,27 +54,36 @@
         mWindowManager = windowManager;
     }
 
-    public void prepare() {
-        ensureView();
-    }
-
     public void show() {
         ensureView();
+        if (mRoot.getVisibility() == View.VISIBLE) {
+            return;
+        }
 
         // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole
         // Keyguard. If we need to authenticate, show the bouncer.
         if (!mKeyguardView.dismiss()) {
             mRoot.setVisibility(View.VISIBLE);
-            mKeyguardView.requestFocus();
             mKeyguardView.onResume();
+            mKeyguardView.startAppearAnimation();
         }
     }
 
-    public void hide() {
+    public void showWithDismissAction(OnDismissAction r) {
+        ensureView();
+        mKeyguardView.setOnDismissAction(r);
+        show();
+    }
+
+    public void hide(boolean destroyView) {
         if (mKeyguardView != null) {
             mKeyguardView.cleanUp();
         }
-        removeView();
+        if (destroyView) {
+            removeView();
+        } else if (mRoot != null) {
+            mRoot.setVisibility(View.INVISIBLE);
+        }
     }
 
     /**
@@ -103,6 +113,10 @@
         return mRoot != null && mRoot.getVisibility() == View.VISIBLE;
     }
 
+    public void prepare() {
+        ensureView();
+    }
+
     private void ensureView() {
         if (mRoot == null) {
             inflateView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 769b1b1..c5c3fff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.TextView;
 
 /**
@@ -50,7 +52,12 @@
     public void switchIndication(CharSequence text) {
 
         // TODO: Animation, make sure that we will show one indication long enough.
-        setText(text);
+        if (TextUtils.isEmpty(text)) {
+            setVisibility(View.INVISIBLE);
+        } else {
+            setVisibility(View.VISIBLE);
+            setText(text);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
new file mode 100644
index 0000000..b4f4865
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -0,0 +1,398 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.PowerManager;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A touch handler of the Keyguard which is responsible for swiping the content left or right.
+ */
+public class KeyguardPageSwipeHelper {
+
+    private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
+    private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
+    private final Context mContext;
+
+    private FlingAnimationUtils mFlingAnimationUtils;
+    private Callback mCallback;
+    private int mTrackingPointer;
+    private VelocityTracker mVelocityTracker;
+    private boolean mSwipingInProgress;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private float mTranslation;
+    private float mTranslationOnDown;
+    private int mTouchSlop;
+    private int mMinTranslationAmount;
+    private int mMinFlingVelocity;
+    private PowerManager mPowerManager;
+    private final View mLeftIcon;
+    private final View mCenterIcon;
+    private final View mRightIcon;
+    private Interpolator mFastOutSlowIn;
+    private Animator mSwipeAnimator;
+    private boolean mCallbackCalled;
+
+    KeyguardPageSwipeHelper(Callback callback, Context context) {
+        mContext = context;
+        mCallback = callback;
+        mLeftIcon = mCallback.getLeftIcon();
+        mCenterIcon = mCallback.getCenterIcon();
+        mRightIcon = mCallback.getRightIcon();
+        updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        initDimens();
+    }
+
+    private void initDimens() {
+        final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
+                R.dimen.keyguard_min_swipe_amount);
+        mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
+        mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.fast_out_slow_in);
+    }
+
+    public boolean onTouchEvent(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                if (mSwipingInProgress) {
+                    cancelAnimations();
+                }
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                mTranslationOnDown = mTranslation;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                    mTranslationOnDown = mTranslation;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float w = x - mInitialTouchX;
+                trackMovement(event);
+                if (((leftSwipePossible() && w > mTouchSlop)
+                        || (rightSwipePossible() && w < -mTouchSlop))
+                        && Math.abs(w) > Math.abs(y - mInitialTouchY)
+                        && !mSwipingInProgress) {
+                    cancelAnimations();
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mTranslationOnDown = mTranslation;
+                    mSwipingInProgress = true;
+                }
+                if (mSwipingInProgress) {
+                    setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
+                    onUserActivity(event.getEventTime());
+                }
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mTrackingPointer = -1;
+                trackMovement(event);
+                if (mSwipingInProgress) {
+                    flingWithCurrentVelocity();
+                }
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
+    }
+
+    private boolean rightSwipePossible() {
+        return mRightIcon.getVisibility() == View.VISIBLE;
+    }
+
+    private boolean leftSwipePossible() {
+        return mLeftIcon.getVisibility() == View.VISIBLE;
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return false;
+    }
+
+    private void onUserActivity(long when) {
+        mPowerManager.userActivity(when, false);
+    }
+
+    private void cancelAnimations() {
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View target : targetViews) {
+            target.animate().cancel();
+        }
+        View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+        targetView.animate().cancel();
+        if (mSwipeAnimator != null) {
+            mSwipeAnimator.removeAllListeners();
+            mSwipeAnimator.cancel();
+            hideInactiveIcons(true);
+        }
+    }
+
+    private void flingWithCurrentVelocity() {
+        float vel = getCurrentVelocity();
+
+        // We snap back if the current translation is not far enough
+        boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
+
+        // or if the velocity is in the opposite direction.
+        boolean velIsInWrongDirection = vel * mTranslation < 0;
+        snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
+        vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+        fling(vel, snapBack);
+    }
+
+    private void fling(float vel, final boolean snapBack) {
+        float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
+        target = snapBack ? 0 : target;
+
+        // translation Animation
+        startTranslationAnimations(vel, target);
+
+        // animate left / right icon
+        startIconAnimation(vel, snapBack, target);
+
+        ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+        mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mTranslation = (float) animation.getAnimatedValue();
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mSwipeAnimator = null;
+                mSwipingInProgress = false;
+                if (!snapBack && !mCallbackCalled) {
+
+                    // ensure that the callback is called eventually
+                    mCallback.onAnimationToSideStarted(mTranslation < 0);
+                    mCallbackCalled = true;
+                }
+            }
+        });
+        if (!snapBack) {
+            mCallbackCalled = false;
+            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                int frameNumber;
+
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    if (frameNumber == 2 && !mCallbackCalled) {
+
+                        // we have to wait for the second frame for this call,
+                        // until the render thread has definitely kicked in, to avoid a lag.
+                        mCallback.onAnimationToSideStarted(mTranslation < 0);
+                        mCallbackCalled = true;
+                    }
+                    frameNumber++;
+                }
+            });
+        } else {
+            showAllIcons(true);
+        }
+        animator.start();
+        mSwipeAnimator = animator;
+    }
+
+    private void startTranslationAnimations(float vel, float target) {
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View targetView : targetViews) {
+            ViewPropertyAnimator animator = targetView.animate();
+            mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+            animator.translationX(target);
+        }
+    }
+
+    private void startIconAnimation(float vel, boolean snapBack, float target) {
+        float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
+        float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
+        View targetView = mTranslation > 0
+                ? mLeftIcon
+                : mRightIcon;
+        if (targetView.getVisibility() == View.VISIBLE) {
+            ViewPropertyAnimator iconAnimator = targetView.animate();
+            mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
+            iconAnimator.scaleX(scale);
+            iconAnimator.scaleY(scale);
+            iconAnimator.alpha(alpha);
+        }
+    }
+
+    private void setTranslation(float translation, boolean isReset) {
+        translation = rightSwipePossible() ? translation : Math.max(0, translation);
+        translation = leftSwipePossible() ? translation : Math.min(0, translation);
+        if (translation != mTranslation) {
+            ArrayList<View> translatedViews = mCallback.getTranslationViews();
+            for (View view : translatedViews) {
+                view.setTranslationX(translation);
+            }
+            if (translation == 0.0f) {
+                boolean animate = !isReset;
+                showAllIcons(animate);
+            } else {
+                View targetView = translation > 0 ? mLeftIcon : mRightIcon;
+                float progress = Math.abs(translation) / mCallback.getPageWidth();
+                progress = Math.min(progress, 1.0f);
+                float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
+                float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
+                updateIcon(targetView, scale, alpha, false);
+                View otherView = translation < 0 ? mLeftIcon : mRightIcon;
+                if (mTranslation * translation <= 0) {
+                    // The sign of the translation has changed so we need to hide the other icons
+                    updateIcon(otherView, 0, 0, true);
+                    updateIcon(mCenterIcon, 0, 0, true);
+                }
+            }
+            mTranslation = translation;
+        }
+    }
+
+    private void showAllIcons(boolean animate) {
+        float scale = 1.0f;
+        float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
+        updateIcon(mRightIcon, scale, alpha, animate);
+        updateIcon(mCenterIcon, scale, alpha, animate);
+        updateIcon(mLeftIcon, scale, alpha, animate);
+    }
+
+    private void hideInactiveIcons(boolean animate){
+        View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
+        updateIcon(otherView, 0, 0, animate);
+        updateIcon(mCenterIcon, 0, 0, animate);
+    }
+
+    private void updateIcon(View view, float scale, float alpha, boolean animate) {
+        if (view.getVisibility() != View.VISIBLE) {
+            return;
+        }
+        if (!animate) {
+            view.setAlpha(alpha);
+            view.setScaleX(scale);
+            view.setScaleY(scale);
+            // TODO: remove this invalidate once the property setters invalidate it properly
+            view.invalidate();
+        } else {
+            if (view.getAlpha() != alpha || view.getScaleX() != scale) {
+                view.animate()
+                        .setInterpolator(mFastOutSlowIn)
+                        .alpha(alpha)
+                        .scaleX(scale)
+                        .scaleY(scale);
+            }
+        }
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.addMovement(event);
+        }
+    }
+
+    private void initVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentVelocity() {
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getXVelocity();
+    }
+
+    public void onConfigurationChanged() {
+        initDimens();
+    }
+
+    public void reset() {
+        setTranslation(0.0f, true);
+        mSwipingInProgress = false;
+    }
+
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public interface Callback {
+
+        /**
+         * Notifies the callback when an animation to a side page was started.
+         *
+         * @param rightPage Is the page animated to the right page?
+         */
+        void onAnimationToSideStarted(boolean rightPage);
+
+        float getPageWidth();
+
+        ArrayList<View> getTranslationViews();
+
+        View getLeftIcon();
+
+        View getCenterIcon();
+
+        View getRightIcon();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index c83b479..3753a72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -30,7 +30,6 @@
 
 public final class NavigationBarTransitions extends BarTransitions {
 
-    private static final float KEYGUARD_QUIESCENT_ALPHA = 0.5f;
     private static final int CONTENT_FADE_DURATION = 200;
 
     private final NavigationBarView mView;
@@ -81,8 +80,6 @@
         setKeyButtonViewQuiescentAlpha(mView.getRecentsButton(), alpha, animate);
         setKeyButtonViewQuiescentAlpha(mView.getMenuButton(), alpha, animate);
 
-        setKeyButtonViewQuiescentAlpha(mView.getSearchLight(), KEYGUARD_QUIESCENT_ALPHA, animate);
-
         applyBackButtonQuiescentAlpha(mode, animate);
 
         // apply to lights out
@@ -96,7 +93,6 @@
 
     public void applyBackButtonQuiescentAlpha(int mode, boolean animate) {
         float backAlpha = 0;
-        backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getSearchLight());
         backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getHomeButton());
         backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getRecentsButton());
         backAlpha = maxVisibleQuiescentAlpha(backAlpha, mView.getMenuButton());
@@ -116,7 +112,6 @@
     public void setContentVisible(boolean visible) {
         final float alpha = visible ? 1 : 0;
         fadeContent(mView.getBackButton(), alpha);
-        fadeContent(mView.getSearchLight(), alpha);
     }
 
     private void fadeContent(View v, float alpha) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 089757a..21842bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -133,16 +133,6 @@
         }
     }
 
-    // simplified click handler to be used when device is in accessibility mode
-    private final OnClickListener mAccessibilityClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (v.getId() == R.id.search_light) {
-                KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
-            }
-        }
-    };
-
     private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
         @Override
         public void onClick(View view) {
@@ -246,11 +236,6 @@
         return mCurrentView.findViewById(R.id.ime_switcher);
     }
 
-    // for when home is disabled, but search isn't
-    public View getSearchLight() {
-        return mCurrentView.findViewById(R.id.search_light);
-    }
-
     private void getIcons(Resources res) {
         mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
         mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land);
@@ -345,9 +330,6 @@
         getHomeButton()   .setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
         getRecentsButton().setVisibility(disableRecent     ? View.INVISIBLE : View.VISIBLE);
 
-        final boolean showSearch = disableHome && !disableSearch;
-        setVisibleOrGone(getSearchLight(), showSearch);
-
         mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
     }
 
@@ -402,38 +384,6 @@
         mCurrentView = mRotatedViews[Surface.ROTATION_0];
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
-
-        watchForAccessibilityChanges();
-    }
-
-    private void watchForAccessibilityChanges() {
-        final AccessibilityManager am =
-                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-
-        // Set the initial state
-        enableAccessibility(am.isTouchExplorationEnabled());
-
-        // Watch for changes
-        am.addTouchExplorationStateChangeListener(new TouchExplorationStateChangeListener() {
-            @Override
-            public void onTouchExplorationStateChanged(boolean enabled) {
-                enableAccessibility(enabled);
-            }
-        });
-    }
-
-    private void enableAccessibility(boolean touchEnabled) {
-        Log.v(TAG, "touchEnabled:"  + touchEnabled);
-
-        // Add a touch handler or accessibility click listener for camera and search buttons
-        // for all view orientations.
-        final OnClickListener onClickListener = touchEnabled ? mAccessibilityClickListener : null;
-        for (int i = 0; i < mRotatedViews.length; i++) {
-            final View searchLight = mRotatedViews[i].findViewById(R.id.search_light);
-            if (searchLight != null) {
-                searchLight.setOnClickListener(onClickListener);
-            }
-        }
     }
 
     public boolean isVertical() {
@@ -571,7 +521,6 @@
         dumpButton(pw, "home", getHomeButton());
         dumpButton(pw, "rcnt", getRecentsButton());
         dumpButton(pw, "menu", getMenuButton());
-        dumpButton(pw, "srch", getSearchLight());
 
         pw.println("    }");
     }
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 0a44904..802e5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,9 +21,8 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.Path;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -32,7 +31,6 @@
 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;
@@ -43,13 +41,17 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.ArrayList;
+
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
-        View.OnClickListener {
+        View.OnClickListener, KeyguardPageSwipeHelper.Callback {
 
+    private KeyguardPageSwipeHelper mPageSwiper;
     PhoneStatusBar mStatusBar;
     private StatusBarHeaderView mHeader;
     private View mQsContainer;
+    private View mQsPanel;
     private View mKeyguardStatusView;
     private ObservableScrollView mScrollView;
     private View mStackScrollerContainer;
@@ -60,7 +62,7 @@
 
     private int mTrackingPointer;
     private VelocityTracker mVelocityTracker;
-    private boolean mTracking;
+    private boolean mQsTracking;
 
     /**
      * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
@@ -68,6 +70,7 @@
      */
     private boolean mIntercepting;
     private boolean mQsExpanded;
+    private boolean mKeyguardShowing;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
     private float mInitialTouchY;
@@ -77,6 +80,7 @@
     private int mQsMinExpansionHeight;
     private int mQsMaxExpansionHeight;
     private int mMinStackHeight;
+    private int mQsPeekHeight;
     private float mNotificationTranslation;
     private int mStackScrollerIntrinsicPadding;
     private boolean mQsExpansionEnabled = true;
@@ -92,6 +96,11 @@
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
             new KeyguardClockPositionAlgorithm.Result();
+    private boolean mIsSwipedHorizontally;
+    private boolean mIsExpanding;
+    private KeyguardBottomAreaView mKeyguardBottomArea;
+    private boolean mBlockTouches;
+    private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -121,6 +130,7 @@
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
         mStackScrollerContainer = findViewById(R.id.notification_container_parent);
         mQsContainer = findViewById(R.id.quick_settings_container);
+        mQsPanel = findViewById(R.id.quick_settings_panel);
         mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
         mScrollView.setListener(this);
         mNotificationStackScroller = (NotificationStackScrollLayout)
@@ -128,6 +138,10 @@
         mNotificationStackScroller.setOnHeightChangedListener(this);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.fast_out_slow_in);
+        mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+        mSwipeTranslationViews.add(mNotificationStackScroller);
+        mSwipeTranslationViews.add(mKeyguardStatusView);
+        mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
     }
 
     @Override
@@ -139,22 +153,23 @@
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
         mStatusBarMinHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
+        mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
         mClockPositionAlgorithm.loadDimens(getResources());
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        if (!mQsExpanded) {
-            positionClockAndNotifications();
-            mNotificationStackScroller.setStackHeight(getExpandedHeight());
-        }
 
         // Calculate quick setting heights.
-        mQsMinExpansionHeight = mHeader.getCollapsedHeight();
+        mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
         mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
-        if (mQsExpansionHeight == 0) {
-            mQsExpansionHeight = mQsMinExpansionHeight;
+        if (mQsExpanded) {
+            setQsStackScrollerPadding(mQsMaxExpansionHeight);
+        } else {
+            setQsExpansion(mQsMinExpansionHeight);
+            positionClockAndNotifications();
+            mNotificationStackScroller.setStackHeight(getExpandedHeight());
         }
     }
 
@@ -165,7 +180,8 @@
     private void positionClockAndNotifications() {
         boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
-            mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding;
+            mStackScrollerIntrinsicPadding = mHeader.getBottom() + mQsPeekHeight
+                    + mNotificationTopPadding;
             mTopPaddingAdjustment = 0;
         } else {
             mClockPositionAlgorithm.setup(
@@ -246,6 +262,13 @@
         mQsExpansionEnabled = qsExpansionEnabled;
     }
 
+    @Override
+    public void resetViews() {
+        mBlockTouches = false;
+        mPageSwiper.reset();
+        closeQs();
+    }
+
     public void closeQs() {
         cancelAnimation();
         setQsExpansion(mQsMinExpansionHeight);
@@ -262,9 +285,7 @@
     public void fling(float vel, boolean always) {
         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
         if (gr != null) {
-            gr.tag(
-                "fling " + ((vel > 0) ? "open" : "closed"),
-                "notifications,v=" + vel);
+            gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
         }
         super.fling(vel, always);
     }
@@ -282,6 +303,9 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -297,7 +321,7 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
-                if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
                     getParent().requestDisallowInterceptTouchEvent(true);
                 }
                 break;
@@ -315,7 +339,7 @@
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
                 trackMovement(event);
-                if (mTracking) {
+                if (mQsTracking) {
 
                     // Already tracking because onOverscrolled was called. We need to update here
                     // so we don't stop for a frame until the next touch event gets handled in
@@ -326,12 +350,12 @@
                     return true;
                 }
                 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
-                        && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
                     onQsExpansionStarted();
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mInitialTouchY = y;
                     mInitialTouchX = x;
-                    mTracking = true;
+                    mQsTracking = true;
                     mIntercepting = false;
                     return true;
                 }
@@ -340,9 +364,9 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mTracking) {
-                    flingWithCurrentVelocity();
-                    mTracking = false;
+                if (mQsTracking) {
+                    flingQsWithCurrentVelocity();
+                    mQsTracking = false;
                 }
                 mIntercepting = false;
                 break;
@@ -353,13 +377,15 @@
     @Override
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
 
-        // Block request so we can still intercept the scrolling when QS is expanded.
-        if (!mQsExpanded) {
-            super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        // Block request when interacting with the scroll view so we can still intercept the
+        // scrolling when QS is expanded.
+        if (mScrollView.isDispatchingTouchEvent()) {
+            return;
         }
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-    private void flingWithCurrentVelocity() {
+    private void flingQsWithCurrentVelocity() {
         float vel = getCurrentVelocity();
 
         // TODO: Better logic whether we should expand or not.
@@ -368,65 +394,83 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
         // implementation.
-        if (mTracking) {
-            int pointerIndex = event.findPointerIndex(mTrackingPointer);
-            if (pointerIndex < 0) {
-                pointerIndex = 0;
-                mTrackingPointer = event.getPointerId(pointerIndex);
+        if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
+            mPageSwiper.onTouchEvent(event);
+            if (mPageSwiper.isSwipingInProgress()) {
+                return true;
             }
-            final float y = event.getY(pointerIndex);
-            final float x = event.getX(pointerIndex);
-
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    mTracking = true;
-                    mInitialTouchY = y;
-                    mInitialTouchX = x;
-                    onQsExpansionStarted();
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    initVelocityTracker();
-                    trackMovement(event);
-                    break;
-
-                case MotionEvent.ACTION_POINTER_UP:
-                    final int upPointer = event.getPointerId(event.getActionIndex());
-                    if (mTrackingPointer == upPointer) {
-                        // gesture is ongoing, find a new pointer to track
-                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                        final float newY = event.getY(newIndex);
-                        final float newX = event.getX(newIndex);
-                        mTrackingPointer = event.getPointerId(newIndex);
-                        mInitialHeightOnTouch = mQsExpansionHeight;
-                        mInitialTouchY = newY;
-                        mInitialTouchX = newX;
-                    }
-                    break;
-
-                case MotionEvent.ACTION_MOVE:
-                    final float h = y - mInitialTouchY;
-                    setQsExpansion(h + mInitialHeightOnTouch);
-                    trackMovement(event);
-                    break;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    mTracking = false;
-                    mTrackingPointer = -1;
-                    trackMovement(event);
-                    flingWithCurrentVelocity();
-                    if (mVelocityTracker != null) {
-                        mVelocityTracker.recycle();
-                        mVelocityTracker = null;
-                    }
-                    break;
-            }
-            return true;
+        }
+        if (mQsTracking || mQsExpanded) {
+            return onQsTouch(event);
         }
 
-        // Consume touch events when QS are expanded.
-        return mQsExpanded || super.onTouchEvent(event);
+        super.onTouchEvent(event);
+        return true;
+    }
+
+    @Override
+    protected boolean hasConflictingGestures() {
+        return mStatusBar.getBarState() != StatusBarState.SHADE;
+    }
+
+    private boolean onQsTouch(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mQsTracking = true;
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                onQsExpansionStarted();
+                mInitialHeightOnTouch = mQsExpansionHeight;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                setQsExpansion(h + mInitialHeightOnTouch);
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mQsTracking = false;
+                mTrackingPointer = -1;
+                trackMovement(event);
+                flingQsWithCurrentVelocity();
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
     }
 
     @Override
@@ -436,7 +480,7 @@
             mInitialHeightOnTouch = mQsExpansionHeight;
             mInitialTouchY = mLastTouchY;
             mInitialTouchX = mLastTouchX;
-            mTracking = true;
+            mQsTracking = true;
         }
     }
 
@@ -453,29 +497,38 @@
         setQsExpansion(height);
     }
 
-    private void expandQs() {
-        mHeader.setExpanded(true);
-        mNotificationStackScroller.setEnabled(false);
-        mScrollView.setVisibility(View.VISIBLE);
-        mQsExpanded = true;
+    private void setQsExpanded(boolean expanded) {
+        boolean changed = mQsExpanded != expanded;
+        if (changed) {
+            mQsExpanded = expanded;
+            updateQsState();
+        }
     }
 
-    private void collapseQs() {
-        mHeader.setExpanded(false);
-        mNotificationStackScroller.setEnabled(true);
-        mScrollView.setVisibility(View.INVISIBLE);
-        mQsExpanded = false;
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        mKeyguardShowing = keyguardShowing;
+        updateQsState();
+    }
+
+    private void updateQsState() {
+        mHeader.setExpanded(mQsExpanded);
+        mNotificationStackScroller.setEnabled(!mQsExpanded);
+        mQsPanel.setVisibility(mQsExpanded ? View.VISIBLE : View.INVISIBLE);
+        mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded
+                ? View.INVISIBLE
+                : View.VISIBLE);
+        mScrollView.setTouchEnabled(mQsExpanded);
     }
 
     private void setQsExpansion(float height) {
         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
         if (height > mQsMinExpansionHeight && !mQsExpanded) {
-            expandQs();
+            setQsExpanded(true);
         } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
-            collapseQs();
+            setQsExpanded(false);
         }
         mQsExpansionHeight = height;
-        mHeader.setExpansion(height);
+        mHeader.setExpansion(height - mQsPeekHeight);
         setQsTranslation(height);
         setQsStackScrollerPadding(height);
         mStatusBar.userActivity();
@@ -557,7 +610,7 @@
     /**
      * @return Whether we should intercept a gesture to open Quick Settings.
      */
-    private boolean shouldIntercept(float x, float y, float yDiff) {
+    private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
         if (!mQsExpansionEnabled) {
             return false;
         }
@@ -635,15 +688,34 @@
     protected void onExpandingStarted() {
         super.onExpandingStarted();
         mNotificationStackScroller.onExpansionStarted();
+        mIsExpanding = true;
     }
 
     @Override
     protected void onExpandingFinished() {
         super.onExpandingFinished();
         mNotificationStackScroller.onExpansionStopped();
+        mIsExpanding = false;
     }
 
     @Override
+    protected void onOverExpansionChanged(float overExpansion) {
+        float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
+        mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + overExpansion
+                        - mOverExpansion, true /* onTop */, false /* animate */);
+        super.onOverExpansionChanged(overExpansion);
+    }
+
+    @Override
+    protected void onTrackingStopped(boolean expand) {
+        super.onTrackingStopped(expand);
+        mOverExpansion = 0.0f;
+        mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
+                true /* animate */);
+    }
+
+
+    @Override
     public void onHeightChanged(ExpandableView view) {
         requestPanelHeightUpdate();
     }
@@ -657,6 +729,12 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mPageSwiper.onConfigurationChanged();
+    }
+
+    @Override
     public void onClick(View v) {
         if (v == mHeader.getBackgroundView()) {
             onQsExpansionStarted();
@@ -667,4 +745,40 @@
             }
         }
     }
+
+    @Override
+    public void onAnimationToSideStarted(boolean rightPage) {
+        if (rightPage) {
+            mKeyguardBottomArea.launchCamera();
+        } else {
+            mKeyguardBottomArea.launchPhone();
+        }
+        mBlockTouches = true;
+    }
+
+
+    @Override
+    public float getPageWidth() {
+        return getWidth();
+    }
+
+    @Override
+    public ArrayList<View> getTranslationViews() {
+        return mSwipeTranslationViews;
+    }
+
+    @Override
+    public View getLeftIcon() {
+        return mKeyguardBottomArea.getPhoneImageView();
+    }
+
+    @Override
+    public View getCenterIcon() {
+        return mKeyguardBottomArea.getLockIcon();
+    }
+
+    @Override
+    public View getRightIcon() {
+        return mKeyguardBottomArea.getCameraImageView();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index ba0b66e..ea5b309 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ScrollView;
 
@@ -28,6 +29,8 @@
 
     private Listener mListener;
     private int mLastOverscrollAmount;
+    private boolean mDispatchingTouchEvent;
+    private boolean mTouchEnabled = true;
 
     public ObservableScrollView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -37,10 +40,18 @@
         mListener = listener;
     }
 
+    public void setTouchEnabled(boolean touchEnabled) {
+        mTouchEnabled = touchEnabled;
+    }
+
     public boolean isScrolledToBottom() {
         return getScrollY() == getMaxScrollY();
     }
 
+    public boolean isDispatchingTouchEvent() {
+        return mDispatchingTouchEvent;
+    }
+
     private int getMaxScrollY() {
         int scrollRange = 0;
         if (getChildCount() > 0) {
@@ -52,6 +63,17 @@
     }
 
     @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!mTouchEnabled) {
+            return false;
+        }
+        mDispatchingTouchEvent = true;
+        boolean result = super.dispatchTouchEvent(ev);
+        mDispatchingTouchEvent = false;
+        return result;
+    }
+
+    @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
         if (mListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 8800625..b94f6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -191,6 +191,7 @@
                 pv.setExpandedFraction(0); // just in case
                 pv.setVisibility(View.GONE);
                 pv.cancelPeek();
+                pv.resetViews();
             }
         }
         if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
@@ -222,7 +223,11 @@
         }
     }
 
-    public void onTrackingStopped(PanelView panel) {
+    public void onTrackingStopped(PanelView panel, boolean expand) {
         mTracking = false;
     }
+
+    public void onExpandingFinished() {
+
+    }
 }
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 8631e3a..c5a9b85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -35,9 +35,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public class PanelView extends FrameLayout {
+public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
+    protected float mOverExpansion;
 
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -66,6 +67,7 @@
     private float mInitialTouchX;
 
     protected void onExpandingFinished() {
+        mBar.onExpandingFinished();
     }
 
     protected void onExpandingStarted() {
@@ -128,21 +130,24 @@
         final float y = event.getY(pointerIndex);
         final float x = event.getX(pointerIndex);
 
+        boolean waitForTouchSlop = hasConflictingGestures();
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                mTracking = true;
 
                 mInitialTouchY = y;
                 mInitialTouchX = x;
+                mInitialOffsetOnTouch = mExpandedHeight;
                 if (mVelocityTracker == null) {
                     initVelocityTracker();
                 }
                 trackMovement(event);
-                if (mHeightAnimator != null) {
-                    mHeightAnimator.cancel(); // end any outstanding animations
+                if (!waitForTouchSlop || mHeightAnimator != null) {
+                    if (mHeightAnimator != null) {
+                        mHeightAnimator.cancel(); // end any outstanding animations
+                    }
+                    onTrackingStarted();
                 }
-                onTrackingStarted();
-                mInitialOffsetOnTouch = mExpandedHeight;
                 if (mExpandedHeight == 0) {
                     mJustPeeked = true;
                     runPeekAnimation();
@@ -164,15 +169,27 @@
                 break;
 
             case MotionEvent.ACTION_MOVE:
-                final float h = y - mInitialTouchY + mInitialOffsetOnTouch;
-                if (h > mPeekHeight) {
+                float h = y - mInitialTouchY;
+                if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
+                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+                    mInitialOffsetOnTouch = mExpandedHeight;
+                    mInitialTouchX = x;
+                    mInitialTouchY = y;
+                    if (mHeightAnimator != null) {
+                        mHeightAnimator.cancel(); // end any outstanding animations
+                    }
+                    onTrackingStarted();
+                    h = 0;
+                }
+                final float newHeight = h + mInitialOffsetOnTouch;
+                if (newHeight > mPeekHeight) {
                     if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
                         mPeekAnimator.cancel();
                     }
                     mJustPeeked = false;
                 }
-                if (!mJustPeeked) {
-                    setExpandedHeightInternal(h);
+                if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
+                    setExpandedHeightInternal(newHeight);
                     mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
                 }
 
@@ -181,25 +198,28 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mTracking = false;
                 mTrackingPointer = -1;
-                onTrackingStopped();
                 trackMovement(event);
-                flingWithCurrentVelocity();
+                boolean expand = flingWithCurrentVelocity();
+                onTrackingStopped(expand);
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
                 }
                 break;
         }
-        return true;
+        return !waitForTouchSlop || mTracking;
     }
 
-    protected void onTrackingStopped() {
-        mBar.onTrackingStopped(PanelView.this);
+    protected abstract boolean hasConflictingGestures();
+
+    protected void onTrackingStopped(boolean expand) {
+        mTracking = false;
+        mBar.onTrackingStopped(PanelView.this, expand);
     }
 
     protected void onTrackingStarted() {
+        mTracking = true;
         mBar.onTrackingStarted(PanelView.this);
         onExpandingStarted();
     }
@@ -302,7 +322,10 @@
         mMaxPanelHeight = -1;
     }
 
-    private void flingWithCurrentVelocity() {
+    /**
+     * @return whether the panel will be expanded after the animation
+     */
+    private boolean flingWithCurrentVelocity() {
         float vel = getCurrentVelocity();
         boolean expand;
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -311,11 +334,16 @@
             expand = vel > 0;
         }
         fling(vel, expand);
+        return expand;
     }
 
     protected void fling(float vel, boolean expand) {
         cancelPeek();
         float target = expand ? getMaxPanelHeight() : 0.0f;
+        if (target == mExpandedHeight) {
+            onExpandingFinished();
+            return;
+        }
         ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
         if (expand) {
             mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
@@ -403,6 +431,11 @@
     public void setExpandedHeightInternal(float h) {
         float fh = getMaxPanelHeight();
         mExpandedHeight = Math.min(fh, h);
+        float overExpansion = h - fh;
+        overExpansion = Math.max(0, overExpansion);
+        if (overExpansion != mOverExpansion) {
+            onOverExpansionChanged(overExpansion);
+        }
 
         if (DEBUG) {
             logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");
@@ -412,6 +445,10 @@
         mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
     }
 
+    protected void onOverExpansionChanged(float overExpansion) {
+        mOverExpansion = overExpansion;
+    }
+
     protected void onHeightUpdated(float expandedHeight) {
         requestLayout();
     }
@@ -503,4 +540,6 @@
                 mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":"")
         ));
     }
+
+    public abstract void resetViews();
 }
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 152ca3f..0e5b7e1 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;
@@ -63,6 +64,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
@@ -85,7 +87,6 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -120,10 +121,12 @@
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
+import com.android.systemui.volume.VolumeComponent;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -195,8 +198,9 @@
     NetworkControllerImpl mNetworkController;
     RotationLockControllerImpl mRotationLockController;
     UserInfoController mUserInfoController;
-    ZenModeControllerImpl mZenModeController;
+    ZenModeController mZenModeController;
     CastControllerImpl mCastController;
+    VolumeComponent mVolumeComponent;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -208,6 +212,7 @@
     PhoneStatusBarView mStatusBarView;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     private StatusBarWindowManager mStatusBarWindowManager;
+    private UnlockMethodCache mUnlockMethodCache;
 
     int mPixelFormat;
     Object mQueueLock = new Object();
@@ -371,6 +376,7 @@
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private ViewMediatorCallback mKeyguardViewMediatorCallback;
+    private ScrimController mScrimController;
 
     private final Runnable mAutohide = new Runnable() {
         @Override
@@ -499,6 +505,12 @@
     }
 
     @Override
+    protected void setShowLockscreenNotifications(boolean show) {
+        super.setShowLockscreenNotifications(show);
+        updateStackScrollerState();
+    }
+
+    @Override
     public void start() {
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
@@ -521,6 +533,7 @@
                     Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
                     mHeadsUpObserver);
         }
+        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         startKeyguard();
     }
 
@@ -637,9 +650,12 @@
         SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
                         R.layout.status_bar_notification_speed_bump, mStackScroller, false);
         mStackScroller.setSpeedBumpView(speedBump);
-
         mExpandedContents = mStackScroller;
 
+        mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
+                mStatusBarWindow.findViewById(R.id.scrim_in_front));
+        mStatusBarView.setScrimController(mScrimController);
+
         mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
         mHeader.setActivityStarter(this);
         mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
@@ -679,7 +695,8 @@
             mRotationLockController = new RotationLockControllerImpl(mContext);
         }
         mUserInfoController = new UserInfoController(mContext);
-        mZenModeController = new ZenModeControllerImpl(mContext, mHandler);
+        mVolumeComponent = getComponent(VolumeComponent.class);
+        mZenModeController = mVolumeComponent.getZenController();
         mCastController = new CastControllerImpl(mContext);
         final SignalClusterView signalCluster =
                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
@@ -742,7 +759,7 @@
             final QSTileHost qsh = new QSTileHost(mContext, this,
                     mBluetoothController, mLocationController, mRotationLockController,
                     mNetworkController, mZenModeController, null /*tethering*/,
-                    mCastController);
+                    mCastController, mVolumeComponent);
             for (QSTile<?> tile : qsh.getTiles()) {
                 mQSPanel.addTile(tile);
             }
@@ -774,7 +791,7 @@
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
-                mStatusBarWindow, mStatusBarWindowManager);
+                mStatusBarWindow, mStatusBarWindowManager, mScrimController);
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
     }
 
@@ -879,7 +896,7 @@
         }
     };
 
-    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
+    View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
             case MotionEvent.ACTION_DOWN:
@@ -914,8 +931,7 @@
 
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
-        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
-        mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
+        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
         updateSearchPanel();
     }
 
@@ -965,7 +981,7 @@
 
     private void addHeadsUpView() {
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
@@ -1028,13 +1044,15 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
         if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
         Entry shadeEntry = createNotificationViews(notification);
         if (shadeEntry == null) {
             return;
         }
         if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
+            // Forward the ranking so we can sort the new notification.
+            mNotificationData.updateRanking(ranking);
             return;
         }
         if (mUseHeadsUp && shouldInterrupt(notification)) {
@@ -1074,7 +1092,7 @@
                 tick(notification, true);
             }
         }
-        addNotificationViews(shadeEntry);
+        addNotificationViews(shadeEntry, ranking);
         // Recalculate the position of the sliding windows and the titles.
         setAreThereNotifications();
         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -1090,14 +1108,14 @@
     }
 
     @Override
-    public void updateNotification(StatusBarNotification notification) {
-        super.updateNotification(notification);
+    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+        super.updateNotificationInternal(notification, ranking);
         mIntercepted.update(notification);
     }
 
     @Override
-    public void removeNotificationInternal(String key) {
-        StatusBarNotification old = removeNotificationViews(key);
+    public void removeNotificationInternal(String key, Ranking ranking) {
+        StatusBarNotification old = removeNotificationViews(key, ranking);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
         if (old != null) {
@@ -1134,7 +1152,7 @@
             R.integer.config_show_search_delay);
     }
 
-    private void loadNotificationShade() {
+    private void updateNotificationShade() {
         if (mStackScroller == null) return;
 
         int N = mNotificationData.size();
@@ -1144,7 +1162,7 @@
         final boolean provisioned = isDeviceProvisioned();
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
-            Entry ent = mNotificationData.get(N-i-1);
+            Entry ent = mNotificationData.get(i);
             if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
 
             // TODO How do we want to badge notifcations from profiles.
@@ -1175,26 +1193,75 @@
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
-                mStackScroller.addView(v, i);
+                mStackScroller.addView(v);
             }
         }
 
+        // So after all this work notifications still aren't sorted correctly.
+        // Let's do that now by advancing through toShow and mStackScroller in
+        // lock-step, making sure mStackScroller matches what we see in toShow.
+        int j = 0;
+        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+            View child = mStackScroller.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                // We don't care about non-notification views.
+                continue;
+            }
+
+            if (child == toShow.get(j)) {
+                // Everything is well, advance both lists.
+                j++;
+                continue;
+            }
+
+            // Oops, wrong notification at this position. Put the right one
+            // here and advance both lists.
+            mStackScroller.changeViewPosition(toShow.get(j), i);
+            j++;
+        }
+        updateRowStates();
+        updateSpeedbump();
         mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
     }
 
+    private void updateSpeedbump() {
+        int speedbumpIndex = -1;
+        int currentIndex = 0;
+        for (int i = 0; i < mNotificationData.size(); i++) {
+            Entry entry = mNotificationData.get(i);
+            if (entry.row.getParent() == null) {
+                // This view isn't even added, so the stack scroller doesn't
+                // know about it. Ignore completely.
+                continue;
+            }
+            if (entry.row.getVisibility() != View.GONE &&
+                    mNotificationData.isAmbient(entry.key)) {
+                speedbumpIndex = currentIndex;
+                break;
+            }
+            currentIndex++;
+        }
+        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
+    }
+
     @Override
-    protected void updateNotificationIcons() {
+    protected void updateNotifications() {
+        // TODO: Move this into updateNotificationIcons()?
         if (mNotificationIcons == null) return;
 
-        loadNotificationShade();
+        updateNotificationShade();
+        updateNotificationIcons();
+    }
 
+    private void updateNotificationIcons() {
         final LinearLayout.LayoutParams params
             = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
 
         int N = mNotificationData.size();
 
         if (DEBUG) {
-            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
+            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" +
+                    mNotificationIcons);
         }
 
         ArrayList<View> toShow = new ArrayList<View>();
@@ -1202,7 +1269,7 @@
         final boolean provisioned = isDeviceProvisioned();
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
-            Entry ent = mNotificationData.get(N-i-1);
+            Entry ent = mNotificationData.get(i);
             if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
                     || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
             if (!notificationIsForCurrentProfiles(ent.notification)) continue;
@@ -1445,6 +1512,10 @@
         startActivityDismissingKeyguard(intent, false);
     }
 
+    public ScrimController getScrimController() {
+        return mScrimController;
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -2346,6 +2417,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) {
@@ -2366,7 +2446,7 @@
     public void userSwitched(int newUserId) {
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
         animateCollapsePanels();
-        updateNotificationIcons();
+        updateNotifications();
         resetUserSetupObserver();
     }
 
@@ -2752,7 +2832,7 @@
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-            mNotificationPanel.closeQs();
+            mNotificationPanel.resetViews();
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
@@ -2760,22 +2840,27 @@
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mHeader.setKeyguardShowing(true);
+            mNotificationPanel.setKeyguardShowing(true);
+            mScrimController.setKeyguardShowing(true);
         } else {
             mKeyguardBottomArea.setVisibility(View.GONE);
             mHeader.setKeyguardShowing(false);
+            mNotificationPanel.setKeyguardShowing(false);
+            mScrimController.setKeyguardShowing(false);
         }
 
         updateStackScrollerState();
         updatePublicMode();
-        updateRowStates();
-        updateSpeedBump();
+        updateNotifications();
         checkBarModes();
-        updateNotificationIcons();
         updateCarrierLabelVisibility(false);
     }
 
     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() {
@@ -2860,10 +2945,15 @@
         }
     }
 
-    public void onTrackingStopped() {
+    public void onTrackingStopped(boolean expand) {
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
         }
+        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+            if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
+                showBouncer();
+            }
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 084bfcf..910d88c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -36,21 +36,16 @@
     private static final boolean DEBUG_GESTURES = true;
 
     PhoneStatusBar mBar;
-    int mScrimColor;
-    int mScrimColorKeyguard;
 
-    PanelView mFadingPanel = null;
     PanelView mLastFullyOpenedPanel = null;
     PanelView mNotificationPanel;
-    private boolean mShouldFade;
     private final PhoneStatusBarTransitions mBarTransitions;
+    private ScrimController mScrimController;
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         Resources res = getContext().getResources();
-        mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
-        mScrimColorKeyguard = res.getColor(R.color.notification_panel_scrim_color_keyguard);
         mBarTransitions = new PhoneStatusBarTransitions(this);
     }
 
@@ -62,6 +57,10 @@
         mBar = bar;
     }
 
+    public void setScrimController(ScrimController scrimController) {
+        mScrimController = scrimController;
+    }
+
     @Override
     public void onFinishInflate() {
         mBarTransitions.init();
@@ -110,27 +109,11 @@
     }
 
     @Override
-    public void startOpeningPanel(PanelView panel) {
-        super.startOpeningPanel(panel);
-        // we only want to start fading if this is the "first" or "last" panel,
-        // which is kind of tricky to determine
-        mShouldFade = (mFadingPanel == null || mFadingPanel.isFullyExpanded());
-        if (DEBUG) {
-            Log.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
-        }
-        mFadingPanel = panel;
-    }
-
-    @Override
     public void onAllPanelsCollapsed() {
         super.onAllPanelsCollapsed();
         // give animations time to settle
         mBar.makeExpandedInvisibleSoon();
-        mFadingPanel = null;
         mLastFullyOpenedPanel = null;
-        if (mScrimColor != 0 && ActivityManager.isHighEndGfx() && mBar.mStatusBarWindow != null) {
-            mBar.mStatusBarWindow.setBackgroundColor(0);
-        }
     }
 
     @Override
@@ -139,9 +122,7 @@
         if (openPanel != mLastFullyOpenedPanel) {
             openPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
-        mFadingPanel = openPanel;
         mLastFullyOpenedPanel = openPanel;
-        mShouldFade = true; // now you own the fade, mister
     }
 
     @Override
@@ -163,12 +144,19 @@
     public void onTrackingStarted(PanelView panel) {
         super.onTrackingStarted(panel);
         mBar.onTrackingStarted();
+        mScrimController.onTrackingStarted();
     }
 
     @Override
-    public void onTrackingStopped(PanelView panel) {
-        super.onTrackingStopped(panel);
-        mBar.onTrackingStopped();
+    public void onTrackingStopped(PanelView panel, boolean expand) {
+        super.onTrackingStopped(panel, expand);
+        mBar.onTrackingStopped(expand);
+    }
+
+    @Override
+    public void onExpandingFinished() {
+        super.onExpandingFinished();
+        mScrimController.onExpandingFinished();
     }
 
     @Override
@@ -184,27 +172,7 @@
             Log.v(TAG, "panelExpansionChanged: f=" + frac);
         }
 
-        if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()
-                && mBar.mStatusBarWindow != null) {
-            if (mShouldFade) {
-                int scrimColor = (mBar.getBarState() == StatusBarState.KEYGUARD
-                        || mBar.getBarState() == StatusBarState.SHADE_LOCKED)
-                        ? mScrimColorKeyguard
-                        : mScrimColor;
-                frac = mPanelExpandedFractionSum; // don't judge me
-                // let's start this 20% of the way down the screen
-                frac = frac * 1.2f - 0.2f;
-                if (frac <= 0) {
-                    mBar.mStatusBarWindow.setBackgroundColor(0);
-                } else {
-                    // woo, special effects
-                    final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
-                    // attenuate background color alpha by k
-                    final int color = (int) ((scrimColor >>> 24) * k) << 24 | (scrimColor & 0xFFFFFF);
-                    mBar.mStatusBarWindow.setBackgroundColor(color);
-                }
-            }
-        }
+        mScrimController.setPanelExpansion(frac);
 
         // fade out the panel as it gets buried into the status bar to avoid overdrawing the
         // status bar on the last frame of a close animation
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7029898..1344703 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,6 +21,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 
+import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
@@ -29,11 +30,10 @@
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
 import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.RingerModeTile;
+import com.android.systemui.qs.tiles.NotificationsTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.HotspotTile;
 import com.android.systemui.qs.tiles.WifiTile;
-import com.android.systemui.qs.tiles.ZenModeTile;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.TetheringController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeComponent;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -60,13 +61,15 @@
     private final CastController mCast;
     private final Looper mLooper;
     private final CurrentUserTracker mUserTracker;
+    private final VolumeComponent mVolume;
     private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>();
+    private final int mFeedbackStartDelay;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
             BluetoothController bluetooth, LocationController location,
             RotationLockController rotation, NetworkController network,
             ZenModeController zen, TetheringController tethering,
-            CastController cast) {
+            CastController cast, VolumeComponent volume) {
         mContext = context;
         mStatusBar = statusBar;
         mBluetooth = bluetooth;
@@ -76,6 +79,7 @@
         mZen = zen;
         mTethering = tethering;
         mCast = cast;
+        mVolume = volume;
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
         ht.start();
@@ -86,8 +90,7 @@
         mTiles.add(new ColorInversionTile(this));
         mTiles.add(new CellularTile(this));
         mTiles.add(new AirplaneModeTile(this));
-        mTiles.add(new ZenModeTile(this));
-        mTiles.add(new RingerModeTile(this));
+        mTiles.add(new NotificationsTile(this));
         mTiles.add(new RotationLockTile(this));
         mTiles.add(new LocationTile(this));
         mTiles.add(new CastTile(this));
@@ -103,6 +106,7 @@
             }
         };
         mUserTracker.startTracking();
+        mFeedbackStartDelay = mContext.getResources().getInteger(R.integer.feedback_start_delay);
     }
 
     @Override
@@ -112,7 +116,7 @@
 
     @Override
     public void startSettingsActivity(final Intent intent) {
-        mStatusBar.postStartSettingsActivity(intent, QSTile.FEEDBACK_START_DELAY);
+        mStatusBar.postStartSettingsActivity(intent, mFeedbackStartDelay);
     }
 
     @Override
@@ -169,4 +173,9 @@
     public CastController getCastController() {
         return mCast;
     }
+
+    @Override
+    public VolumeComponent getVolumeComponent() {
+        return mVolume;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
new file mode 100644
index 0000000..6156fc3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -0,0 +1,193 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * Controls both the scrim behind the notifications and in front of the notifications (when a
+ * security method gets shown).
+ */
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
+
+    private static final float SCRIM_BEHIND_ALPHA = 0.62f;
+    private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.5f;
+    private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
+    private static final long ANIMATION_DURATION = 220;
+
+    private final View mScrimBehind;
+    private final View mScrimInFront;
+    private final UnlockMethodCache mUnlockMethodCache;
+
+    private boolean mKeyguardShowing;
+    private float mFraction;
+
+    private boolean mDarkenWhileDragging;
+    private boolean mBouncerShowing;
+    private boolean mAnimateChange;
+    private boolean mUpdatePending;
+    private boolean mExpanding;
+
+    private final Interpolator mInterpolator = new DecelerateInterpolator();
+
+    public ScrimController(View scrimBehind, View scrimInFront) {
+        mScrimBehind = scrimBehind;
+        mScrimInFront = scrimInFront;
+        mUnlockMethodCache = UnlockMethodCache.getInstance(scrimBehind.getContext());
+    }
+
+    public void setKeyguardShowing(boolean showing) {
+        mKeyguardShowing = showing;
+        scheduleUpdate();
+    }
+
+    public void onTrackingStarted() {
+        mExpanding = true;
+        mDarkenWhileDragging = !mUnlockMethodCache.isMethodInsecure();
+    }
+
+    public void onExpandingFinished() {
+        mExpanding = false;
+    }
+
+    public void setPanelExpansion(float fraction) {
+        mFraction = fraction;
+        scheduleUpdate();
+    }
+
+    public void setBouncerShowing(boolean showing) {
+        mBouncerShowing = showing;
+        mAnimateChange = !mExpanding;
+        scheduleUpdate();
+    }
+
+    private void scheduleUpdate() {
+        if (mUpdatePending) return;
+        mScrimBehind.getViewTreeObserver().addOnPreDrawListener(this);
+        mUpdatePending = true;
+    }
+
+    private void updateScrims() {
+        if (!mKeyguardShowing) {
+            updateScrimNormal();
+            setScrimInFrontColor(0);
+        } else {
+            updateScrimKeyguard();
+        }
+        mAnimateChange = false;
+    }
+
+    private void updateScrimKeyguard() {
+        if (mExpanding && mDarkenWhileDragging) {
+            float behindFraction = Math.max(0, Math.min(mFraction, 1));
+            float fraction = 1 - behindFraction;
+            setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
+            setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
+        } else if (mBouncerShowing) {
+            setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
+            setScrimBehindColor(0f);
+        } else {
+            setScrimInFrontColor(0f);
+            setScrimBehindColor(SCRIM_BEHIND_ALPHA_KEYGUARD);
+        }
+    }
+
+    private void updateScrimNormal() {
+        float frac = mFraction;
+        // let's start this 20% of the way down the screen
+        frac = frac * 1.2f - 0.2f;
+        if (frac <= 0) {
+            setScrimBehindColor(0);
+        } else {
+            // woo, special effects
+            final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
+            setScrimBehindColor(k * SCRIM_BEHIND_ALPHA);
+        }
+    }
+
+    private void setScrimBehindColor(float alpha) {
+        setScrimColor(mScrimBehind, alpha);
+    }
+
+    private void setScrimInFrontColor(float alpha) {
+        setScrimColor(mScrimInFront, alpha);
+        if (alpha == 0f) {
+            mScrimInFront.setClickable(false);
+        } else {
+
+            // Eat touch events.
+            mScrimInFront.setClickable(true);
+        }
+    }
+
+    private void setScrimColor(View scrim, float alpha) {
+        int color = Color.argb((int) (alpha * 255), 0, 0, 0);
+        if (mAnimateChange) {
+            startScrimAnimation(scrim, color);
+        } else {
+            scrim.setBackgroundColor(color);
+        }
+    }
+
+    private void startScrimAnimation(final View scrim, int targetColor) {
+        int current = getBackgroundAlpha(scrim);
+        int target = Color.alpha(targetColor);
+        if (current == targetColor) {
+            return;
+        }
+        ValueAnimator anim = ValueAnimator.ofInt(current, target);
+        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                int value = (int) animation.getAnimatedValue();
+                scrim.setBackgroundColor(Color.argb(value, 0, 0, 0));
+            }
+        });
+        anim.setInterpolator(mInterpolator);
+        anim.setDuration(ANIMATION_DURATION);
+        anim.start();
+    }
+
+    private int getBackgroundAlpha(View scrim) {
+        if (scrim.getBackground() instanceof ColorDrawable) {
+            ColorDrawable drawable = (ColorDrawable) scrim.getBackground();
+            return Color.alpha(drawable.getColor());
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
+        mUpdatePending = false;
+        updateScrims();
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 389e725..3245f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -38,6 +38,11 @@
  */
 public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
 
+    /**
+     * How much the header expansion gets rubberbanded while expanding the panel.
+     */
+    private static final float EXPANSION_RUBBERBAND_FACTOR = 0.35f;
+
     private boolean mExpanded;
     private boolean mKeyguardShowing;
 
@@ -128,6 +133,8 @@
             updateVisibilities();
             updateSystemIconsLayoutParams();
             updateBrightnessControllerState();
+            updateZTranslation();
+            updateClickTargets();
             if (mQSPanel != null) {
                 mQSPanel.setExpanded(expanded);
             }
@@ -202,18 +209,30 @@
         }
     }
 
+    private void updateClickTargets() {
+        mDateTime.setClickable(mExpanded);
+        mMultiUserSwitch.setClickable(mExpanded);
+    }
+
+    private void updateZTranslation() {
+
+        // If we are on the Keyguard, we need to set our z position to zero, so we don't get
+        // shadows.
+        if (mKeyguardShowing && !mExpanded) {
+            setZ(0);
+        } else {
+            setTranslationZ(0);
+        }
+    }
+
     public void setExpansion(float height) {
+        height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight;
         if (height < mCollapsedHeight) {
             height = mCollapsedHeight;
         }
         if (height > mExpandedHeight) {
             height = mExpandedHeight;
         }
-        if (mExpanded) {
-            mBackground.setTranslationY(-(mExpandedHeight - height));
-        } else {
-            mBackground.setTranslationY(0);
-        }
         setClipping(height);
     }
 
@@ -247,14 +266,10 @@
 
     public void setKeyguardShowing(boolean keyguardShowing) {
         mKeyguardShowing = keyguardShowing;
-        if (keyguardShowing) {
-            setZ(0);
-        } else {
-            setTranslationZ(0);
-        }
         updateHeights();
         updateWidth();
         updateVisibilities();
+        updateZTranslation();
     }
 
     public void setUserInfoController(UserInfoController userInfoController) {
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..d5551b8 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,
@@ -43,6 +45,7 @@
     private LockPatternUtils mLockPatternUtils;
     private ViewMediatorCallback mViewMediatorCallback;
     private PhoneStatusBar mPhoneStatusBar;
+    private ScrimController mScrimController;
 
     private ViewGroup mContainer;
     private StatusBarWindowManager mStatusBarWindowManager;
@@ -66,10 +69,12 @@
     }
 
     public void registerStatusBar(PhoneStatusBar phoneStatusBar,
-            ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
+            ViewGroup container, StatusBarWindowManager statusBarWindowManager,
+            ScrimController scrimController) {
         mPhoneStatusBar = phoneStatusBar;
         mContainer = container;
         mStatusBarWindowManager = statusBarWindowManager;
+        mScrimController = scrimController;
         mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
                 mStatusBarWindowManager, container);
     }
@@ -96,7 +101,7 @@
             mBouncer.show();
         } else {
             mPhoneStatusBar.showKeyguard();
-            mBouncer.hide();
+            mBouncer.hide(false /* destroyView */);
             mBouncer.prepare();
         }
     }
@@ -108,6 +113,13 @@
         updateStates();
     }
 
+    public void dismissWithAction(OnDismissAction r) {
+        if (!mOccluded) {
+            mBouncer.showWithDismissAction(r);
+        }
+        updateStates();
+    }
+
     /**
      * Reset the state of the view.
      */
@@ -115,7 +127,7 @@
         if (mShowing) {
             if (mOccluded) {
                 mPhoneStatusBar.hideKeyguard();
-                mBouncer.hide();
+                mBouncer.hide(false /* destroyView */);
             } else {
                 showBouncerOrKeyguard();
             }
@@ -175,7 +187,7 @@
         mShowing = false;
         mPhoneStatusBar.hideKeyguard();
         mStatusBarWindowManager.setKeyguardShowing(false);
-        mBouncer.hide();
+        mBouncer.hide(true /* destroyView */);
         mViewMediatorCallback.keyguardGone();
         updateStates();
     }
@@ -207,7 +219,7 @@
      */
     public boolean onBackPressed() {
         if (mBouncer.isShowing()) {
-            mBouncer.hide();
+            mBouncer.hide(false /* destroyView */);
             mPhoneStatusBar.showKeyguard();
             updateStates();
             return true;
@@ -244,6 +256,7 @@
         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
             mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
             mPhoneStatusBar.setBouncerShowing(bouncerShowing);
+            mScrimController.setBouncerShowing(bouncerShowing);
         }
 
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
deleted file mode 100644
index 049c5fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Button;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.KeyButtonView;
-
-/**
- * A swipeable button for affordances on the lockscreen. This is used for the camera and phone
- * affordance.
- */
-public class SwipeAffordanceView extends KeyButtonView {
-
-    private static final int SWIPE_DIRECTION_START = 0;
-    private static final int SWIPE_DIRECTION_END = 1;
-
-    private static final int SWIPE_DIRECTION_LEFT = 0;
-    private static final int SWIPE_DIRECTION_RIGHT = 1;
-
-    private AffordanceListener mListener;
-    private int mScaledTouchSlop;
-    private float mDragDistance;
-    private int mResolvedSwipeDirection;
-    private int mSwipeDirection;
-
-    public SwipeAffordanceView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SwipeAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        TypedArray a = context.getTheme().obtainStyledAttributes(
-                attrs,
-                R.styleable.SwipeAffordanceView,
-                0, 0);
-        try {
-            mSwipeDirection = a.getInt(R.styleable.SwipeAffordanceView_swipeDirection, 0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        if (!isLayoutRtl()) {
-            mResolvedSwipeDirection = mSwipeDirection;
-        } else {
-            mResolvedSwipeDirection = mSwipeDirection == SWIPE_DIRECTION_START
-                    ? SWIPE_DIRECTION_RIGHT
-                    : SWIPE_DIRECTION_LEFT;
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDragDistance = getResources().getDimension(R.dimen.affordance_drag_distance);
-        mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-    }
-
-    public void enableAccessibility(boolean touchExplorationEnabled) {
-
-        // Add a touch handler or accessibility click listener for camera button.
-        if (touchExplorationEnabled) {
-            setOnTouchListener(null);
-            setOnClickListener(mClickListener);
-        } else {
-            setOnTouchListener(mTouchListener);
-            setOnClickListener(null);
-        }
-    }
-
-    public void setAffordanceListener(AffordanceListener listener) {
-        mListener = listener;
-    }
-
-    private void onActionPerformed() {
-        if (mListener != null) {
-            mListener.onActionPerformed(this);
-        }
-    }
-
-    private void onUserActivity(long when) {
-        if (mListener != null) {
-            mListener.onUserActivity(when);
-        }
-    }
-
-    private final OnClickListener mClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            onActionPerformed();
-        }
-    };
-
-    private final OnTouchListener mTouchListener = new OnTouchListener() {
-        private float mStartX;
-        private boolean mTouchSlopReached;
-        private boolean mSkipCancelAnimation;
-
-        @Override
-        public boolean onTouch(final View view, MotionEvent event) {
-            float realX = event.getRawX();
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    mStartX = realX;
-                    mTouchSlopReached = false;
-                    mSkipCancelAnimation = false;
-                    break;
-                case MotionEvent.ACTION_MOVE:
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX > mStartX
-                            : realX < mStartX) {
-                        realX = mStartX;
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mDragDistance
-                            : realX > mStartX + mDragDistance) {
-                        view.setPressed(true);
-                        onUserActivity(event.getEventTime());
-                    } else {
-                        view.setPressed(false);
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mScaledTouchSlop
-                            : realX > mStartX + mScaledTouchSlop) {
-                        mTouchSlopReached = true;
-                    }
-                    view.setTranslationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                    ? Math.max(realX - mStartX, -mDragDistance)
-                                    : Math.min(realX - mStartX, mDragDistance));
-                    break;
-                case MotionEvent.ACTION_UP:
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mDragDistance
-                            : realX > mStartX + mDragDistance) {
-                        onActionPerformed();
-                        view.animate().x(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                ? -view.getWidth()
-                                : ((View) view.getParent()).getWidth() + view.getWidth())
-                                .setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
-                                new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        view.setTranslationX(0);
-                                    }
-                                });
-                        mSkipCancelAnimation = true;
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mScaledTouchSlop
-                            : realX > mStartX + mScaledTouchSlop) {
-                        mTouchSlopReached = true;
-                    }
-                    if (!mTouchSlopReached) {
-                        mSkipCancelAnimation = true;
-                        view.animate().translationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                ? -mDragDistance / 2
-                                : mDragDistance / 2).
-                                setInterpolator(new DecelerateInterpolator()).withEndAction(
-                                new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        view.animate().translationX(0).
-                                                setInterpolator(new AccelerateInterpolator());
-                                    }
-                                });
-                    }
-                case MotionEvent.ACTION_CANCEL:
-                    view.setPressed(false);
-                    if (!mSkipCancelAnimation) {
-                        view.animate().translationX(0)
-                                .setInterpolator(new AccelerateInterpolator(2f));
-                    }
-                    break;
-            }
-            return true;
-        }
-    };
-
-    public interface AffordanceListener {
-
-        /**
-         * Called when the view would like to report user activity.
-         *
-         * @param when The timestamp of the user activity in {@link SystemClock#uptimeMillis} time
-         *             base.
-         */
-        void onUserActivity(long when);
-
-        /**
-         * Called when the action of the affordance has been performed.
-         */
-        void onActionPerformed(SwipeAffordanceView view);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
new file mode 100644
index 0000000..bfd657b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -0,0 +1,107 @@
+/*
+ * 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.Context;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+
+import java.util.ArrayList;
+
+/**
+ * Caches whether the current unlock method is insecure, taking trust into account. This information
+ * might be a little bit out of date and should not be used for actual security decisions; it should
+ * be only used for visual indications.
+ */
+public class UnlockMethodCache {
+
+    private static UnlockMethodCache sInstance;
+
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final ArrayList<OnUnlockMethodChangedListener> mListeners = new ArrayList<>();
+    private boolean mMethodInsecure;
+
+    private UnlockMethodCache(Context ctx) {
+        mLockPatternUtils = new LockPatternUtils(ctx);
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx);
+        KeyguardUpdateMonitor.getInstance(ctx).registerCallback(mCallback);
+        updateMethodSecure(true /* updateAlways */);
+    }
+
+    public static UnlockMethodCache getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new UnlockMethodCache(context);
+        }
+        return sInstance;
+    }
+
+    /**
+     * @return whether the current security method is secure, i. e. the bouncer will be shown
+     */
+    public boolean isMethodInsecure() {
+        return mMethodInsecure;
+    }
+
+    public void addListener(OnUnlockMethodChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeListener(OnUnlockMethodChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    private void updateMethodSecure(boolean updateAlways) {
+        int user = mLockPatternUtils.getCurrentUser();
+        boolean methodInsecure = !mLockPatternUtils.isSecure() ||
+                mKeyguardUpdateMonitor.getUserHasTrust(user);
+        boolean changed = methodInsecure != mMethodInsecure;
+        if (changed || updateAlways) {
+            mMethodInsecure = methodInsecure;
+            notifyListeners(mMethodInsecure);
+        }
+    }
+
+    private void notifyListeners(boolean secure) {
+        for (OnUnlockMethodChangedListener listener : mListeners) {
+            listener.onMethodSecureChanged(secure);
+        }
+    }
+
+    private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onUserSwitchComplete(int userId) {
+            updateMethodSecure(false /* updateAlways */);
+        }
+
+        @Override
+        public void onTrustChanged(int userId) {
+            updateMethodSecure(false /* updateAlways */);
+        }
+
+        @Override
+        public void onScreenTurnedOn() {
+            updateMethodSecure(false /* updateAlways */);
+        }
+    };
+
+    public static interface OnUnlockMethodChangedListener {
+        void onMethodSecureChanged(boolean methodSecure);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 81e2cb3..9271e71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Outline;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -25,6 +26,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
 
 import com.android.systemui.ExpandHelper;
@@ -35,14 +37,17 @@
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationData;
 
-public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback {
+public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
+        ViewTreeObserver.OnComputeInternalInsetsListener {
     private static final String TAG = "HeadsUpNotificationView";
     private static final boolean DEBUG = false;
     private static final boolean SPEW = DEBUG;
 
     Rect mTmpRect = new Rect();
+    int[] mTmpTwoArray = new int[2];
 
     private final int mTouchSensitivityDelay;
+    private final float mMaxAlpha = 0.95f;
     private SwipeHelper mSwipeHelper;
     private EdgeSwipeHelper mEdgeSwipeHelper;
 
@@ -87,8 +92,9 @@
             }
             mContentHolder.setX(0);
             mContentHolder.setVisibility(View.VISIBLE);
-            mContentHolder.setAlpha(1f);
+            mContentHolder.setAlpha(mMaxAlpha);
             mContentHolder.addView(mHeadsUp.row);
+
             mSwipeHelper.snapChild(mContentHolder, 1f);
             mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
         }
@@ -99,32 +105,6 @@
         return mHeadsUp == null || mHeadsUp.notification.isClearable();
     }
 
-    public void setMargin(int notificationPanelMarginPx) {
-        if (SPEW) Log.v(TAG, "setMargin() " + notificationPanelMarginPx);
-        if (mContentHolder != null &&
-                mContentHolder.getLayoutParams() instanceof FrameLayout.LayoutParams) {
-            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mContentHolder.getLayoutParams();
-            lp.setMarginStart(notificationPanelMarginPx);
-            mContentHolder.setLayoutParams(lp);
-        }
-    }
-
-    // LinearLayout methods
-
-    @Override
-    public void onDraw(android.graphics.Canvas c) {
-        super.onDraw(c);
-        if (DEBUG) {
-            //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
-            //        + getMeasuredHeight() + "px");
-            c.save();
-            c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
-                    android.graphics.Region.Op.DIFFERENCE);
-            c.drawColor(0xFFcc00cc);
-            c.restore();
-        }
-    }
-
     // ViewGroup methods
 
     @Override
@@ -134,6 +114,7 @@
         float pagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
         float touchSlop = viewConfiguration.getScaledTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+        mSwipeHelper.setMaxAlpha(mMaxAlpha);
         mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
 
         int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
@@ -146,6 +127,8 @@
             // whoops, we're on already!
             setNotification(mHeadsUp);
         }
+
+        getViewTreeObserver().addOnComputeInternalInsetsListener(this);
     }
 
     @Override
@@ -163,6 +146,20 @@
     // View methods
 
     @Override
+    public void onDraw(android.graphics.Canvas c) {
+        super.onDraw(c);
+        if (DEBUG) {
+            //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
+            //        + getMeasuredHeight() + "px");
+            c.save();
+            c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
+                    android.graphics.Region.Op.DIFFERENCE);
+            c.drawColor(0xFFcc00cc);
+            c.restore();
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (System.currentTimeMillis() < mStartTouchTime) {
             return false;
@@ -183,6 +180,14 @@
         mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        Outline o = new Outline();
+        o.setRect(0, 0, mContentHolder.getWidth(), mContentHolder.getHeight());
+        mContentHolder.setOutline(o);
+    }
+
     // ExpandHelper.Callback methods
 
     @Override
@@ -233,7 +238,7 @@
 
     @Override
     public void onDragCancelled(View v) {
-        mContentHolder.setAlpha(1f); // sometimes this isn't quite reset
+        mContentHolder.setAlpha(mMaxAlpha); // sometimes this isn't quite reset
     }
 
     @Override
@@ -250,6 +255,16 @@
         return mContentHolder;
     }
 
+    @Override
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+        mContentHolder.getLocationOnScreen(mTmpTwoArray);
+
+        info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        info.touchableRegion.set(mTmpTwoArray[0], mTmpTwoArray[1],
+                mTmpTwoArray[0] + mContentHolder.getWidth(),
+                mTmpTwoArray[1] + mContentHolder.getHeight());
+    }
+
     private class EdgeSwipeHelper implements Gefingerpoken {
         private static final boolean DEBUG_EDGE_SWIPE = false;
         private final float mTouchSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 718acc3..330b599 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -17,12 +17,9 @@
 package com.android.systemui.statusbar.policy;
 
 import android.animation.Animator;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.hardware.input.InputManager;
 import android.os.SystemClock;
@@ -34,9 +31,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.SoundEffectConstants;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 
@@ -46,25 +41,18 @@
     private static final String TAG = "StatusBar.KeyButtonView";
     private static final boolean DEBUG = false;
 
-    final float GLOW_MAX_SCALE_FACTOR = 1.8f;
     public static final float DEFAULT_QUIESCENT_ALPHA = 0.70f;
 
-    long mDownTime;
-    int mCode;
-    int mTouchSlop;
-    Drawable mGlowBG;
-    int mGlowWidth, mGlowHeight;
-    float mGlowAlpha = 0f, mGlowScale = 1f;
-    @ViewDebug.ExportedProperty(category = "drawing")
-    float mDrawingAlpha = 1f;
-    @ViewDebug.ExportedProperty(category = "drawing")
-    float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
-    boolean mSupportsLongpress = true;
-    RectF mRect = new RectF();
-    AnimatorSet mPressedAnim;
-    Animator mAnimateToQuiescent = new ObjectAnimator();
+    private long mDownTime;
+    private int mCode;
+    private int mTouchSlop;
+    private float mDrawingAlpha = 1f;
+    private float mQuiescentAlpha = DEFAULT_QUIESCENT_ALPHA;
+    private boolean mSupportsLongpress = true;
+    private Animator mAnimateToQuiescent = new ObjectAnimator();
+    private Drawable mBackground;
 
-    Runnable mCheckLongPress = new Runnable() {
+    private final Runnable mCheckLongPress = new Runnable() {
         public void run() {
             if (isPressed()) {
                 // Log.d("KeyButtonView", "longpressed: " + this);
@@ -93,47 +81,27 @@
 
         mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
 
-        mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
-        setDrawingAlpha(mQuiescentAlpha);
-        if (mGlowBG != null) {
-            mGlowWidth = mGlowBG.getIntrinsicWidth();
-            mGlowHeight = mGlowBG.getIntrinsicHeight();
+        Drawable d = getBackground();
+        if (d != null) {
+            mBackground = d.mutate();
+            setBackground(mBackground);
         }
 
+        setDrawingAlpha(mQuiescentAlpha);
+
         a.recycle();
 
         setClickable(true);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mGlowBG != null) {
-            canvas.save();
-            final int w = getWidth();
-            final int h = getHeight();
-            final float aspect = (float)mGlowWidth / mGlowHeight;
-            final int drawW = (int)(h*aspect);
-            final int drawH = h;
-            final int margin = (drawW-w)/2;
-            canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
-            mGlowBG.setBounds(-margin, 0, drawW-margin, drawH);
-            mGlowBG.setAlpha((int)(mDrawingAlpha * mGlowAlpha * 255));
-            mGlowBG.draw(canvas);
-            canvas.restore();
-            mRect.right = w;
-            mRect.bottom = h;
-        }
-        super.onDraw(canvas);
-    }
-
     public void setQuiescentAlpha(float alpha, boolean animate) {
         mAnimateToQuiescent.cancel();
         alpha = Math.min(Math.max(alpha, 0), 1);
         if (alpha == mQuiescentAlpha && alpha == mDrawingAlpha) return;
         mQuiescentAlpha = alpha;
         if (DEBUG) Log.d(TAG, "New quiescent alpha = " + mQuiescentAlpha);
-        if (mGlowBG != null && animate) {
+        if (mBackground != null && animate) {
             mAnimateToQuiescent = animateToQuiescent();
             mAnimateToQuiescent.start();
         } else {
@@ -154,87 +122,35 @@
     }
 
     public void setDrawingAlpha(float x) {
-        // Calling setAlpha(int), which is an ImageView-specific
-        // method that's different from setAlpha(float). This sets
-        // the alpha on this ImageView's drawable directly
-        setAlpha((int) (x * 255));
+        setImageAlpha((int) (x * 255));
+        if (mBackground != null) {
+            mBackground.setAlpha((int)(x * 255));
+        }
         mDrawingAlpha = x;
     }
 
-    public float getGlowAlpha() {
-        if (mGlowBG == null) return 0;
-        return mGlowAlpha;
-    }
-
-    public void setGlowAlpha(float x) {
-        if (mGlowBG == null) return;
-        mGlowAlpha = x;
-        invalidate();
-    }
-
-    public float getGlowScale() {
-        if (mGlowBG == null) return 0;
-        return mGlowScale;
-    }
-
-    public void setGlowScale(float x) {
-        if (mGlowBG == null) return;
-        mGlowScale = x;
-        final float w = getWidth();
-        final float h = getHeight();
-        if (GLOW_MAX_SCALE_FACTOR <= 1.0f) {
-            // this only works if we know the glow will never leave our bounds
-            invalidate();
-        } else {
-            final float rx = (w * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
-            final float ry = (h * (GLOW_MAX_SCALE_FACTOR - 1.0f)) / 2.0f + 1.0f;
-            com.android.systemui.SwipeHelper.invalidateGlobalRegion(
-                    this,
-                    new RectF(getLeft() - rx,
-                              getTop() - ry,
-                              getRight() + rx,
-                              getBottom() + ry));
-
-            // also invalidate our immediate parent to help avoid situations where nearby glows
-            // interfere
-            ((View)getParent()).invalidate();
-        }
-    }
-
     public void setPressed(boolean pressed) {
-        if (mGlowBG != null) {
+        if (mBackground != null) {
             if (pressed != isPressed()) {
-                if (mPressedAnim != null && mPressedAnim.isRunning()) {
-                    mPressedAnim.cancel();
-                }
-                final AnimatorSet as = mPressedAnim = new AnimatorSet();
                 if (pressed) {
-                    if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
-                        mGlowScale = GLOW_MAX_SCALE_FACTOR;
-                    if (mGlowAlpha < mQuiescentAlpha)
-                        mGlowAlpha = mQuiescentAlpha;
                     setDrawingAlpha(1f);
-                    as.playTogether(
-                        ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
-                        ObjectAnimator.ofFloat(this, "glowScale", GLOW_MAX_SCALE_FACTOR)
-                    );
-                    as.setDuration(50);
                 } else {
                     mAnimateToQuiescent.cancel();
                     mAnimateToQuiescent = animateToQuiescent();
-                    as.playTogether(
-                        ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
-                        ObjectAnimator.ofFloat(this, "glowScale", 1f),
-                        mAnimateToQuiescent
-                    );
-                    as.setDuration(500);
+                    mAnimateToQuiescent.setDuration(500);
+                    mAnimateToQuiescent.start();
                 }
-                as.start();
             }
         }
         super.setPressed(pressed);
     }
 
+    private void setHotspot(float x, float y) {
+        if (mBackground != null) {
+            mBackground.setHotspot(x, y);
+        }
+    }
+
     public boolean onTouchEvent(MotionEvent ev) {
         final int action = ev.getAction();
         int x, y;
@@ -254,6 +170,7 @@
                     removeCallbacks(mCheckLongPress);
                     postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
                 }
+                setHotspot(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_MOVE:
                 x = (int)ev.getX();
@@ -262,6 +179,7 @@
                         && x < getWidth() + mTouchSlop
                         && y >= -mTouchSlop
                         && y < getHeight() + mTouchSlop);
+                setHotspot(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_CANCEL:
                 setPressed(false);
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/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 173af40..3ce6905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -166,7 +166,7 @@
                 if (rawAvatar != null) {
                     avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
                 } else {
-                    avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
+                    avatar = mContext.getResources().getDrawable(R.drawable.ic_account_circle);
                     mUseDefaultAvatar = true;
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 5e2d06b..cf56fa57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -27,6 +27,7 @@
     boolean animateZ;
     boolean animateScale;
     boolean animateHeight;
+    boolean animateTopInset;
     boolean animateDimmed;
     boolean hasDelays;
 
@@ -60,6 +61,11 @@
         return this;
     }
 
+    public AnimationFilter animateTopInset() {
+        animateTopInset = true;
+        return this;
+    }
+
     public AnimationFilter animateDimmed() {
         animateDimmed = true;
         return this;
@@ -84,6 +90,7 @@
         animateZ |= filter.animateZ;
         animateScale |= filter.animateScale;
         animateHeight |= filter.animateHeight;
+        animateTopInset |= filter.animateTopInset;
         animateDimmed |= filter.animateDimmed;
         hasDelays |= filter.hasDelays;
     }
@@ -94,6 +101,7 @@
         animateZ = false;
         animateScale = false;
         animateHeight = false;
+        animateTopInset = false;
         animateDimmed = false;
         hasDelays = false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 079b184..58176b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1599,6 +1599,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1607,6 +1608,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1615,6 +1617,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1623,6 +1626,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateDimmed()
                         .animateScale()
@@ -1651,6 +1655,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
         };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index bd2541a..2b52c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -49,6 +49,7 @@
     private int mBottomStackPeekSize;
     private int mZDistanceBetweenElements;
     private int mZBasicHeight;
+    private int mRoundedRectCornerRadius;
 
     private StackIndentationFunctor mTopStackIndentationFunctor;
     private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -111,6 +112,8 @@
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
         mBottomStackSlowDownLength = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
+        mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
 
@@ -146,6 +149,67 @@
 
         handleDraggedViews(ambientState, resultState, algorithmState);
         updateDimmedActivated(ambientState, resultState, algorithmState);
+        updateClipping(resultState, algorithmState);
+    }
+
+    private void updateClipping(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState) {
+        float previousNotificationEnd = 0;
+        float previousNotificationStart = 0;
+        boolean previousNotificationIsSwiped = false;
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            StackScrollState.ViewState state = resultState.getViewStateForView(child);
+            float newYTranslation = state.yTranslation;
+            int newHeight = state.height;
+            // apply clipping and shadow
+            float newNotificationEnd = newYTranslation + newHeight;
+
+            // In the unlocked shade we have to clip a little bit higher because of the rounded
+            // corners of the notifications.
+            float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
+
+            // When the previous notification is swiped, we don't clip the content to the
+            // bottom of it.
+            float clipHeight = previousNotificationIsSwiped
+                    ? newHeight
+                    : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
+
+            updateChildClippingAndBackground(state, newHeight, clipHeight,
+                    (int) (newHeight - (previousNotificationStart - newYTranslation)));
+
+            if (!child.isTransparent()) {
+                // Only update the previous values if we are not transparent,
+                // otherwise we would clip to a transparent view.
+                previousNotificationStart = newYTranslation + child.getClipTopAmount();
+                previousNotificationEnd = newNotificationEnd;
+                previousNotificationIsSwiped = child.getTranslationX() != 0;
+            }
+        }
+    }
+
+    /**
+     * Updates the shadow outline and the clipping for a view.
+     *
+     * @param state the viewState to update
+     * @param realHeight the currently applied height of the view
+     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
+     * @param backgroundHeight the desired background height. The shadows of the view will be
+     *                         based on this height and the content will be clipped from the top
+     */
+    private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight,
+            float clipHeight, int backgroundHeight) {
+        if (realHeight > clipHeight) {
+            state.topOverLap = (int) (realHeight - clipHeight);
+        } else {
+            state.topOverLap = 0;
+        }
+        if (realHeight > backgroundHeight) {
+            state.clipTopAmount = (realHeight - backgroundHeight);
+        } else {
+            state.clipTopAmount = 0;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index ae2acab..94cb16d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.stack;
 
-import android.graphics.Outline;
 import android.graphics.Rect;
 import android.util.Log;
 import android.view.View;
@@ -80,9 +79,6 @@
      */
     public void apply() {
         int numChildren = mHostView.getChildCount();
-        float previousNotificationEnd = 0;
-        float previousNotificationStart = 0;
-        boolean previousNotificationIsSwiped = false;
         for (int i = 0; i < numChildren; i++) {
             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
             ViewState state = mStateMap.get(child);
@@ -152,35 +148,41 @@
                 // apply dimming
                 child.setDimmed(state.dimmed, false /* animate */);
 
-                // apply clipping and shadow
-                float newNotificationEnd = newYTranslation + newHeight;
+                float oldClipTopAmount = child.getClipTopAmount();
+                if (oldClipTopAmount != state.clipTopAmount) {
+                    child.setClipTopAmount(state.clipTopAmount);
+                }
 
-                // When the previous notification is swiped, we don't clip the content to the
-                // bottom of it.
-                float clipHeight = previousNotificationIsSwiped
-                        ? newHeight
-                        : newNotificationEnd - (previousNotificationEnd);
-
-                updateChildClippingAndBackground(child, newHeight,
-                        clipHeight,
-                        (int) (newHeight - (previousNotificationStart - newYTranslation)));
-
-                if (!child.isTransparent()) {
-                    // Only update the previous values if we are not transparent,
-                    // otherwise we would clip to a transparent view.
-                    previousNotificationStart = newYTranslation + child.getClipTopAmount();
-                    previousNotificationEnd = newNotificationEnd;
-                    previousNotificationIsSwiped = child.getTranslationX() != 0;
+                if (state.topOverLap != 0) {
+                    updateChildClip(child, newHeight, state.topOverLap);
+                } else {
+                    child.setClipBounds(null);
                 }
 
                 if(child instanceof SpeedBumpView) {
-                    performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
+                    float speedBumpEnd = newYTranslation + newHeight;
+                    performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
                             newYTranslation);
                 }
             }
         }
     }
 
+    /**
+     * Updates the clipping of a view
+     *
+     * @param child the view to update
+     * @param height the currently applied height of the view
+     * @param clipInset how much should this view be clipped from the top
+     */
+    private void updateChildClip(View child, int height, int clipInset) {
+        mClipRect.set(0,
+                clipInset,
+                child.getWidth(),
+                height);
+        child.setClipBounds(mClipRect);
+    }
+
     private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
             float speedBumpStart) {
         View nextChild = getNextChildNotGone(i);
@@ -209,45 +211,6 @@
         return null;
     }
 
-    /**
-     * Updates the shadow outline and the clipping for a view.
-     *
-     * @param child the view to update
-     * @param realHeight the currently applied height of the view
-     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
-     * @param backgroundHeight the desired background height. The shadows of the view will be
-     *                         based on this height and the content will be clipped from the top
-     */
-    private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
-            float clipHeight, int backgroundHeight) {
-        if (realHeight > clipHeight) {
-            updateChildClip(child, realHeight, clipHeight);
-        } else {
-            child.setClipBounds(null);
-        }
-        if (realHeight > backgroundHeight) {
-            child.setClipTopAmount(realHeight - backgroundHeight);
-        } else {
-            child.setClipTopAmount(0);
-        }
-    }
-
-    /**
-     * Updates the clipping of a view
-     *
-     * @param child the view to update
-     * @param height the currently applied height of the view
-     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
-     */
-    private void updateChildClip(View child, int height, float clipHeight) {
-        int clipInset = (int) (height - clipHeight);
-        mClipRect.set(0,
-                clipInset,
-                child.getWidth(),
-                height);
-        child.setClipBounds(mClipRect);
-    }
-
     public static class ViewState {
 
         // These are flags such that we can create masks for filtering.
@@ -269,6 +232,18 @@
         boolean dimmed;
 
         /**
+         * The amount which the view should be clipped from the top. This is calculated to
+         * perceive consistent shadows.
+         */
+        int clipTopAmount;
+
+        /**
+         * How much does the child overlap with the previous view on the top? Can be used for
+         * a clipping optimization
+         */
+        int topOverLap;
+
+        /**
          * The index of the view, only accounting for views not equal to GONE
          */
         int notGoneIndex;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f019e6c..f41ab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -133,10 +133,11 @@
         boolean scaleChanging = child.getScaleX() != viewState.scale;
         boolean alphaChanging = alpha != child.getAlpha();
         boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
         boolean wasAdded = mNewAddChildren.contains(child);
         boolean hasDelays = mAnimationFilter.hasDelays;
         boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
-                alphaChanging || heightChanging;
+                alphaChanging || heightChanging || topInsetChanging;
         long delay = 0;
         if (hasDelays && isDelayRelevant || wasAdded) {
             delay = calculateChildAnimationDelay(viewState, finalState);
@@ -167,6 +168,11 @@
             startHeightAnimation(child, viewState, delay);
         }
 
+        // start top inset animation
+        if (topInsetChanging) {
+            startInsetAnimation(child, viewState, delay);
+        }
+
         // start dimmed animation
         child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
 
@@ -280,6 +286,64 @@
         child.setTag(TAG_END_HEIGHT, newEndValue);
     }
 
+    private void startInsetAnimation(final ExpandableView child,
+            StackScrollState.ViewState viewState, long delay) {
+        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
+        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
+        int newEndValue = viewState.clipTopAmount;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
+        if (!mAnimationFilter.animateTopInset) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                int relativeDiff = newEndValue - previousEndValue;
+                int newStartValue = previousStartValue + relativeDiff;
+                values[0].setIntValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TOP_INSET, newStartValue);
+                child.setTag(TAG_END_TOP_INSET, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setClipTopAmount(newEndValue);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setClipTopAmount((int) animation.getAnimatedValue());
+            }
+        });
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        animator.setDuration(newDuration);
+        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+            animator.setStartDelay(delay);
+        }
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
+                child.setTag(TAG_START_TOP_INSET, null);
+                child.setTag(TAG_END_TOP_INSET, null);
+            }
+        });
+        startAnimator(animator);
+        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
+        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
+        child.setTag(TAG_END_TOP_INSET, newEndValue);
+    }
+
     private void startAlphaAnimation(final ExpandableView child,
             final StackScrollState.ViewState viewState, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
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 25147b4..c2bd1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,12 +17,12 @@
 package com.android.systemui.statusbar.tv;
 
 import android.os.IBinder;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 
-import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.BaseStatusBar;
 
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
     }
 
     @Override
@@ -58,7 +58,7 @@
     }
 
     @Override
-    protected void removeNotificationInternal(String key) {
+    protected void removeNotificationInternal(String key, Ranking ranking) {
     }
 
     @Override
@@ -117,7 +117,7 @@
     }
 
     @Override
-    protected void updateNotificationIcons() {
+    protected void updateNotifications() {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
new file mode 100644
index 0000000..5ee89253
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
@@ -0,0 +1,24 @@
+/*
+ * 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.volume;
+
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+public interface VolumeComponent {
+    ZenModeController getZenController();
+    void setVolumePanel(VolumePanel panel);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 8657e07..67f3a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -16,14 +16,12 @@
 
 package com.android.systemui.volume;
 
-import com.android.internal.R;
-
 import android.app.AlertDialog;
 import android.app.Dialog;
-import android.content.DialogInterface.OnDismissListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
@@ -42,6 +40,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -49,6 +48,9 @@
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 
+import com.android.internal.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
 import java.util.HashMap;
 
 /**
@@ -57,7 +59,6 @@
  * @hide
  */
 public class VolumePanel extends Handler {
-    private static final String TAG = VolumePanel.class.getSimpleName();
     private static boolean LOGD = false;
 
     private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
@@ -88,33 +89,48 @@
     private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
     private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
     private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
+    private static final int MSG_LAYOUT_DIRECTION = 12;
+    private static final int MSG_ZEN_MODE_CHANGED = 13;
 
     // Pseudo stream type for master volume
     private static final int STREAM_MASTER = -100;
     // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
 
+    private final String mTag;
     protected final Context mContext;
     private final AudioManager mAudioManager;
+    private final ZenModeController mZenController;
     private boolean mRingIsSilent;
-    private boolean mShowCombinedVolumes;
     private boolean mVoiceCapable;
+    private boolean mZenModeCapable;
 
     // True if we want to play tones on the system stream when the master stream is specified.
     private final boolean mPlayMasterStreamTones;
 
-    /** Dialog containing all the sliders */
-    private final Dialog mDialog;
-    /** Dialog's content view */
+
+    /** Volume panel content view */
     private final View mView;
+    /** Dialog hosting the panel, if not embedded */
+    private final Dialog mDialog;
+    /** Parent view hosting the panel, if embedded */
+    private final ViewGroup mParent;
 
     /** The visible portion of the volume overlay */
     private final ViewGroup mPanel;
-    /** Contains the sliders and their touchable icons */
-    private final ViewGroup mSliderGroup;
-    /** The button that expands the dialog to show all sliders */
-    private final View mMoreButton;
-    /** Dummy divider icon that needs to vanish with the more button */
-    private final View mDivider;
+    /** Contains the slider and its touchable icons */
+    private final ViewGroup mSliderPanel;
+    /** The button that expands the dialog to show the zen panel */
+    private final ImageView mExpandButton;
+    /** Dummy divider icon that needs to vanish with the expand button */
+    private final View mExpandDivider;
+    /** The zen mode configuration panel view stub */
+    private final ViewStub mZenPanelStub;
+    /** The zen mode configuration panel view, once inflated */
+    private ZenModePanel mZenPanel;
+    /** Dummy divider icon that needs to vanish with the zen panel */
+    private final View mZenPanelDivider;
+
+    private ZenModePanel.Callback mZenPanelCallback;
 
     /** Currently active stream that shows up at the top of the list of sliders */
     private int mActiveStreamType = -1;
@@ -129,8 +145,8 @@
                 false),
         RingerStream(AudioManager.STREAM_RING,
                 R.string.volume_icon_description_ringer,
-                R.drawable.ic_audio_ring_notif,
-                R.drawable.ic_audio_ring_notif_mute,
+                com.android.systemui.R.drawable.ic_ringer_audible,
+                com.android.systemui.R.drawable.ic_ringer_silent,
                 false),
         VoiceStream(AudioManager.STREAM_VOICE_CALL,
                 R.string.volume_icon_description_incall,
@@ -149,8 +165,8 @@
                 true),
         NotificationStream(AudioManager.STREAM_NOTIFICATION,
                 R.string.volume_icon_description_notification,
-                R.drawable.ic_audio_notification,
-                R.drawable.ic_audio_notification_mute,
+                com.android.systemui.R.drawable.ic_ringer_audible,
+                com.android.systemui.R.drawable.ic_ringer_silent,
                 true),
         // for now, use media resources for master volume
         MasterStream(STREAM_MASTER,
@@ -245,8 +261,11 @@
     }
 
 
-    public VolumePanel(Context context) {
+    public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
+        mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
         mContext = context;
+        mParent = parent;
+        mZenController = zenController;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         // For now, only show master volume if master volume is supported
@@ -258,74 +277,81 @@
                 streamRes.show = (streamRes.streamType == STREAM_MASTER);
             }
         }
-
-        mDialog = new Dialog(context) {
-            @Override
-            public boolean onTouchEvent(MotionEvent event) {
-                if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
-                        sConfirmSafeVolumeDialog == null) {
-                    forceTimeout();
-                    return true;
+        if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
+        final int layoutId = com.android.systemui.R.layout.volume_panel;
+        if (parent == null) {
+            // dialog mode
+            mDialog = new Dialog(context) {
+                @Override
+                public boolean onTouchEvent(MotionEvent event) {
+                    if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
+                            sConfirmSafeVolumeDialog == null) {
+                        forceTimeout();
+                        return true;
+                    }
+                    return false;
                 }
-                return false;
-            }
-        };
+            };
 
-        // Change some window properties
-        final Window window = mDialog.getWindow();
-        final LayoutParams lp = window.getAttributes();
-        lp.token = null;
-        // Offset from the top
-        lp.y = res.getDimensionPixelOffset(R.dimen.volume_panel_top);
-        lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
-        lp.windowAnimations = R.style.Animation_VolumePanel;
-        window.setAttributes(lp);
-        window.setGravity(Gravity.TOP);
-        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        window.requestFeature(Window.FEATURE_NO_TITLE);
-        window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
-                | LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+            // Change some window properties
+            final Window window = mDialog.getWindow();
+            final LayoutParams lp = window.getAttributes();
+            lp.token = null;
+            // Offset from the top
+            lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
+            lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
+            lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
+            lp.windowAnimations = R.style.Animation_VolumePanel;
+            window.setBackgroundDrawableResource(com.android.systemui.R.drawable.qs_panel_background);
+            window.setAttributes(lp);
+            window.setGravity(Gravity.TOP);
+            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            window.requestFeature(Window.FEATURE_NO_TITLE);
+            window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
+                    | LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+            mDialog.setCanceledOnTouchOutside(true);
+            mDialog.setContentView(layoutId);
+            mDialog.setOnDismissListener(new OnDismissListener() {
+                @Override
+                public void onDismiss(DialogInterface dialog) {
+                    mActiveStreamType = -1;
+                    mAudioManager.forceVolumeControlStream(mActiveStreamType);
+                }
+            });
 
-        mDialog.setCanceledOnTouchOutside(true);
-        mDialog.setContentView(R.layout.volume_adjust);
-        mDialog.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                mActiveStreamType = -1;
-                mAudioManager.forceVolumeControlStream(mActiveStreamType);
-            }
-        });
+            mDialog.create();
 
-        mDialog.create();
+            mView = window.findViewById(R.id.content);
+            mView.setOnTouchListener(new View.OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    resetTimeout();
+                    return false;
+                }
+            });
 
-        mView = window.findViewById(R.id.content);
-        mView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                resetTimeout();
-                return false;
-            }
-        });
-
-        mPanel = (ViewGroup) mView.findViewById(R.id.visible_panel);
-        mSliderGroup = (ViewGroup) mView.findViewById(R.id.slider_group);
-        mMoreButton = mView.findViewById(R.id.expand_button);
-        mDivider = mView.findViewById(R.id.expand_button_divider);
+        } else {
+            // embedded mode
+            mDialog = null;
+            mView = LayoutInflater.from(mContext).inflate(layoutId, parent, true);
+        }
+        mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
+        mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
+        mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
+        mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
+        mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
+        mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
 
         mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
 
-        // If we don't want to show multiple volumes, hide the settings button
-        // and divider.
-        mShowCombinedVolumes = !mVoiceCapable && !useMasterVolume;
-        if (!mShowCombinedVolumes) {
-            mMoreButton.setVisibility(View.GONE);
-            mDivider.setVisibility(View.GONE);
-        } else {
-            mMoreButton.setOnClickListener(mClickListener);
-        }
+        mZenModeCapable = !useMasterVolume && mZenController != null;
+        mZenPanelDivider.setVisibility(View.GONE);
+        mExpandButton.setOnClickListener(mClickListener);
+        updateZenMode(mZenController == null ? false : mZenController.isZen());
+        mZenController.addCallback(mZenCallback);
 
         final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
         final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
@@ -334,7 +360,7 @@
         listenToRingerMode();
     }
 
-    public void setLayoutDirection(int layoutDirection) {
+    private void setLayoutDirection(int layoutDirection) {
         mPanel.setLayoutDirection(layoutDirection);
         updateStates();
     }
@@ -406,21 +432,19 @@
             StreamResources streamRes = STREAMS[i];
 
             final int streamType = streamRes.streamType;
-            if (mVoiceCapable && streamRes == StreamResources.NotificationStream) {
-                streamRes = StreamResources.RingerStream;
-            }
 
             final StreamControl sc = new StreamControl();
             sc.streamType = streamType;
-            sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
+            sc.group = (ViewGroup) inflater.inflate(
+                    com.android.systemui.R.layout.volume_panel_item, null);
             sc.group.setTag(sc);
-            sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
+            sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
             sc.icon.setTag(sc);
             sc.icon.setContentDescription(res.getString(streamRes.descRes));
             sc.iconRes = streamRes.iconRes;
             sc.iconMuteRes = streamRes.iconMuteRes;
             sc.icon.setImageResource(sc.iconRes);
-            sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
+            sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
             final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
                     streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
             sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
@@ -431,34 +455,18 @@
     }
 
     private void reorderSliders(int activeStreamType) {
-        mSliderGroup.removeAllViews();
+        mSliderPanel.removeAllViews();
 
         final StreamControl active = mStreamControls.get(activeStreamType);
         if (active == null) {
             Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
             mActiveStreamType = -1;
         } else {
-            mSliderGroup.addView(active.group);
+            mSliderPanel.addView(active.group);
             mActiveStreamType = activeStreamType;
             active.group.setVisibility(View.VISIBLE);
             updateSlider(active);
-        }
-
-        addOtherVolumes();
-    }
-
-    private void addOtherVolumes() {
-        if (!mShowCombinedVolumes) return;
-
-        for (int i = 0; i < STREAMS.length; i++) {
-            // Skip the phone specific ones and the active one
-            final int streamType = STREAMS[i].streamType;
-            if (!STREAMS[i].show || streamType == mActiveStreamType) {
-                continue;
-            }
-            StreamControl sc = mStreamControls.get(streamType);
-            mSliderGroup.addView(sc.group);
-            updateSlider(sc);
+            updateZenMode(mZenController == null ? false : mZenController.isZen());
         }
     }
 
@@ -472,7 +480,7 @@
         if (((sc.streamType == AudioManager.STREAM_RING) ||
                 (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
                 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
-            sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
+            sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
         }
         if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
             // never disable touch interactions for remote playback, the muting is not tied to
@@ -486,32 +494,70 @@
         }
     }
 
+    public void setZenModePanelCallback(ZenModePanel.Callback callback) {
+        mZenPanelCallback = callback;
+    }
+
     private void expand() {
-        final int count = mSliderGroup.getChildCount();
-        for (int i = 0; i < count; i++) {
-            mSliderGroup.getChildAt(i).setVisibility(View.VISIBLE);
+        if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
+        if (mZenPanel == null) {
+            mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
+            mZenPanel.init(mZenController);
+            mZenPanel.setCallback(new ZenModePanel.Callback() {
+                @Override
+                public void onMoreSettings() {
+                    if (mZenPanelCallback != null) {
+                        mZenPanelCallback.onMoreSettings();
+                    }
+                }
+
+                @Override
+                public void onInteraction() {
+                    if (mZenPanelCallback != null) {
+                        mZenPanelCallback.onInteraction();
+                    }
+                }
+            });
         }
-        mMoreButton.setVisibility(View.INVISIBLE);
-        mDivider.setVisibility(View.INVISIBLE);
+        mZenPanel.setVisibility(View.VISIBLE);
+        mZenPanelDivider.setVisibility(View.VISIBLE);
     }
 
     private void collapse() {
-        mMoreButton.setVisibility(View.VISIBLE);
-        mDivider.setVisibility(View.VISIBLE);
-        final int count = mSliderGroup.getChildCount();
-        for (int i = 1; i < count; i++) {
-            mSliderGroup.getChildAt(i).setVisibility(View.GONE);
+        if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
+        if (mZenPanel != null) {
+            mZenPanel.setVisibility(View.GONE);
         }
+        mZenPanelDivider.setVisibility(View.GONE);
     }
 
     public void updateStates() {
-        final int count = mSliderGroup.getChildCount();
+        final int count = mSliderPanel.getChildCount();
         for (int i = 0; i < count; i++) {
-            StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag();
+            StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
             updateSlider(sc);
         }
     }
 
+    private void updateZenMode(boolean zen) {
+        if (mZenModeCapable) {
+            final boolean show = mActiveStreamType == AudioManager.STREAM_NOTIFICATION
+                    || mActiveStreamType == AudioManager.STREAM_RING;
+            mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
+            mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
+            mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
+                    : com.android.systemui.R.drawable.ic_vol_zen_off);
+        } else {
+            mExpandButton.setVisibility(View.GONE);
+            mExpandDivider.setVisibility(View.GONE);
+        }
+    }
+
+    public void postZenModeChanged(boolean zen) {
+        removeMessages(MSG_ZEN_MODE_CHANGED);
+        obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0).sendToTarget();
+    }
+
     public void postVolumeChanged(int streamType, int flags) {
         if (hasMessages(MSG_VOLUME_CHANGED)) return;
         synchronized (this) {
@@ -582,8 +628,12 @@
     }
 
     public void postDismiss() {
-        removeMessages(MSG_TIMEOUT);
-        sendEmptyMessage(MSG_TIMEOUT);
+        forceTimeout();
+    }
+
+    public void postLayoutDirection(int layoutDirection) {
+        removeMessages(MSG_LAYOUT_DIRECTION);
+        obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection).sendToTarget();
     }
 
     /**
@@ -593,7 +643,7 @@
      */
     protected void onVolumeChanged(int streamType, int flags) {
 
-        if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
+        if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
 
         if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
             synchronized (this) {
@@ -622,7 +672,7 @@
 
     protected void onMuteChanged(int streamType, int flags) {
 
-        if (LOGD) Log.d(TAG, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
+        if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
 
         StreamControl sc = mStreamControls.get(streamType);
         if (sc != null) {
@@ -638,7 +688,7 @@
         mRingIsSilent = false;
 
         if (LOGD) {
-            Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+            Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
                     + ", flags: " + flags + "), index: " + index);
         }
 
@@ -707,7 +757,7 @@
             }
 
             case AudioService.STREAM_REMOTE_MUSIC: {
-                if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
+                if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
                 break;
             }
         }
@@ -730,16 +780,18 @@
             }
         }
 
-        if (!mDialog.isShowing()) {
+        if (!isShowing()) {
             int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
             // when the stream is for remote playback, use -1 to reset the stream type evaluation
             mAudioManager.forceVolumeControlStream(stream);
 
             // Showing dialog - use collapsed state
-            if (mShowCombinedVolumes) {
+            if (mZenModeCapable) {
                 collapse();
             }
-            mDialog.show();
+            if (mDialog != null) {
+                mDialog.show();
+            }
         }
 
         // Do a little vibrate if applicable (only when going into vibrate mode)
@@ -751,6 +803,10 @@
         }
     }
 
+    private boolean isShowing() {
+        return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
+    }
+
     protected void onPlaySound(int streamType, int flags) {
 
         if (hasMessages(MSG_STOP_SOUNDS)) {
@@ -795,9 +851,9 @@
         // streamType is the real stream type being affected, but for the UI sliders, we
         // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
         // stream type.
-        if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
+        if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
 
-        if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
+        if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
             synchronized (this) {
                 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
                     reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
@@ -805,7 +861,7 @@
                 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
             }
         } else {
-            if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
+            if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
         }
 
         if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
@@ -825,8 +881,8 @@
     }
 
     protected void onRemoteVolumeUpdateIfShown() {
-        if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
-        if (mDialog.isShowing()
+        if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
+        if (isShowing()
                 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
                 && (mStreamControls != null)) {
             onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
@@ -842,7 +898,7 @@
      * @param visible
      */
     synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
-        if (LOGD) Log.d(TAG, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
+        if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
         boolean isVisible = (visible == 1);
         for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
             StreamResources streamRes = STREAMS[i];
@@ -857,7 +913,7 @@
     }
 
     protected void onDisplaySafeVolumeWarning(int flags) {
-        if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) {
+        if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
             synchronized (sConfirmSafeVolumeLock) {
                 if (sConfirmSafeVolumeDialog != null) {
                     return;
@@ -907,7 +963,7 @@
                     mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
                 } catch (RuntimeException e) {
                     if (LOGD) {
-                        Log.d(TAG, "ToneGenerator constructor failed with "
+                        Log.d(mTag, "ToneGenerator constructor failed with "
                                 + "RuntimeException: " + e);
                     }
                 }
@@ -976,9 +1032,11 @@
             }
 
             case MSG_TIMEOUT: {
-                if (mDialog.isShowing()) {
-                    mDialog.dismiss();
-                    mActiveStreamType = -1;
+                if (isShowing()) {
+                    if (mDialog != null) {
+                        mDialog.dismiss();
+                        mActiveStreamType = -1;
+                    }
                 }
                 synchronized (sConfirmSafeVolumeLock) {
                     if (sConfirmSafeVolumeDialog != null) {
@@ -988,7 +1046,7 @@
                 break;
             }
             case MSG_RINGER_MODE_CHANGED: {
-                if (mDialog.isShowing()) {
+                if (isShowing()) {
                     updateStates();
                 }
                 break;
@@ -1010,17 +1068,30 @@
             case MSG_DISPLAY_SAFE_VOLUME_WARNING:
                 onDisplaySafeVolumeWarning(msg.arg1);
                 break;
+
+            case MSG_LAYOUT_DIRECTION:
+                setLayoutDirection(msg.arg1);
+                break;
+
+            case MSG_ZEN_MODE_CHANGED:
+                updateZenMode(msg.arg1 != 0);
+                break;
         }
     }
 
-    private void resetTimeout() {
+    public void resetTimeout() {
+        if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
         removeMessages(MSG_TIMEOUT);
-        sendMessageDelayed(obtainMessage(MSG_TIMEOUT), TIMEOUT_DELAY);
+        sendEmptyMessageDelayed(MSG_TIMEOUT, TIMEOUT_DELAY);
     }
 
     private void forceTimeout() {
         removeMessages(MSG_TIMEOUT);
-        sendMessage(obtainMessage(MSG_TIMEOUT));
+        sendEmptyMessage(MSG_TIMEOUT);
+    }
+
+    public ZenModeController getZenController() {
+        return mZenController;
     }
 
     private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
@@ -1061,10 +1132,22 @@
     private final View.OnClickListener mClickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            if (v == mMoreButton) {
-                expand();
+            if (v == mExpandButton && mZenController != null) {
+                final boolean newZen = !mZenController.isZen();
+                mZenController.setZen(newZen);
+                if (newZen) {
+                    expand();
+                } else {
+                    collapse();
+                }
             }
             resetTimeout();
         }
     };
+
+    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
+        public void onZenChanged(boolean zen) {
+            updateZenMode(zen);
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 9bd75b7..7da90d8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -1,16 +1,22 @@
 package com.android.systemui.volume;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.IVolumeController;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 
 /*
  * Copyright (C) 2014 The Android Open Source Project
@@ -34,21 +40,21 @@
     private static final Uri SETTING_URI = Settings.Global.getUriFor(SETTING);
     private static final int DEFAULT = 1;  // enabled by default
 
+    private final Handler mHandler = new Handler();
     private AudioManager mAudioManager;
     private VolumeController mVolumeController;
 
     @Override
     public void start() {
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mVolumeController = new VolumeController(mContext);
+        putComponent(VolumeComponent.class, mVolumeController);
         updateController();
         mContext.getContentResolver().registerContentObserver(SETTING_URI, false, mObserver);
     }
 
     private void updateController() {
         if (Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT) != 0) {
-            if (mVolumeController == null) {
-                mVolumeController = new VolumeController(mContext);
-            }
             Log.d(TAG, "Registering volume controller");
             mAudioManager.setVolumeController(mVolumeController);
         } else {
@@ -57,7 +63,7 @@
         }
     }
 
-    private final ContentObserver mObserver = new ContentObserver(new Handler()) {
+    private final ContentObserver mObserver = new ContentObserver(mHandler) {
         public void onChange(boolean selfChange, Uri uri) {
             if (SETTING_URI.equals(uri)) {
                 updateController();
@@ -66,13 +72,38 @@
     };
 
     /** For now, simply host an unmodified base volume panel in this process. */
-    private final class VolumeController extends IVolumeController.Stub {
-        private final VolumePanel mPanel;
+    private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
+        private final VolumePanel mDialogPanel;
+        private VolumePanel mPanel;
 
         public VolumeController(Context context) {
-            mPanel = new VolumePanel(context);
+            mPanel = new VolumePanel(context, null, new ZenModeControllerImpl(mContext, mHandler));
+            final int delay = context.getResources().getInteger(R.integer.feedback_start_delay);
+            mPanel.setZenModePanelCallback(new ZenModePanel.Callback() {
+                @Override
+                public void onMoreSettings() {
+                    mHandler.removeCallbacks(mStartZenSettings);
+                    mHandler.postDelayed(mStartZenSettings, delay);
+                }
+
+                @Override
+                public void onInteraction() {
+                    mDialogPanel.resetTimeout();
+                }
+            });
+            mDialogPanel = mPanel;
         }
 
+        private final Runnable mStartZenSettings = new Runnable() {
+            @Override
+            public void run() {
+                mDialogPanel.postDismiss();
+                final Intent intent = ZenModePanel.ZEN_SETTINGS;
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+            }
+        };
+
         @Override
         public void hasNewRemotePlaybackInfo() throws RemoteException {
             mPanel.postHasNewRemotePlaybackInfo();
@@ -114,12 +145,22 @@
         @Override
         public void setLayoutDirection(int layoutDirection)
                 throws RemoteException {
-            mPanel.setLayoutDirection(layoutDirection);
+            mPanel.postLayoutDirection(layoutDirection);
         }
 
         @Override
         public void dismiss() throws RemoteException {
             mPanel.postDismiss();
         }
+
+        @Override
+        public ZenModeController getZenController() {
+            return mDialogPanel.getZenController();
+        }
+
+        @Override
+        public void setVolumePanel(VolumePanel panel) {
+            mPanel = panel == null ? mDialogPanel : panel;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
new file mode 100644
index 0000000..77d267e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -0,0 +1,248 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class ZenModePanel extends LinearLayout {
+    private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+    public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+
+    private final LayoutInflater mInflater;
+    private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
+    private final H mHandler = new H();
+    private LinearLayout mConditions;
+    private int mMinutesIndex = Arrays.binarySearch(MINUTES, 60);  // default to one hour
+    private Callback mCallback;
+    private ZenModeController mController;
+    private boolean mRequestingConditions;
+
+    public ZenModePanel(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mConditions = (LinearLayout) findViewById(android.R.id.content);
+        findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                fireMoreSettings();
+            }
+        });
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        setRequestingConditions(visibility == VISIBLE);
+    }
+
+    /** Start or stop requesting relevant zen mode exit conditions */
+    private void setRequestingConditions(boolean requesting) {
+        if (mRequestingConditions == requesting) return;
+        mRequestingConditions = requesting;
+        if (mRequestingConditions) {
+            mController.addCallback(mZenCallback);
+        } else {
+            mController.removeCallback(mZenCallback);
+        }
+        mController.requestConditions(mRequestingConditions);
+    }
+
+    public void init(ZenModeController controller) {
+        mController = controller;
+        mConditions.removeAllViews();
+        bind(updateTimeCondition(), mConditions.getChildAt(0));
+        handleUpdateConditions(new Condition[0]);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    private Condition updateTimeCondition() {
+        final int minutes = MINUTES[mMinutesIndex];
+        final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
+        final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
+                .appendPath("countdown").appendPath(Long.toString(millis)).build();
+        final int num = minutes < 60 ? minutes : minutes / 60;
+        final int resId = minutes < 60
+                ? R.plurals.zen_mode_duration_minutes
+                : R.plurals.zen_mode_duration_hours;
+        final String caption = mContext.getResources().getQuantityString(resId, num, num);
+        return new Condition(id, caption, "", "", 0, Condition.STATE_TRUE,
+                Condition.FLAG_RELEVANT_NOW);
+    }
+
+    private void handleUpdateConditions(Condition[] conditions) {
+        final int newCount = conditions == null ? 0 : conditions.length;
+        for (int i = mConditions.getChildCount() - 1; i > newCount; i--) {
+            mConditions.removeViewAt(i);
+        }
+        for (int i = 0; i < newCount; i++) {
+            bind(conditions[i], mConditions.getChildAt(i + 1));
+        }
+        bind(null, mConditions.getChildAt(newCount + 1));
+    }
+
+    private void editTimeCondition(int delta) {
+        final int i = mMinutesIndex + delta;
+        if (i < 0 || i >= MINUTES.length) return;
+        mMinutesIndex = i;
+        final Condition c = updateTimeCondition();
+        bind(c, mConditions.getChildAt(0));
+    }
+
+    private void bind(final Condition condition, View convertView) {
+        final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE;
+        final View row;
+        if (convertView == null) {
+            row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
+            mConditions.addView(row);
+        } else {
+            row = convertView;
+        }
+        final int position = mConditions.indexOfChild(row);
+        final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
+        mRadioButtons.add(rb);
+        rb.setEnabled(enabled);
+        rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    for (RadioButton otherButton : mRadioButtons) {
+                        if (otherButton == rb) continue;
+                        otherButton.setChecked(false);
+                    }
+                    mController.select(condition);
+                    fireInteraction();
+                }
+            }
+        });
+        final TextView title = (TextView) row.findViewById(android.R.id.title);
+        if (condition == null) {
+            title.setText(R.string.zen_mode_forever);
+        } else {
+            title.setText(condition.summary);
+        }
+        title.setEnabled(enabled);
+        title.setAlpha(enabled ? 1 : .5f);
+        final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
+        button1.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                rb.setChecked(true);
+                editTimeCondition(-1);
+                fireInteraction();
+            }
+        });
+
+        final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
+        button2.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                rb.setChecked(true);
+                editTimeCondition(1);
+                fireInteraction();
+            }
+        });
+        title.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                rb.setChecked(true);
+                fireInteraction();
+            }
+        });
+        if (position == 0) {
+            button1.setEnabled(mMinutesIndex > 0);
+            button2.setEnabled(mMinutesIndex < MINUTES.length - 1);
+            button1.setImageAlpha(button1.isEnabled() ? 0xff : 0x7f);
+            button2.setImageAlpha(button2.isEnabled() ? 0xff : 0x7f);
+        } else {
+            button1.setVisibility(View.GONE);
+            button2.setVisibility(View.GONE);
+        }
+        if (position == 0 &&  mConditions.getChildCount() == 1) {
+            rb.setChecked(true);
+        }
+    }
+
+    private void fireMoreSettings() {
+        if (mCallback != null) {
+            mCallback.onMoreSettings();
+        }
+    }
+
+    private void fireInteraction() {
+        if (mCallback != null) {
+            mCallback.onInteraction();
+        }
+    }
+
+    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
+        @Override
+        public void onConditionsChanged(Condition[] conditions) {
+            mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
+        }
+    };
+
+    private final class H extends Handler {
+        private static final int UPDATE_CONDITIONS = 1;
+
+        private H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == UPDATE_CONDITIONS) {
+                handleUpdateConditions((Condition[])msg.obj);
+            }
+        }
+    }
+
+    public interface Callback {
+        void onMoreSettings();
+        void onInteraction();
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2fea785..6b0095a 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -3179,7 +3179,9 @@
                         com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                 layoutResource = res.resourceId;
             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
-                layoutResource = com.android.internal.R.layout.screen_action_bar;
+                layoutResource = a.getResourceId(
+                        com.android.internal.R.styleable.Window_windowActionBarFullscreenDecorLayout,
+                        com.android.internal.R.layout.screen_action_bar);
             } else {
                 layoutResource = com.android.internal.R.layout.screen_title;
             }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 99771934..8e68dfc 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;
@@ -354,6 +355,10 @@
     // the same as mCur*, but may be larger if the screen decor has supplied
     // content insets.
     int mContentLeft, mContentTop, mContentRight, mContentBottom;
+    // During layout, the frame in which voice content should be displayed
+    // to the user, accounting for all screen decoration except for any
+    // space they deem as available for other content.
+    int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom;
     // During layout, the current screen borders along which input method
     // windows are placed.
     int mDockLeft, mDockTop, mDockRight, mDockBottom;
@@ -1261,6 +1266,7 @@
             case TYPE_INPUT_METHOD:
             case TYPE_WALLPAPER:
             case TYPE_PRIVATE_PRESENTATION:
+            case TYPE_VOICE_INTERACTION:
                 // The window manager will check these.
                 break;
             case TYPE_PHONE:
@@ -1427,74 +1433,77 @@
             return 3;
         case TYPE_SEARCH_BAR:
             return 4;
+        case TYPE_VOICE_INTERACTION:
+            // voice interaction layer is almost immediately above apps.
+            return 5;
         case TYPE_RECENTS_OVERLAY:
         case TYPE_SYSTEM_DIALOG:
-            return 5;
+            return 6;
         case TYPE_TOAST:
             // toasts and the plugged-in battery thing
-            return 6;
+            return 7;
         case TYPE_PRIORITY_PHONE:
             // SIM errors and unlock.  Not sure if this really should be in a high layer.
-            return 7;
+            return 8;
         case TYPE_DREAM:
             // used for Dreams (screensavers with TYPE_DREAM windows)
-            return 8;
+            return 9;
         case TYPE_SYSTEM_ALERT:
             // like the ANR / app crashed dialogs
-            return 9;
+            return 10;
         case TYPE_INPUT_METHOD:
             // on-screen keyboards and other such input method user interfaces go here.
-            return 10;
+            return 11;
         case TYPE_INPUT_METHOD_DIALOG:
             // on-screen keyboards and other such input method user interfaces go here.
-            return 11;
+            return 12;
         case TYPE_KEYGUARD_SCRIM:
             // the safety window that shows behind keyguard while keyguard is starting
-            return 12;
-        case TYPE_STATUS_BAR_SUB_PANEL:
             return 13;
-        case TYPE_STATUS_BAR:
+        case TYPE_STATUS_BAR_SUB_PANEL:
             return 14;
-        case TYPE_STATUS_BAR_PANEL:
+        case TYPE_STATUS_BAR:
             return 15;
-        case TYPE_KEYGUARD_DIALOG:
+        case TYPE_STATUS_BAR_PANEL:
             return 16;
+        case TYPE_KEYGUARD_DIALOG:
+            return 17;
         case TYPE_VOLUME_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 17;
+            return 18;
         case TYPE_SYSTEM_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 18;
+            return 19;
         case TYPE_NAVIGATION_BAR:
             // the navigation bar, if available, shows atop most things
-            return 19;
+            return 20;
         case TYPE_NAVIGATION_BAR_PANEL:
             // some panels (e.g. search) need to show on top of the navigation bar
-            return 20;
+            return 21;
         case TYPE_SYSTEM_ERROR:
             // system-level error dialogs
-            return 21;
+            return 22;
         case TYPE_MAGNIFICATION_OVERLAY:
             // used to highlight the magnified portion of a display
-            return 22;
+            return 23;
         case TYPE_DISPLAY_OVERLAY:
             // used to simulate secondary display devices
-            return 23;
+            return 24;
         case TYPE_DRAG:
             // the drag layer: input for drag-and-drop is associated with this window,
             // which sits above all other focusable windows
-            return 24;
-        case TYPE_SECURE_SYSTEM_OVERLAY:
             return 25;
-        case TYPE_BOOT_PROGRESS:
+        case TYPE_SECURE_SYSTEM_OVERLAY:
             return 26;
+        case TYPE_BOOT_PROGRESS:
+            return 27;
         case TYPE_POINTER:
             // the (mouse) pointer layer
-            return 27;
-        case TYPE_HIDDEN_NAV_CONSUMER:
             return 28;
+        case TYPE_HIDDEN_NAV_CONSUMER:
+            return 29;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return 2;
@@ -1563,11 +1572,16 @@
     }
 
     @Override
-    public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) {
+    public boolean doesForceHide(WindowManager.LayoutParams attrs) {
         return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
     }
 
     @Override
+    public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
+        return attrs.type == TYPE_STATUS_BAR;
+    }
+
+    @Override
     public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs) {
         switch (attrs.type) {
             case TYPE_STATUS_BAR:
@@ -1921,9 +1935,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 +2019,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.
@@ -2705,13 +2714,13 @@
         mRestrictedScreenTop = mUnrestrictedScreenTop;
         mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
         mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
-        mDockLeft = mContentLeft = mStableLeft = mStableFullscreenLeft
+        mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
                 = mCurLeft = mUnrestrictedScreenLeft;
-        mDockTop = mContentTop = mStableTop = mStableFullscreenTop
+        mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
                 = mCurTop = mUnrestrictedScreenTop;
-        mDockRight = mContentRight = mStableRight = mStableFullscreenRight
+        mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
                 = mCurRight = displayWidth - overscanRight;
-        mDockBottom = mContentBottom = mStableBottom = mStableFullscreenBottom
+        mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
                 = mCurBottom = displayHeight - overscanBottom;
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
@@ -2822,10 +2831,10 @@
                 }
                 // Make sure the content and current rectangles are updated to
                 // account for the restrictions from the navigation bar.
-                mContentTop = mCurTop = mDockTop;
-                mContentBottom = mCurBottom = mDockBottom;
-                mContentLeft = mCurLeft = mDockLeft;
-                mContentRight = mCurRight = mDockRight;
+                mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+                mContentRight = mVoiceContentRight = mCurRight = mDockRight;
                 mStatusBarLayer = mNavigationBar.getSurfaceLayer();
                 // And compute the final frame.
                 mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
@@ -2872,10 +2881,10 @@
                     // status bar is visible.
                     mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
 
-                    mContentTop = mCurTop = mDockTop;
-                    mContentBottom = mCurBottom = mDockBottom;
-                    mContentLeft = mCurLeft = mDockLeft;
-                    mContentRight = mCurRight = mDockRight;
+                    mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+                    mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+                    mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+                    mContentRight = mVoiceContentRight = mCurRight = mDockRight;
 
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
                         String.format(
@@ -2946,7 +2955,12 @@
                 // Ungh.  So to deal with that, make sure the content frame
                 // we end up using is not covering the IM dock.
                 cf.set(attached.getContentFrameLw());
-                if (attached.getSurfaceLayer() < mDockLayer) {
+                if (attached.isVoiceInteraction()) {
+                    if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft;
+                    if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop;
+                    if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight;
+                    if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom;
+                } else if (attached.getSurfaceLayer() < mDockLayer) {
                     if (cf.left < mContentLeft) cf.left = mContentLeft;
                     if (cf.top < mContentTop) cf.top = mContentTop;
                     if (cf.right > mContentRight) cf.right = mContentRight;
@@ -3154,16 +3168,23 @@
                     }
 
                     if ((fl & FLAG_FULLSCREEN) == 0) {
-                        if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                            cf.left = mDockLeft;
-                            cf.top = mDockTop;
-                            cf.right = mDockRight;
-                            cf.bottom = mDockBottom;
+                        if (win.isVoiceInteraction()) {
+                            cf.left = mVoiceContentLeft;
+                            cf.top = mVoiceContentTop;
+                            cf.right = mVoiceContentRight;
+                            cf.bottom = mVoiceContentBottom;
                         } else {
-                            cf.left = mContentLeft;
-                            cf.top = mContentTop;
-                            cf.right = mContentRight;
-                            cf.bottom = mContentBottom;
+                            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+                                cf.left = mDockLeft;
+                                cf.top = mDockTop;
+                                cf.right = mDockRight;
+                                cf.bottom = mDockBottom;
+                            } else {
+                                cf.left = mContentLeft;
+                                cf.top = mContentTop;
+                                cf.right = mContentRight;
+                                cf.bottom = mContentBottom;
+                            }
                         }
                     } else {
                         // Full screen windows are always given a layout that is as if the
@@ -3375,6 +3396,10 @@
             setLastInputMethodWindowLw(null, null);
             offsetInputMethodWindowLw(win);
         }
+        if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleOrBehindKeyguardLw()
+                && !win.getGivenInsetsPendingLw()) {
+            offsetVoiceInputWindowLw(win);
+        }
     }
 
     private void offsetInputMethodWindowLw(WindowState win) {
@@ -3383,6 +3408,9 @@
         if (mContentBottom > top) {
             mContentBottom = top;
         }
+        if (mVoiceContentBottom > top) {
+            mVoiceContentBottom = top;
+        }
         top = win.getVisibleFrameLw().top;
         top += win.getGivenVisibleInsetsLw().top;
         if (mCurBottom > top) {
@@ -3393,6 +3421,40 @@
                 + mContentBottom + " mCurBottom=" + mCurBottom);
     }
 
+    private void offsetVoiceInputWindowLw(WindowState win) {
+        final int gravity = win.getAttrs().gravity;
+        switch (gravity&((Gravity.AXIS_PULL_BEFORE|Gravity.AXIS_PULL_AFTER)
+                << Gravity.AXIS_X_SHIFT)) {
+            case Gravity.AXIS_PULL_BEFORE<<Gravity.AXIS_X_SHIFT: {
+                int right = win.getContentFrameLw().right - win.getGivenContentInsetsLw().right;
+                if (mVoiceContentLeft < right) {
+                    mVoiceContentLeft = right;
+                }
+            } break;
+            case Gravity.AXIS_PULL_AFTER<<Gravity.AXIS_X_SHIFT: {
+                int left = win.getContentFrameLw().left - win.getGivenContentInsetsLw().left;
+                if (mVoiceContentRight < left) {
+                    mVoiceContentRight = left;
+                }
+            } break;
+        }
+        switch (gravity&((Gravity.AXIS_PULL_BEFORE|Gravity.AXIS_PULL_AFTER)
+                << Gravity.AXIS_Y_SHIFT)) {
+            case Gravity.AXIS_PULL_BEFORE<<Gravity.AXIS_Y_SHIFT: {
+                int bottom = win.getContentFrameLw().bottom - win.getGivenContentInsetsLw().bottom;
+                if (mVoiceContentTop < bottom) {
+                    mVoiceContentTop = bottom;
+                }
+            } break;
+            case Gravity.AXIS_PULL_AFTER<<Gravity.AXIS_Y_SHIFT: {
+                int top = win.getContentFrameLw().top - win.getGivenContentInsetsLw().top;
+                if (mVoiceContentBottom < top) {
+                    mVoiceContentBottom = top;
+                }
+            } break;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     public void finishLayoutLw() {
@@ -3957,37 +4019,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 +4062,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 +4101,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 +4146,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 +4181,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;
                         }
                     }
                 }
@@ -4523,6 +4565,18 @@
         }
     }
 
+    @Override
+    public void startKeyguardExitAnimation(final long fadeoutDuration) {
+        if (mKeyguardDelegate != null) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mKeyguardDelegate.startKeyguardExitAnimation(fadeoutDuration);
+                }
+            });
+        }
+    }
+
     void sendCloseSystemWindows() {
         sendCloseSystemWindows(mContext, null);
     }
@@ -5496,6 +5550,10 @@
                 pw.print(","); pw.print(mContentTop);
                 pw.print(")-("); pw.print(mContentRight);
                 pw.print(","); pw.print(mContentBottom); pw.println(")");
+        pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft);
+                pw.print(","); pw.print(mVoiceContentTop);
+                pw.print(")-("); pw.print(mVoiceContentRight);
+                pw.print(","); pw.print(mVoiceContentBottom); pw.println(")");
         pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft);
                 pw.print(","); pw.print(mDockTop);
                 pw.print(")-("); pw.print(mDockRight);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index 966924b..faf7020 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -274,6 +274,12 @@
         mKeyguardState.currentUser = newUserId;
     }
 
+    public void startKeyguardExitAnimation(long fadeoutDuration) {
+        if (mKeyguardService != null) {
+            mKeyguardService.startKeyguardExitAnimation(fadeoutDuration);
+        }
+    }
+
     private static final View createScrim(Context context) {
         View view = new View(context);
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index 7cb48fa..f236ce7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -190,6 +190,14 @@
         }
     }
 
+    public void startKeyguardExitAnimation(long fadeoutDuration) {
+        try {
+            mService.startKeyguardExitAnimation(fadeoutDuration);
+        } catch (RemoteException e) {
+            Slog.w(TAG , "Remote Exception", e);
+        }
+    }
+
     public void showAssistant() {
         // Not used by PhoneWindowManager
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7ecf248..5527528 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;
@@ -5647,16 +5606,23 @@
         boolean isNewDefault = false;
         if (DBG) log("handleConnectionValidated for "+newNetwork.name());
         // check if any NetworkRequest wants this NetworkAgent
-        // first check if it satisfies the NetworkCapabilities
         ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
         if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities);
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+            if (newNetwork == currentNetwork) {
+                if (VDBG) log("Network " + newNetwork.name() + " was already satisfying" +
+                              " request " + nri.request.requestId + ". No change.");
+                keep = true;
+                continue;
+            }
+
+            // check if it satisfies the NetworkCapabilities
             if (VDBG) log("  checking if request is satisfied: " + nri.request);
             if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
                     newNetwork.networkCapabilities)) {
                 // next check if it's better than any current network we're using for
                 // this request
-                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
                 if (VDBG) {
                     log("currentScore = " +
                             (currentNetwork != null ? currentNetwork.currentScore : 0) +
@@ -5785,12 +5751,19 @@
         }
 
         if (state == NetworkInfo.State.CONNECTED) {
-            // TODO - check if we want it (optimization)
             try {
+                // This is likely caused by the fact that this network already
+                // exists. An example is when a network goes from CONNECTED to
+                // CONNECTING and back (like wifi on DHCP renew).
+                // TODO: keep track of which networks we've created, or ask netd
+                // to tell us whether we've already created this network or not.
                 mNetd.createNetwork(networkAgent.network.netId);
             } catch (Exception e) {
-                loge("Error creating Network " + networkAgent.network.netId);
+                loge("Error creating network " + networkAgent.network.netId + ": "
+                        + e.getMessage());
+                return;
             }
+
             updateLinkProperties(networkAgent, null);
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac30319..fc808ec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -409,7 +409,7 @@
     /**
      * List of intents that were used to start the most recent tasks.
      */
-    final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
+    ArrayList<TaskRecord> mRecentTasks;
 
     public class PendingAssistExtras extends Binder implements Runnable {
         public final ActivityRecord activity;
@@ -822,6 +822,11 @@
     final AppOpsService mAppOpsService;
 
     /**
+     * Save recent tasks information across reboots.
+     */
+    final TaskPersister mTaskPersister;
+
+    /**
      * Current configuration information.  HistoryRecord objects are given
      * a reference to this object to indicate which configuration they are
      * currently running in, so this object must be kept immutable.
@@ -2138,6 +2143,7 @@
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
         mStackSupervisor = new ActivityStackSupervisor(this);
+        mTaskPersister = new TaskPersister(systemDir, mStackSupervisor);
 
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
@@ -7079,14 +7085,18 @@
      * Creates a new RecentTaskInfo from a TaskRecord.
      */
     private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
+        // Update the task description to reflect any changes in the task stack
+        tr.updateTaskDescription();
+
+        // Compose the recent task info
         ActivityManager.RecentTaskInfo rti
                 = new ActivityManager.RecentTaskInfo();
-        rti.id = tr.numActivities > 0 ? tr.taskId : -1;
+        rti.id = tr.mActivities.isEmpty() ? -1 : tr.taskId;
         rti.persistentId = tr.taskId;
         rti.baseIntent = new Intent(tr.getBaseIntent());
         rti.origActivity = tr.origActivity;
         rti.description = tr.lastDescription;
-        rti.stackId = tr.stack.mStackId;
+        rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
         rti.userId = tr.userId;
         rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
         return rti;
@@ -7320,6 +7330,9 @@
         if (tr != null) {
             tr.removeTaskActivitiesLocked(-1, false);
             cleanUpRemovedTaskLocked(tr, flags);
+            if (tr.isPersistable) {
+                notifyTaskPersisterLocked(tr, true);
+            }
             return true;
         }
         return false;
@@ -7559,14 +7572,11 @@
         try {
             synchronized (this) {
                 TaskRecord tr = recentTaskForIdLocked(taskId);
-                if (tr != null) {
-                    return tr.stack.isHomeStack();
-                }
+                return tr != null && tr.stack != null && tr.stack.isHomeStack();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        return false;
     }
 
     @Override
@@ -8635,6 +8645,10 @@
         }
     }
 
+    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+        mTaskPersister.notify(task, flush);
+    }
+
     @Override
     public boolean shutdown(int timeout) {
         if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
@@ -8657,6 +8671,7 @@
         synchronized (this) {
             mProcessStats.shutdownLocked();
         }
+        notifyTaskPersisterLocked(null, true);
 
         return timedout;
     }
@@ -9562,7 +9577,15 @@
                 if (goingCallback != null) goingCallback.run();
                 return;
             }
-            
+
+            if (mRecentTasks == null) {
+                mRecentTasks = mTaskPersister.restoreTasksLocked();
+                if (!mRecentTasks.isEmpty()) {
+                    mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
+                }
+                mTaskPersister.startPersisting();
+            }
+
             // Check to see if there are any update receivers to run.
             if (!mDidUpdate) {
                 if (mWaitingUpdate) {
@@ -17179,7 +17202,7 @@
 
     /**
      * An implementation of IAppTask, that allows an app to manage its own tasks via
-     * {@link android.app.ActivityManager#AppTask}.  We keep track of the callingUid to ensure that
+     * {@link android.app.ActivityManager.AppTask}.  We keep track of the callingUid to ensure that
      * only the process that calls getAppTasks() can call the AppTask methods.
      */
     class AppTaskImpl extends IAppTask.Stub {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index dbe2ca1..b429b93 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,14 +16,15 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManager.TaskDescription;
 import android.os.PersistableBundle;
 import android.os.Trace;
 import com.android.internal.app.ResolverActivity;
+import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
 
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ResultInfo;
 import android.content.ComponentName;
@@ -48,7 +49,11 @@
 import android.util.TimeUtils;
 import android.view.IApplicationToken;
 import android.view.WindowManager;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -62,6 +67,19 @@
     static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
     final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recent";
 
+    private static final String TAG_ACTIVITY = "activity";
+    private static final String ATTR_ID = "id";
+    private static final String TAG_INTENT = "intent";
+    private static final String ATTR_USERID = "user_id";
+    private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
+    private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
+    private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+    private static final String ATTR_RESOLVEDTYPE = "resolved_type";
+    private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
+    private static final String ATTR_TASKDESCRIPTIONLABEL = "task_description_label";
+    private static final String ATTR_TASKDESCRIPTIONCOLOR = "task_description_color";
+    private static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
+
     final ActivityManagerService service; // owner
     final IApplicationToken.Stub appToken; // window manager token
     final ActivityInfo info; // all about me
@@ -97,6 +115,7 @@
     int windowFlags;        // custom window flags for preview window.
     TaskRecord task;        // the task this is in.
     ThumbnailHolder thumbHolder; // where our thumbnails should go.
+    long createTime = System.currentTimeMillis();
     long displayStartTime;  // when we started launching this activity
     long fullyDrawnStartTime; // when we started launching this activity
     long startTime;         // last time this activity was started
@@ -149,7 +168,7 @@
     boolean mStartingWindowShown = false;
     ActivityContainer mInitialActivityContainer;
 
-    ActivityManager.TaskDescription taskDescription; // the recents information for this activity
+    TaskDescription taskDescription; // the recents information for this activity
 
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
@@ -490,14 +509,6 @@
                         (newTask == null ? null : newTask.stack));
             }
         }
-        if (inHistory && !finishing) {
-            if (task != null) {
-                task.numActivities--;
-            }
-            if (newTask != null) {
-                newTask.numActivities++;
-            }
-        }
         if (newThumbHolder == null) {
             newThumbHolder = newTask;
         }
@@ -527,9 +538,6 @@
     void putInHistory() {
         if (!inHistory) {
             inHistory = true;
-            if (task != null && !finishing) {
-                task.numActivities++;
-            }
         }
     }
 
@@ -537,7 +545,6 @@
         if (inHistory) {
             inHistory = false;
             if (task != null && !finishing) {
-                task.numActivities--;
                 task = null;
             }
             clearOptionsLocked();
@@ -560,12 +567,13 @@
         return mActivityType == APPLICATION_ACTIVITY_TYPE;
     }
 
+    boolean isPersistable() {
+        return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+    }
+
     void makeFinishing() {
         if (!finishing) {
             finishing = true;
-            if (task != null && inHistory) {
-                task.numActivities--;
-            }
             if (stopped) {
                 clearOptionsLocked();
             }
@@ -767,6 +775,9 @@
                         "Setting thumbnail of " + this + " holder " + thumbHolder
                         + " to " + newThumbnail);
                 thumbHolder.lastThumbnail = newThumbnail;
+                if (isPersistable()) {
+                    mStackSupervisor.mService.notifyTaskPersisterLocked(task, false);
+                }
             }
             thumbHolder.lastDescription = description;
         }
@@ -1042,7 +1053,134 @@
         return null;
     }
 
-    private String activityTypeToString(int type) {
+    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        out.attribute(null, ATTR_ID, String.valueOf(createTime));
+        out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
+        if (launchedFromPackage != null) {
+            out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
+        }
+        if (resolvedType != null) {
+            out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
+        }
+        out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
+        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        if (taskDescription != null) {
+            final String label = taskDescription.getLabel();
+            if (label != null) {
+                out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, label);
+            }
+            final int colorPrimary = taskDescription.getPrimaryColor();
+            if (colorPrimary != 0) {
+                out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR, Integer.toHexString(colorPrimary));
+            }
+            final Bitmap icon = taskDescription.getIcon();
+            if (icon != null) {
+                TaskPersister.saveImage(icon, String.valueOf(task.taskId) + ACTIVITY_ICON_SUFFIX +
+                        createTime);
+            }
+        }
+
+        out.startTag(null, TAG_INTENT);
+        intent.saveToXml(out);
+        out.endTag(null, TAG_INTENT);
+
+        if (isPersistable() && persistentState != null) {
+            out.startTag(null, TAG_PERSISTABLEBUNDLE);
+            persistentState.saveToXml(out);
+            out.endTag(null, TAG_PERSISTABLEBUNDLE);
+        }
+    }
+
+    static ActivityRecord restoreFromXml(XmlPullParser in, int taskId,
+            ActivityStackSupervisor stackSupervisor) throws IOException, XmlPullParserException {
+        Intent intent = null;
+        PersistableBundle persistentState = null;
+        int launchedFromUid = 0;
+        String launchedFromPackage = null;
+        String resolvedType = null;
+        boolean componentSpecified = false;
+        int userId = 0;
+        String activityLabel = null;
+        int activityColor = 0;
+        long createTime = -1;
+        final int outerDepth = in.getDepth();
+
+        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+            final String attrName = in.getAttributeName(attrNdx);
+            final String attrValue = in.getAttributeValue(attrNdx);
+            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "ActivityRecord: attribute name=" +
+                    attrName + " value=" + attrValue);
+            if (ATTR_ID.equals(attrName)) {
+                createTime = Long.valueOf(attrValue);
+            } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
+                launchedFromUid = Integer.valueOf(attrValue);
+            } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
+                launchedFromPackage = attrValue;
+            } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
+                resolvedType = attrValue;
+            } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
+                componentSpecified = Boolean.valueOf(attrValue);
+            } else if (ATTR_USERID.equals(attrName)) {
+                userId = Integer.valueOf(attrValue);
+            } else if (ATTR_TASKDESCRIPTIONLABEL.equals(attrName)) {
+                activityLabel = attrValue;
+            } else if (ATTR_TASKDESCRIPTIONCOLOR.equals(attrName)) {
+                activityColor = (int) Long.parseLong(attrValue, 16);
+            } else {
+                Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
+            }
+        }
+
+        int event;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                final String name = in.getName();
+                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                        "ActivityRecord: START_TAG name=" + name);
+                if (TAG_INTENT.equals(name)) {
+                    intent = Intent.restoreFromXml(in);
+                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                            "ActivityRecord: intent=" + intent);
+                } else if (TAG_PERSISTABLEBUNDLE.equals(name)) {
+                    persistentState = PersistableBundle.restoreFromXml(in);
+                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                            "ActivityRecord: persistentState=" + persistentState);
+                } else {
+                    Slog.w(TAG, "restoreActivity: unexpected name=" + name);
+                    XmlUtils.skipCurrentTag(in);
+                }
+            }
+        }
+
+        if (intent == null) {
+            throw new XmlPullParserException("restoreActivity error intent=" + intent);
+        }
+
+        final ActivityManagerService service = stackSupervisor.mService;
+        final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
+                null, userId);
+        if (aInfo == null) {
+            throw new XmlPullParserException("restoreActivity resolver error.");
+        }
+        final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
+                launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
+                null, null, 0, componentSpecified, stackSupervisor, null, null);
+
+        r.persistentState = persistentState;
+
+        Bitmap icon = null;
+        if (createTime >= 0) {
+            icon = TaskPersister.restoreImage(String.valueOf(taskId) + ACTIVITY_ICON_SUFFIX +
+                    createTime);
+        }
+        r.taskDescription = new TaskDescription(activityLabel, icon, activityColor);
+        r.createTime = createTime;
+
+        return r;
+    }
+
+    private static String activityTypeToString(int type) {
         switch (type) {
             case APPLICATION_ACTIVITY_TYPE: return "APPLICATION_ACTIVITY_TYPE";
             case HOME_ACTIVITY_TYPE: return "HOME_ACTIVITY_TYPE";
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 33e59a7..1804d03 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;
     }
 
@@ -863,7 +880,10 @@
         final ActivityRecord r = isInStackLocked(token);
         if (r != null) {
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-            r.persistentState = persistentState;
+            if (persistentState != null) {
+                r.persistentState = persistentState;
+                mService.notifyTaskPersisterLocked(r.task, false);
+            }
             if (mPausingActivity == r) {
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
                         + (timeout ? " (due to timeout)" : " (pause complete)"));
@@ -885,7 +905,10 @@
             mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
             return;
         }
-        r.persistentState = persistentState;
+        if (persistentState != null) {
+            r.persistentState = persistentState;
+            mService.notifyTaskPersisterLocked(r.task, false);
+        }
         if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle);
         if (icicle != null) {
             // If icicle is null, this is happening due to a timeout, so we
@@ -1032,6 +1055,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();
+        }
     }
 
     /**
@@ -1821,6 +1850,7 @@
             ++stackNdx;
         }
         mTaskHistory.add(stackNdx, task);
+        updateTaskMovement(task, true);
     }
 
     final void startActivityLocked(ActivityRecord r, boolean newTask,
@@ -1852,7 +1882,7 @@
                         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                 (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                                r.userId, r.info.configChanges);
+                                r.userId, r.info.configChanges, task.voiceSession != null);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
                         }
@@ -1913,7 +1943,7 @@
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges);
+                    r.info.configChanges, task.voiceSession != null);
             boolean doShow = true;
             if (newTask) {
                 // Even though this activity is starting fresh, we still need
@@ -1958,7 +1988,7 @@
             mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                     r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges);
+                    r.info.configChanges, task.voiceSession != null);
             ActivityOptions.abort(options);
             options = null;
         }
@@ -3138,6 +3168,18 @@
         mWindowManager.prepareAppTransition(transit, false);
     }
 
+    void updateTaskMovement(TaskRecord task, boolean toFront) {
+        if (task.isPersistable) {
+            task.mLastTimeMoved = System.currentTimeMillis();
+            // Sign is used to keep tasks sorted when persisted. Tasks sent to the bottom most
+            // recently will be most negative, tasks sent to the bottom before that will be less
+            // negative. Similarly for recent tasks moved to the top which will be most positive.
+            if (!toFront) {
+                task.mLastTimeMoved *= -1;
+            }
+        }
+    }
+
     void moveHomeTaskToTop() {
         final int top = mTaskHistory.size() - 1;
         for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
@@ -3146,6 +3188,7 @@
                 if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
                 mTaskHistory.remove(taskNdx);
                 mTaskHistory.add(top, task);
+                updateTaskMovement(task, true);
                 mWindowManager.moveTaskToTop(task.taskId);
                 return;
             }
@@ -3247,10 +3290,10 @@
 
         mTaskHistory.remove(tr);
         mTaskHistory.add(0, tr);
+        updateTaskMovement(tr, false);
 
         // There is an assumption that moving a task to the back moves it behind the home activity.
         // We make sure here that some activity in the stack will launch home.
-        ActivityRecord lastActivity = null;
         int numTasks = mTaskHistory.size();
         for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -3727,6 +3770,7 @@
             mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
         }
         mTaskHistory.remove(task);
+        updateTaskMovement(task, true);
 
         if (task.mActivities.isEmpty()) {
             final boolean isVoiceSession = task.voiceSession != null;
@@ -3758,7 +3802,8 @@
     TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             boolean toTop) {
-        TaskRecord task = new TaskRecord(taskId, info, intent, voiceSession, voiceInteractor);
+        TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
+                voiceInteractor);
         addTask(task, toTop, false);
         return task;
     }
@@ -3773,6 +3818,7 @@
             insertTaskAtTop(task);
         } else {
             mTaskHistory.add(0, task);
+            updateTaskMovement(task, false);
         }
         if (!moving && task.voiceSession != null) {
             try {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 252c0bb..0b1c2b8 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;
 
@@ -370,6 +371,12 @@
         return null;
     }
 
+    void setNextTaskId(int taskId) {
+        if (taskId > mCurTaskId) {
+            mCurTaskId = taskId;
+        }
+    }
+
     int getNextTaskId() {
         do {
             mCurTaskId++;
@@ -1194,8 +1201,7 @@
             requestCode = sourceRecord.requestCode;
             sourceRecord.resultTo = null;
             if (resultRecord != null) {
-                resultRecord.removeResultsLocked(
-                    sourceRecord, resultWho, requestCode);
+                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
             }
             if (sourceRecord.launchedFromUid == callingUid) {
                 // The new activity is being launched from the same uid as the previous
@@ -1367,7 +1373,7 @@
         return err;
     }
 
-    ActivityStack adjustStackFocus(ActivityRecord r) {
+    ActivityStack adjustStackFocus(ActivityRecord r, boolean newTask) {
         final TaskRecord task = r.task;
         if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) {
             if (task != null) {
@@ -1392,7 +1398,8 @@
                 return container.mStack;
             }
 
-            if (mFocusedStack != mHomeStack) {
+            if (mFocusedStack != mHomeStack && (!newTask ||
+                    mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
                         "adjustStackFocus: Have a focused stack=" + mFocusedStack);
                 return mFocusedStack;
@@ -1445,7 +1452,7 @@
 
         // We'll invoke onUserLeaving before onPause only if the launching
         // activity did not explicitly state that this is an automated launch.
-        mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+        mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
         if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving);
 
         // If the caller has asked not to resume at this point, we make note
@@ -1455,7 +1462,8 @@
             r.delayedResume = true;
         }
 
-        ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
+        ActivityRecord notTop =
+                (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
 
         // If the onlyIfNeeded flag is set, then we can do this if the activity
         // being launched is the same as the one making the call...  or, as
@@ -1478,9 +1486,11 @@
             case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
                 intent.addFlags(
                         Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                launchFlags = intent.getFlags();
                 break;
             case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+                launchFlags = intent.getFlags();
                 break;
         }
         final boolean newDocument = intent.isDocument();
@@ -1786,7 +1796,8 @@
                 Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
-            targetStack = adjustStackFocus(r);
+            newTask = true;
+            targetStack = adjustStackFocus(r, newTask);
             targetStack.moveToFront();
             if (reuseTask == null) {
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(),
@@ -1798,7 +1809,6 @@
             } else {
                 r.setTask(reuseTask, reuseTask, true);
             }
-            newTask = true;
             if (!movedHome) {
                 if ((launchFlags &
                         (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
@@ -1866,7 +1876,7 @@
             // This not being started from an existing activity, and not part
             // of a new task...  just put it in the top task, though these days
             // this case should never happen.
-            targetStack = adjustStackFocus(r);
+            targetStack = adjustStackFocus(r, newTask);
             targetStack.moveToFront();
             ActivityRecord prev = targetStack.topActivity();
             r.setTask(prev != null ? prev.task
@@ -2250,6 +2260,26 @@
         return mLastStackId;
     }
 
+    void createStackForRestoredTaskHistory(ArrayList<TaskRecord> tasks) {
+        int stackId = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
+        final ActivityStack stack = getStack(stackId);
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            stack.addTask(task, false, false);
+            final int taskId = task.taskId;
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                mWindowManager.addAppToken(0, r.appToken, taskId, stackId,
+                        r.info.screenOrientation, r.fullscreen,
+                        (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+                        r.userId, r.info.configChanges, task.voiceSession != null);
+            }
+            mWindowManager.addTask(taskId, stackId, false);
+        }
+        resumeHomeActivity(null);
+    }
+
     void moveTaskToStack(int taskId, int stackId, boolean toTop) {
         final TaskRecord task = anyTaskForIdLocked(taskId);
         if (task == null) {
@@ -2273,7 +2303,12 @@
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 if (!r.isApplicationActivity() && !stack.isHomeStack()) {
-                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
+                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (home activity) " + stack);
+                    continue;
+                }
+                if (!stack.mActivityContainer.isEligibleForNewTasks()) {
+                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (new task not allowed) " +
+                            stack);
                     continue;
                 }
                 final ActivityRecord ar = stack.findTaskLocked(r);
@@ -3204,6 +3239,11 @@
         void setDrawn() {
         }
 
+        // You can always start a new task on a regular ActivityStack.
+        boolean isEligibleForNewTasks() {
+            return true;
+        }
+
         @Override
         public String toString() {
             return mIdString + (mActivityDisplay == null ? "N" : "A");
@@ -3284,6 +3324,12 @@
             }
         }
 
+        // Never start a new task on an ActivityView if it isn't explicitly specified.
+        @Override
+        boolean isEligibleForNewTasks() {
+            return false;
+        }
+
         private void setSurfaceIfReady() {
             if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
                     " mContainerState=" + mContainerState + " mSurface=" + mSurface);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
new file mode 100644
index 0000000..3bfaca9
--- /dev/null
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -0,0 +1,353 @@
+/*
+ * 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.server.am;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Debug;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class TaskPersister {
+    static final String TAG = "TaskPersister";
+    static final boolean DEBUG = false;
+
+    /** When in slow mode don't write tasks out faster than this */
+    private static final long INTER_TASK_DELAY_MS = 60000;
+    private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
+
+    private static final String RECENTS_FILENAME = "_task";
+    private static final String TASKS_DIRNAME = "recent_tasks";
+    private static final String TASK_EXTENSION = ".xml";
+    private static final String IMAGES_DIRNAME = "recent_images";
+    private static final String IMAGE_EXTENSION = ".png";
+
+    private static final String TAG_TASK = "task";
+
+    private static File sImagesDir;
+    private static File sTasksDir;
+
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mStackSupervisor;
+
+    private boolean mRecentsChanged = false;
+
+    private final LazyTaskWriterThread mLazyTaskWriterThread;
+
+    TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
+        sTasksDir = new File(systemDir, TASKS_DIRNAME);
+        if (!sTasksDir.exists()) {
+            if (!sTasksDir.mkdir()) {
+                Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
+            }
+        }
+
+        sImagesDir = new File(systemDir, IMAGES_DIRNAME);
+        if (!sImagesDir.exists()) {
+            if (!sImagesDir.mkdir()) {
+                Slog.e(TAG, "Failure creating images directory " + sImagesDir);
+            }
+        }
+
+        mStackSupervisor = stackSupervisor;
+        mService = stackSupervisor.mService;
+
+        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+    }
+
+    void startPersisting() {
+        mLazyTaskWriterThread.start();
+    }
+
+    public void notify(TaskRecord task, boolean flush) {
+        if (DEBUG) Slog.d(TAG, "notify: task=" + task + " flush=" + flush +
+                " Callers=" + Debug.getCallers(4));
+        if (task != null) {
+            task.needsPersisting = true;
+        }
+        synchronized (this) {
+            mLazyTaskWriterThread.mSlow = !flush;
+            mRecentsChanged = true;
+            notifyAll();
+        }
+    }
+
+    private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
+        if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+        final XmlSerializer xmlSerializer = new FastXmlSerializer();
+        StringWriter stringWriter = new StringWriter();
+        xmlSerializer.setOutput(stringWriter);
+
+        if (DEBUG) xmlSerializer.setFeature(
+                    "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+        // save task
+        xmlSerializer.startDocument(null, true);
+
+        xmlSerializer.startTag(null, TAG_TASK);
+        task.saveToXml(xmlSerializer);
+        xmlSerializer.endTag(null, TAG_TASK);
+
+        xmlSerializer.endDocument();
+        xmlSerializer.flush();
+
+        return stringWriter;
+    }
+
+    static void saveImage(Bitmap image, String filename) throws IOException {
+        if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename);
+        FileOutputStream imageFile = null;
+        try {
+            imageFile = new FileOutputStream(new File(sImagesDir, filename + IMAGE_EXTENSION));
+            image.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+        } catch (Exception e) {
+            Slog.e(TAG, "saveImage: unable to save " + filename, e);
+        } finally {
+            if (imageFile != null) {
+                imageFile.close();
+            }
+        }
+    }
+
+    ArrayList<TaskRecord> restoreTasksLocked() {
+        final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+        ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
+
+        File[] recentFiles = sTasksDir.listFiles();
+        if (recentFiles == null) {
+            Slog.e(TAG, "Unable to list files from " + sTasksDir);
+            return tasks;
+        }
+
+        for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
+            File taskFile = recentFiles[taskNdx];
+            if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+            BufferedReader reader = null;
+            boolean deleteFile = false;
+            try {
+                reader = new BufferedReader(new FileReader(taskFile));
+                final XmlPullParser in = Xml.newPullParser();
+                in.setInput(reader);
+
+                int event;
+                while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                        event != XmlPullParser.END_TAG) {
+                    final String name = in.getName();
+                    if (event == XmlPullParser.START_TAG) {
+                        if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
+                        if (TAG_TASK.equals(name)) {
+                            final TaskRecord task =
+                                    TaskRecord.restoreFromXml(in, mStackSupervisor);
+                            if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task);
+                            if (task != null) {
+                                tasks.add(task);
+                                final int taskId = task.taskId;
+                                recoveredTaskIds.add(taskId);
+                                mStackSupervisor.setNextTaskId(taskId);
+                            }
+                        } else {
+                            Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name="
+                                    + name);
+                        }
+                    }
+                    XmlUtils.skipCurrentTag(in);
+                }
+            } catch (Exception e) {
+                Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error " + e);
+                deleteFile = true;
+            } finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (IOException e) {
+                    }
+                }
+                if (!DEBUG && deleteFile) {
+                    taskFile.delete();
+                }
+            }
+        }
+
+        if (!DEBUG) {
+            removeObsoleteFiles(recoveredTaskIds);
+        }
+
+        TaskRecord[] tasksArray = new TaskRecord[tasks.size()];
+        tasks.toArray(tasksArray);
+        Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
+            @Override
+            public int compare(TaskRecord lhs, TaskRecord rhs) {
+                final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved;
+                if (diff < 0) {
+                    return -1;
+                } else if (diff > 0) {
+                    return +1;
+                } else {
+                    return 0;
+                }
+            }
+        });
+
+        return new ArrayList<TaskRecord>(Arrays.asList(tasksArray));
+    }
+
+    private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
+        for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
+            File file = files[fileNdx];
+            String filename = file.getName();
+            final int taskIdEnd = filename.indexOf('_') + 1;
+            if (taskIdEnd > 0) {
+                final int taskId;
+                try {
+                    taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
+                } catch (Exception e) {
+                    if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" +
+                            file.getName());
+                    file.delete();
+                    continue;
+                }
+                if (!persistentTaskIds.contains(taskId)) {
+                    if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
+                    file.delete();
+                }
+            }
+        }
+    }
+
+    private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
+        removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles());
+        removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles());
+    }
+
+    static Bitmap restoreImage(String filename) {
+        if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
+        return BitmapFactory.decodeFile(sImagesDir + File.separator + filename + IMAGE_EXTENSION);
+    }
+
+    private class LazyTaskWriterThread extends Thread {
+        boolean mSlow = true;
+
+        LazyTaskWriterThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void run() {
+            ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
+            while (true) {
+                // If mSlow, then delay between each call to saveToXml().
+                synchronized (TaskPersister.this) {
+                    long now = SystemClock.uptimeMillis();
+                    final long releaseTime =
+                            now + (DEBUG ? DEBUG_INTER_TASK_DELAY_MS: INTER_TASK_DELAY_MS);
+                    while (mSlow && now < releaseTime) {
+                        try {
+                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
+                                    (releaseTime - now));
+                            TaskPersister.this.wait(releaseTime - now);
+                        } catch (InterruptedException e) {
+                        }
+                        now = SystemClock.uptimeMillis();
+                    }
+                }
+
+                StringWriter stringWriter = null;
+                TaskRecord task = null;
+                synchronized(mService) {
+                    final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
+                    persistentTaskIds.clear();
+                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                        task = tasks.get(taskNdx);
+                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
+                                task.isPersistable + " needsPersisting=" + task.needsPersisting);
+                        if (task.isPersistable) {
+                            persistentTaskIds.add(task.taskId);
+
+                            if (task.needsPersisting) {
+                                try {
+                                    stringWriter = saveToXml(task);
+                                    break;
+                                } catch (IOException e) {
+                                } catch (XmlPullParserException e) {
+                                } finally {
+                                    task.needsPersisting = false;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (stringWriter != null) {
+                    // Write out xml file while not holding mService lock.
+                    FileOutputStream file = null;
+                    AtomicFile atomicFile = null;
+                    try {
+                        atomicFile = new AtomicFile(new File(sTasksDir,
+                                String.valueOf(task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
+                        file = atomicFile.startWrite();
+                        file.write(stringWriter.toString().getBytes());
+                        file.write('\n');
+                        atomicFile.finishWrite(file);
+                    } catch (IOException e) {
+                        if (file != null) {
+                            atomicFile.failWrite(file);
+                        }
+                        Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + e);
+                    }
+                } else {
+                    // Made it through the entire list and didn't find anything new that needed
+                    // persisting.
+                    if (!DEBUG) {
+                        removeObsoleteFiles(persistentTaskIds);
+                    }
+
+                    // Wait here for someone to call setRecentsChanged().
+                    synchronized (TaskPersister.this) {
+                        while (!mRecentsChanged) {
+                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: Waiting.");
+                            try {
+                                TaskPersister.this.wait();
+                            } catch (InterruptedException e) {
+                            }
+                        }
+                        mRecentsChanged = false;
+                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: Awake");
+                    }
+                }
+                // Some recents file needs to be written.
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 6d66b29..ce83ae6 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -27,15 +27,39 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Slog;
 import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
 final class TaskRecord extends ThumbnailHolder {
+    private static final String TAG_TASK = "task";
+    private static final String ATTR_TASKID = "task_id";
+    private static final String TAG_INTENT = "intent";
+    private static final String TAG_AFFINITYINTENT = "affinity_intent";
+    private static final String ATTR_REALACTIVITY = "real_activity";
+    private static final String ATTR_ORIGACTIVITY = "orig_activity";
+    private static final String TAG_ACTIVITY = "activity";
+    private static final String ATTR_AFFINITY = "affinity";
+    private static final String ATTR_ROOTHASRESET = "root_has_reset";
+    private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
+    private static final String ATTR_USERID = "user_id";
+    private static final String ATTR_TASKTYPE = "task_type";
+    private static final String ATTR_ONTOPOFHOME = "on_top_of_home";
+    private static final String ATTR_LASTDESCRIPTION = "last_description";
+    private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
+
+    private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
+
     final int taskId;       // Unique identifier for this task.
     final String affinity;  // The affinity name for this task, or null.
     final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
@@ -62,25 +86,63 @@
             new ActivityManager.TaskDescription();
 
     /** List of all activities in the task arranged in history order */
-    final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();
+    final ArrayList<ActivityRecord> mActivities;
 
     /** Current stack */
     ActivityStack stack;
 
     /** Takes on same set of values as ActivityRecord.mActivityType */
-    private int mTaskType;
+    int taskType;
 
+    /** Takes on same value as first root activity */
+    boolean isPersistable = false;
+
+    /** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
+     * determining the order when restoring. Sign indicates whether last task movement was to front
+     * (positive) or back (negative). Absolute value indicates time. */
+    long mLastTimeMoved = System.currentTimeMillis();
+
+    /** True if persistable, has changed, and has not yet been persisted */
+    boolean needsPersisting = false;
     /** Launch the home activity when leaving this task. Will be false for tasks that are not on
      * Display.DEFAULT_DISPLAY. */
     boolean mOnTopOfHome = false;
 
-    TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
+    final ActivityManagerService mService;
+
+    TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
+        mService = service;
         taskId = _taskId;
         affinity = info.taskAffinity;
         voiceSession = _voiceSession;
         voiceInteractor = _voiceInteractor;
         setIntent(_intent, info);
+        mActivities = new ArrayList<ActivityRecord>();
+    }
+
+    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
+            String _affinity, ComponentName _realActivity, ComponentName _origActivity,
+            boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
+            int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
+            long lastTimeMoved) {
+        mService = service;
+        taskId = _taskId;
+        intent = _intent;
+        affinityIntent = _affinityIntent;
+        affinity = _affinity;
+        voiceSession = null;
+        voiceInteractor = null;
+        realActivity = _realActivity;
+        origActivity = _origActivity;
+        rootWasReset = _rootWasReset;
+        askedCompatMode = _askedCompatMode;
+        taskType = _taskType;
+        mOnTopOfHome = _onTopOfHome;
+        userId = _userId;
+        lastDescription = _lastDescription;
+        mActivities = activities;
+        mLastTimeMoved = lastTimeMoved;
     }
 
     void touchActiveTime() {
@@ -237,12 +299,16 @@
         }
         // Only set this based on the first activity
         if (mActivities.isEmpty()) {
-            mTaskType = r.mActivityType;
+            taskType = r.mActivityType;
+            isPersistable = r.isPersistable();
         } else {
             // Otherwise make all added activities match this one.
-            r.mActivityType = mTaskType;
+            r.mActivityType = taskType;
         }
         mActivities.add(index, r);
+        if (r.isPersistable()) {
+            mService.notifyTaskPersisterLocked(this, false);
+        }
     }
 
     /** @return true if this was the last activity in the task */
@@ -251,6 +317,9 @@
             // Was previously in list.
             numFullscreen--;
         }
+        if (r.isPersistable()) {
+            mService.notifyTaskPersisterLocked(this, false);
+        }
         return mActivities.size() == 0;
     }
 
@@ -270,7 +339,14 @@
             if (r.finishing) {
                 continue;
             }
-            if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear", false)) {
+            if (stack == null) {
+                // Task was restored from persistent storage.
+                r.takeFromHistory();
+                mActivities.remove(activityNdx);
+                --activityNdx;
+                --numActivities;
+            } else if (stack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, "clear",
+                    false)) {
                 --activityNdx;
                 --numActivities;
             }
@@ -354,11 +430,13 @@
     }
 
     public Bitmap getTaskTopThumbnailLocked() {
-        final ActivityRecord resumedActivity = stack.mResumedActivity;
-        if (resumedActivity != null && resumedActivity.task == this) {
-            // This task is the current resumed task, we just need to take
-            // a screenshot of it and return that.
-            return stack.screenshotActivities(resumedActivity);
+        if (stack != null) {
+            final ActivityRecord resumedActivity = stack.mResumedActivity;
+            if (resumedActivity != null && resumedActivity.task == this) {
+                // This task is the current resumed task, we just need to take
+                // a screenshot of it and return that.
+                return stack.screenshotActivities(resumedActivity);
+            }
         }
         // Return the information about the task, to figure out the top
         // thumbnail to return.
@@ -399,11 +477,11 @@
     }
 
     boolean isHomeTask() {
-        return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE;
+        return taskType == ActivityRecord.HOME_ACTIVITY_TYPE;
     }
 
     boolean isApplicationTask() {
-        return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+        return taskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
     }
 
     public TaskAccessInfo getTaskAccessInfoLocked() {
@@ -493,7 +571,7 @@
         int activityNdx;
         final int numActivities = mActivities.size();
         for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
-             ++activityNdx) {
+                ++activityNdx) {
             final ActivityRecord r = mActivities.get(activityNdx);
             if (r.intent != null &&
                     (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
@@ -528,12 +606,155 @@
         }
     }
 
+    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        Slog.i(TAG, "Saving task=" + this);
+
+        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
+        if (realActivity != null) {
+            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
+        }
+        if (origActivity != null) {
+            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
+        }
+        if (affinity != null) {
+            out.attribute(null, ATTR_AFFINITY, affinity);
+        }
+        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
+        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
+        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
+        out.attribute(null, ATTR_ONTOPOFHOME, String.valueOf(mOnTopOfHome));
+        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+        if (lastDescription != null) {
+            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
+        }
+
+        if (affinityIntent != null) {
+            out.startTag(null, TAG_AFFINITYINTENT);
+            affinityIntent.saveToXml(out);
+            out.endTag(null, TAG_AFFINITYINTENT);
+        }
+
+        out.startTag(null, TAG_INTENT);
+        intent.saveToXml(out);
+        out.endTag(null, TAG_INTENT);
+
+        final ArrayList<ActivityRecord> activities = mActivities;
+        final int numActivities = activities.size();
+        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+            final ActivityRecord r = activities.get(activityNdx);
+            if (!r.isPersistable() || (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
+                    Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) {
+                break;
+            }
+            out.startTag(null, TAG_ACTIVITY);
+            r.saveToXml(out);
+            out.endTag(null, TAG_ACTIVITY);
+        }
+
+        final Bitmap thumbnail = getTaskTopThumbnailLocked();
+        if (thumbnail != null) {
+            TaskPersister.saveImage(thumbnail, String.valueOf(taskId) + TASK_THUMBNAIL_SUFFIX);
+        }
+    }
+
+    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+            throws IOException, XmlPullParserException {
+        Intent intent = null;
+        Intent affinityIntent = null;
+        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+        ComponentName realActivity = null;
+        ComponentName origActivity = null;
+        String affinity = null;
+        boolean rootHasReset = false;
+        boolean askedCompatMode = false;
+        int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+        boolean onTopOfHome = true;
+        int userId = 0;
+        String lastDescription = null;
+        long lastTimeOnTop = 0;
+        int taskId = -1;
+        final int outerDepth = in.getDepth();
+
+        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+            final String attrName = in.getAttributeName(attrNdx);
+            final String attrValue = in.getAttributeValue(attrNdx);
+            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
+                    attrName + " value=" + attrValue);
+            if (ATTR_TASKID.equals(attrName)) {
+                taskId = Integer.valueOf(attrValue);
+            } else if (ATTR_REALACTIVITY.equals(attrName)) {
+                realActivity = ComponentName.unflattenFromString(attrValue);
+            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
+                origActivity = ComponentName.unflattenFromString(attrValue);
+            } else if (ATTR_AFFINITY.equals(attrName)) {
+                affinity = attrValue;
+            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
+                rootHasReset = Boolean.valueOf(attrValue);
+            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
+                askedCompatMode = Boolean.valueOf(attrValue);
+            } else if (ATTR_USERID.equals(attrName)) {
+                userId = Integer.valueOf(attrValue);
+            } else if (ATTR_TASKTYPE.equals(attrName)) {
+                taskType = Integer.valueOf(attrValue);
+            } else if (ATTR_ONTOPOFHOME.equals(attrName)) {
+                onTopOfHome = Boolean.valueOf(attrValue);
+            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
+                lastDescription = attrValue;
+            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
+                lastTimeOnTop = Long.valueOf(attrValue);
+            } else {
+                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
+            }
+        }
+
+        int event;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                final String name = in.getName();
+                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
+                        name);
+                if (TAG_AFFINITYINTENT.equals(name)) {
+                    affinityIntent = Intent.restoreFromXml(in);
+                } else if (TAG_INTENT.equals(name)) {
+                    intent = Intent.restoreFromXml(in);
+                } else if (TAG_ACTIVITY.equals(name)) {
+                    ActivityRecord activity =
+                            ActivityRecord.restoreFromXml(in, taskId, stackSupervisor);
+                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
+                            activity);
+                    if (activity != null) {
+                        activities.add(activity);
+                    }
+                } else {
+                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+                    XmlUtils.skipCurrentTag(in);
+                }
+            }
+        }
+
+        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
+                affinityIntent, affinity, realActivity, origActivity, rootHasReset,
+                askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
+                lastTimeOnTop);
+
+        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
+            final ActivityRecord r = activities.get(activityNdx);
+            r.thumbHolder = r.task = task;
+        }
+
+        task.lastThumbnail = TaskPersister.restoreImage(taskId + TASK_THUMBNAIL_SUFFIX);
+
+        Slog.i(TAG, "Restored task=" + task);
+        return task;
+    }
+
     void dump(PrintWriter pw, String prefix) {
-        if (numActivities != 0 || rootWasReset || userId != 0 || numFullscreen != 0) {
-            pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
-                    pw.print(" rootWasReset="); pw.print(rootWasReset);
+        if (rootWasReset || userId != 0 || numFullscreen != 0) {
+            pw.print(prefix); pw.print(" rootWasReset="); pw.print(rootWasReset);
                     pw.print(" userId="); pw.print(userId);
-                    pw.print(" mTaskType="); pw.print(mTaskType);
+                    pw.print(" taskType="); pw.print(taskType);
                     pw.print(" numFullscreen="); pw.print(numFullscreen);
                     pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
         }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b94ea62..1b1fc8b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -259,13 +259,17 @@
                     userIds[i]));
         }
 
-        ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()];
+        ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
         final SparseArray<ArrayList<ComponentName>> toAdd
                 = new SparseArray<ArrayList<ComponentName>>();
 
         synchronized (mMutex) {
-            // unbind and remove all existing services
-            toRemove = mServices.toArray(toRemove);
+            // Unbind automatically bound services, retain system services.
+            for (ManagedServiceInfo service : mServices) {
+                if (!service.isSystem) {
+                    toRemove.add(service);
+                }
+            }
 
             final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
             final ArraySet<String> newPackages = new ArraySet<String>();
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d81a25e..66cc532 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -46,12 +46,14 @@
  * {@hide}
  */
 public class NotificationUsageStats {
+    private static final boolean ENABLE_SQLITE_LOG = false;
+
     // Guarded by synchronized(this).
     private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
     private final SQLiteLog mSQLiteLog;
 
     public NotificationUsageStats(Context context) {
-        mSQLiteLog = new SQLiteLog(context);
+        mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null;
     }
 
     /**
@@ -63,7 +65,9 @@
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numPostedByApp++;
         }
-        mSQLiteLog.logPosted(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logPosted(notification);
+        }
     }
 
     /**
@@ -85,7 +89,9 @@
             stats.numRemovedByApp++;
             stats.collect(notification.stats);
         }
-        mSQLiteLog.logRemoved(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logRemoved(notification);
+        }
     }
 
     /**
@@ -97,7 +103,9 @@
             stats.numDismissedByUser++;
             stats.collect(notification.stats);
         }
-        mSQLiteLog.logDismissed(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logDismissed(notification);
+        }
     }
 
     /**
@@ -108,7 +116,9 @@
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numClickedByUser++;
         }
-        mSQLiteLog.logClicked(notification);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logClicked(notification);
+        }
     }
 
     /**
@@ -164,7 +174,9 @@
         for (AggregatedStats as : mStats.values()) {
             as.dump(pw, indent);
         }
-        mSQLiteLog.dump(pw, indent);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.dump(pw, indent);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 157d749..a629a5f 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -139,56 +139,64 @@
     }
 
     private String[] getExtraPeople(Bundle extras) {
-        String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
-        if (people != null) {
-            return people;
+        Object people = extras.get(Notification.EXTRA_PEOPLE);
+        if (people instanceof String[]) {
+            return (String[]) people;
         }
 
-        ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE);
-        if (stringArray != null) {
-            return (String[]) stringArray.toArray();
+        if (people instanceof ArrayList) {
+            ArrayList arrayList = (ArrayList) people;
+
+            if (arrayList.isEmpty()) {
+                return null;
+            }
+
+            if (arrayList.get(0) instanceof String) {
+                ArrayList<String> stringArray = (ArrayList<String>) arrayList;
+                return stringArray.toArray(new String[stringArray.size()]);
+            }
+
+            if (arrayList.get(0) instanceof CharSequence) {
+                ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
+                final int N = charSeqList.size();
+                String[] array = new String[N];
+                for (int i = 0; i < N; i++) {
+                    array[i] = charSeqList.get(i).toString();
+                }
+                return array;
+            }
+
+            return null;
         }
 
-        String string = extras.getString(Notification.EXTRA_PEOPLE);
-        if (string != null) {
-            people = new String[1];
-            people[0] = string;
-            return people;
-        }
-        char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE);
-        if (charArray != null) {
-            people = new String[1];
-            people[0] = new String(charArray);
-            return people;
+        if (people instanceof String) {
+            String[] array = new String[1];
+            array[0] = (String) people;
+            return array;
         }
 
-        CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE);
-        if (charSeq != null) {
-            people = new String[1];
-            people[0] = charSeq.toString();
-            return people;
+        if (people instanceof char[]) {
+            String[] array = new String[1];
+            array[0] = new String((char[]) people);
+            return array;
         }
 
-        CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE);
-        if (charSeqArray != null) {
+        if (people instanceof CharSequence) {
+            String[] array = new String[1];
+            array[0] = ((CharSequence) people).toString();
+            return array;
+        }
+
+        if (people instanceof CharSequence[]) {
+            CharSequence[] charSeqArray = (CharSequence[]) people;
             final int N = charSeqArray.length;
-            people = new String[N];
+            String[] array = new String[N];
             for (int i = 0; i < N; i++) {
-                people[i] = charSeqArray[i].toString();
+                array[i] = charSeqArray[i].toString();
             }
-            return people;
+            return array;
         }
 
-        ArrayList<CharSequence> charSeqList =
-                extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE);
-        if (charSeqList != null) {
-            final int N = charSeqList.size();
-            people = new String[N];
-            for (int i = 0; i < N; i++) {
-                people[i] = charSeqList.get(i).toString();
-            }
-            return people;
-        }
         return null;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0fa0b14..d505e81 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.
@@ -11770,8 +11775,8 @@
                     }
                 }
                 if (removed.size() > 0) {
-                    for (int j=0; j<removed.size(); j++) {
-                        PreferredActivity pa = removed.get(i);
+                    for (int r=0; r<removed.size(); r++) {
+                        PreferredActivity pa = removed.get(r);
                         Slog.w(TAG, "Removing dangling preferred activity: "
                                 + pa.mPref.mComponent);
                         pir.removeFilter(pa);
diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java
index db2d4ee..b1a4636 100644
--- a/services/core/java/com/android/server/task/StateChangedListener.java
+++ b/services/core/java/com/android/server/task/StateChangedListener.java
@@ -27,9 +27,8 @@
     /**
      * Called by the controller to notify the TaskManager that it should check on the state of a
      * task.
-     * @param taskStatus The state of the task which has changed.
      */
-    public void onTaskStateChanged(TaskStatus taskStatus);
+    public void onControllerStateChanged();
 
     /**
      * Called by the controller to notify the TaskManager that regardless of the state of the task,
diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/task/TaskCompletedListener.java
index 0210442..c53f5ca 100644
--- a/services/core/java/com/android/server/task/TaskCompletedListener.java
+++ b/services/core/java/com/android/server/task/TaskCompletedListener.java
@@ -16,6 +16,8 @@
 
 package com.android.server.task;
 
+import com.android.server.task.controllers.TaskStatus;
+
 /**
  * Used for communication between {@link com.android.server.task.TaskServiceContext} and the
  * {@link com.android.server.task.TaskManagerService}.
@@ -26,13 +28,5 @@
      * Callback for when a task is completed.
      * @param needsReschedule Whether the implementing class should reschedule this task.
      */
-    public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule);
-
-    /**
-     * Callback for when the implementing class needs to clean up the
-     * {@link com.android.server.task.TaskServiceContext}. The scheduler can get this callback
-     * several times if the TaskServiceContext got into a bad state (for e.g. the client crashed
-     * and it needs to clean up).
-     */
-    public void onAllTasksCompleted(int serviceToken);
+    public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule);
 }
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 6d208ff..d5b70e6 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -16,15 +16,33 @@
 
 package com.android.server.task;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
+import android.app.task.TaskManager;
 import android.content.Context;
-import android.content.Task;
+import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.util.Log;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.server.task.controllers.ConnectivityController;
+import com.android.server.task.controllers.IdleController;
+import com.android.server.task.controllers.StateController;
 import com.android.server.task.controllers.TaskStatus;
+import com.android.server.task.controllers.TimeController;
+
+import java.util.LinkedList;
 
 /**
  * Responsible for taking tasks representing work to be performed by a client app, and determining
@@ -34,78 +52,151 @@
  */
 public class TaskManagerService extends com.android.server.SystemService
         implements StateChangedListener, TaskCompletedListener {
+    // TODO: Switch this off for final version.
+    private static final boolean DEBUG = true;
+    /** The number of concurrent tasks we run at one time. */
+    private static final int MAX_TASK_CONTEXTS_COUNT = 3;
     static final String TAG = "TaskManager";
+    /**
+     * When a task fails, it gets rescheduled according to its backoff policy. To be nice, we allow
+     * this amount of time from the rescheduled time by which the retry must occur.
+     */
+    private static final long RESCHEDULE_WINDOW_SLOP_MILLIS = 5000L;
 
     /** Master list of tasks. */
     private final TaskStore mTasks;
 
-    /** Check the pending queue and start any tasks. */
-    static final int MSG_RUN_PENDING = 0;
-    /** Initiate the stop task flow. */
-    static final int MSG_STOP_TASK = 1;
-    /** */
-    static final int MSG_CHECK_TASKS = 2;
+    static final int MSG_TASK_EXPIRED = 0;
+    static final int MSG_CHECK_TASKS = 1;
+
+    // Policy constants
+    /**
+     * Minimum # of idle tasks that must be ready in order to force the TM to schedule things
+     * early.
+     */
+    private static final int MIN_IDLE_COUNT = 1;
+    /**
+     * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule
+     * things early.
+     */
+    private static final int MIN_CONNECTIVITY_COUNT = 2;
+    /**
+     * Minimum # of tasks (with no particular constraints) for which the TM will be happy running
+     * some work early.
+     */
+    private static final int MIN_READY_TASKS_COUNT = 4;
 
     /**
      * Track Services that have currently active or pending tasks. The index is provided by
      * {@link TaskStatus#getServiceToken()}
      */
-    private final SparseArray<TaskServiceContext> mActiveServices =
-            new SparseArray<TaskServiceContext>();
+    private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>();
+    /** List of controllers that will notify this service of updates to tasks. */
+    private List<StateController> mControllers;
+    /**
+     * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list
+     * when ready to execute them.
+     */
+    private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>();
 
     private final TaskHandler mHandler;
+    private final TaskManagerStub mTaskManagerStub;
 
-    private class TaskHandler extends Handler {
+    /**
+     * Entry point from client to schedule the provided task.
+     * This will add the task to the
+     * @param task Task object containing execution parameters
+     * @param uId The package identifier of the application this task is for.
+     * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
+     *                    of this task.
+     * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+     */
+    public int schedule(Task task, int uId, boolean canPersistTask) {
+        TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask);
+        return startTrackingTask(taskStatus) ?
+                TaskManager.RESULT_SUCCESS : TaskManager.RESULT_FAILURE;
+    }
 
-        public TaskHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_RUN_PENDING:
-
-                    break;
-                case MSG_STOP_TASK:
-
-                    break;
-                case MSG_CHECK_TASKS:
-                    checkTasks();
-                    break;
+    public List<Task> getPendingTasks(int uid) {
+        ArrayList<Task> outList = new ArrayList<Task>();
+        synchronized (mTasks) {
+            for (TaskStatus ts : mTasks.getTasks()) {
+                if (ts.getUid() == uid) {
+                    outList.add(ts.getTask());
+                }
             }
         }
+        return outList;
+    }
 
-        /**
-         * Called when we need to run through the list of all tasks and start/stop executing one or
-         * more of them.
-         */
-        private void checkTasks() {
-            synchronized (mTasks) {
-                final SparseArray<TaskStatus> tasks = mTasks.getTasks();
-                for (int i = 0; i < tasks.size(); i++) {
-                    TaskStatus ts = tasks.valueAt(i);
-                    if (ts.isReady() && ! isCurrentlyActive(ts)) {
-                        assignTaskToServiceContext(ts);
-                    }
+    /**
+     * Entry point from client to cancel all tasks originating from their uid.
+     * This will remove the task from the master list, and cancel the task if it was staged for
+     * execution or being executed.
+     * @param uid To check against for removal of a task.
+     */
+    public void cancelTaskForUid(int uid) {
+        // Remove from master list.
+        synchronized (mTasks) {
+            if (!mTasks.removeAllByUid(uid)) {
+                // If it's not in the master list, it's nowhere.
+                return;
+            }
+        }
+        // Remove from pending queue.
+        synchronized (mPendingTasks) {
+            Iterator<TaskStatus> it = mPendingTasks.iterator();
+            while (it.hasNext()) {
+                TaskStatus ts = it.next();
+                if (ts.getUid() == uid) {
+                    it.remove();
+                }
+            }
+        }
+        // Cancel if running.
+        synchronized (mActiveServices) {
+            for (TaskServiceContext tsc : mActiveServices) {
+                if (tsc.getRunningTask().getUid() == uid) {
+                    tsc.cancelExecutingTask();
                 }
             }
         }
     }
 
     /**
-     * Entry point from client to schedule the provided task.
-     * This will add the task to the
-     * @param task Task object containing execution parameters
-     * @param userId The id of the user this task is for.
-     * @param uId The package identifier of the application this task is for.
-     * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
-     *                    of this task.
-     * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+     * Entry point from client to cancel the task corresponding to the taskId provided.
+     * This will remove the task from the master list, and cancel the task if it was staged for
+     * execution or being executed.
+     * @param uid Uid of the calling client.
+     * @param taskId Id of the task, provided at schedule-time.
      */
-    public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
-        TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
-        return 0;
+    public void cancelTask(int uid, int taskId) {
+        synchronized (mTasks) {
+            if (!mTasks.remove(uid, taskId)) {
+                // If it's not in the master list, it's nowhere.
+                return;
+            }
+        }
+        synchronized (mPendingTasks) {
+            Iterator<TaskStatus> it = mPendingTasks.iterator();
+            while (it.hasNext()) {
+                TaskStatus ts = it.next();
+                if (ts.getUid() == uid && ts.getTaskId() == taskId) {
+                    it.remove();
+                    // If we got it from pending, it didn't make it to active so return.
+                    return;
+                }
+            }
+        }
+        synchronized (mActiveServices) {
+            for (TaskServiceContext tsc : mActiveServices) {
+                if (tsc.getRunningTask().getUid() == uid &&
+                        tsc.getRunningTask().getTaskId() == taskId) {
+                    tsc.cancelExecutingTask();
+                    return;
+                }
+            }
+        }
     }
 
     /**
@@ -121,11 +212,176 @@
         super(context);
         mTasks = new TaskStore(context);
         mHandler = new TaskHandler(context.getMainLooper());
+        mTaskManagerStub = new TaskManagerStub();
+        // Create the "runners".
+        for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) {
+            mActiveServices.add(
+                    new TaskServiceContext(this, context.getMainLooper()));
+        }
+
+        mControllers = new LinkedList<StateController>();
+        mControllers.add(ConnectivityController.get(this));
+        mControllers.add(TimeController.get(this));
+        mControllers.add(IdleController.get(this));
+        // TODO: Add BatteryStateController when implemented.
     }
 
     @Override
     public void onStart() {
+        publishBinderService(Context.TASK_SERVICE, mTaskManagerStub);
+    }
 
+    /**
+     * Called when we have a task status object that we need to insert in our
+     * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know
+     * about.
+     */
+    private boolean startTrackingTask(TaskStatus taskStatus) {
+        boolean added = false;
+        synchronized (mTasks) {
+            added = mTasks.add(taskStatus);
+        }
+        if (added) {
+            for (StateController controller : mControllers) {
+                controller.maybeStartTrackingTask(taskStatus);
+            }
+        }
+        return added;
+    }
+
+    /**
+     * Called when we want to remove a TaskStatus object that we've finished executing. Returns the
+     * object removed.
+     */
+    private boolean stopTrackingTask(TaskStatus taskStatus) {
+        boolean removed;
+        synchronized (mTasks) {
+            // Remove from store as well as controllers.
+            removed = mTasks.remove(taskStatus);
+        }
+        if (removed) {
+            for (StateController controller : mControllers) {
+                controller.maybeStopTrackingTask(taskStatus);
+            }
+        }
+        return removed;
+    }
+
+    private boolean cancelTaskOnServiceContext(TaskStatus ts) {
+        synchronized (mActiveServices) {
+            for (TaskServiceContext tsc : mActiveServices) {
+                if (tsc.getRunningTask() == ts) {
+                    tsc.cancelExecutingTask();
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * @param ts TaskStatus we are querying against.
+     * @return Whether or not the task represented by the status object is currently being run or
+     * is pending.
+     */
+    private boolean isCurrentlyActive(TaskStatus ts) {
+        synchronized (mActiveServices) {
+            for (TaskServiceContext serviceContext : mActiveServices) {
+                if (serviceContext.getRunningTask() == ts) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * A task is rescheduled with exponential back-off if the client requests this from their
+     * execution logic.
+     * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the
+     * timeliness of the reschedule. For an idle-mode task, no deadline is given.
+     * @param failureToReschedule Provided task status that we will reschedule.
+     * @return A newly instantiated TaskStatus with the same constraints as the last task except
+     * with adjusted timing constraints.
+     */
+    private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) {
+        final long elapsedNowMillis = SystemClock.elapsedRealtime();
+        final Task task = failureToReschedule.getTask();
+
+        final long initialBackoffMillis = task.getInitialBackoffMillis();
+        final int backoffAttempt = failureToReschedule.getNumFailures() + 1;
+        long newEarliestRuntimeElapsed = elapsedNowMillis;
+
+        switch (task.getBackoffPolicy()) {
+            case Task.BackoffPolicy.LINEAR:
+                newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt;
+                break;
+            default:
+                if (DEBUG) {
+                    Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
+                }
+            case Task.BackoffPolicy.EXPONENTIAL:
+                newEarliestRuntimeElapsed += Math.pow(initialBackoffMillis, backoffAttempt);
+                break;
+        }
+        long newLatestRuntimeElapsed = failureToReschedule.hasIdleConstraint() ? Long.MAX_VALUE
+                : newEarliestRuntimeElapsed + RESCHEDULE_WINDOW_SLOP_MILLIS;
+        return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed,
+                newLatestRuntimeElapsed, backoffAttempt);
+    }
+
+    /**
+     * Called after a periodic has executed so we can to re-add it. We take the last execution time
+     * of the task to be the time of completion (i.e. the time at which this function is called).
+     * This could be inaccurate b/c the task can run for as long as
+     * {@link com.android.server.task.TaskServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
+     * to underscheduling at least, rather than if we had taken the last execution time to be the
+     * start of the execution.
+     * @return A new task representing the execution criteria for this instantiation of the
+     * recurring task.
+     */
+    private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) {
+        final long elapsedNow = SystemClock.elapsedRealtime();
+        // Compute how much of the period is remaining.
+        long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+        long newEarliestRunTimeElapsed = elapsedNow + runEarly;
+        long period = periodicToReschedule.getTask().getIntervalMillis();
+        long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
+
+        if (DEBUG) {
+            Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
+                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
+        }
+        return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed,
+                newLatestRuntimeElapsed, 0 /* backoffAttempt */);
+    }
+
+    // TaskCompletedListener implementations.
+
+    /**
+     * A task just finished executing. We fetch the
+     * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
+     * whether we want to reschedule we readd it to the controllers.
+     * @param taskStatus Completed task.
+     * @param needsReschedule Whether the implementing class should reschedule this task.
+     */
+    @Override
+    public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) {
+        if (!stopTrackingTask(taskStatus)) {
+            if (DEBUG) {
+                Slog.e(TAG, "Error removing task: could not find task to remove. Was task" +
+                        "removed while executing?");
+            }
+            return;
+        }
+        if (needsReschedule) {
+            TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus);
+            startTrackingTask(rescheduled);
+        } else if (taskStatus.getTask().isPeriodic()) {
+            TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus);
+            startTrackingTask(rescheduledPeriodic);
+        }
+        mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
     }
 
     // StateChangedListener implementations.
@@ -138,69 +394,236 @@
      * see which are ready. This will further decouple the controllers from the execution logic.
      */
     @Override
-    public void onTaskStateChanged(TaskStatus taskStatus) {
-        postCheckTasksMessage();
-
+    public void onControllerStateChanged() {
+        // Post a message to to run through the list of tasks and start/stop any that are eligible.
+        mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
     }
 
     @Override
     public void onTaskDeadlineExpired(TaskStatus taskStatus) {
-
+        mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus);
     }
 
-    // TaskCompletedListener implementations.
+    private class TaskHandler extends Handler {
 
-    /**
-     * A task just finished executing. We fetch the
-     * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
-     * whether we want to reschedule we readd it to the controllers.
-     * @param serviceToken key for the service context in {@link #mActiveServices}.
-     * @param taskId Id of the task that is complete.
-     * @param needsReschedule Whether the implementing class should reschedule this task.
-     */
-    @Override
-    public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
-        final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
-        if (serviceContext == null) {
-            Log.e(TAG, "Task completed for invalid service context; " + serviceToken);
-            return;
+        public TaskHandler(Looper looper) {
+            super(looper);
         }
 
-    }
-
-    @Override
-    public void onAllTasksCompleted(int serviceToken) {
-        
-    }
-
-    private void assignTaskToServiceContext(TaskStatus ts) {
-        TaskServiceContext serviceContext =
-                mActiveServices.get(ts.getServiceToken());
-        if (serviceContext == null) {
-            serviceContext = new TaskServiceContext(this, mHandler.getLooper(), ts);
-            mActiveServices.put(ts.getServiceToken(), serviceContext);
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_TASK_EXPIRED:
+                    final TaskStatus expired = (TaskStatus) message.obj;  // Unused for now.
+                    queueReadyTasksForExecutionH();
+                    break;
+                case MSG_CHECK_TASKS:
+                    // Check the list of tasks and run some of them if we feel inclined.
+                    maybeQueueReadyTasksForExecutionH();
+                    break;
+            }
+            maybeRunNextPendingTaskH();
+            // Don't remove TASK_EXPIRED in case one came along while processing the queue.
+            removeMessages(MSG_CHECK_TASKS);
         }
-        serviceContext.addPendingTask(ts);
+
+        /**
+         * Run through list of tasks and execute all possible - at least one is expired so we do
+         * as many as we can.
+         */
+        private void queueReadyTasksForExecutionH() {
+            synchronized (mTasks) {
+                for (TaskStatus ts : mTasks.getTasks()) {
+                    final boolean criteriaSatisfied = ts.isReady();
+                    final boolean isRunning = isCurrentlyActive(ts);
+                    if (criteriaSatisfied && !isRunning) {
+                        synchronized (mPendingTasks) {
+                            mPendingTasks.add(ts);
+                        }
+                    } else if (!criteriaSatisfied && isRunning) {
+                        cancelTaskOnServiceContext(ts);
+                    }
+                }
+            }
+        }
+
+        /**
+         * The state of at least one task has changed. Here is where we could enforce various
+         * policies on when we want to execute tasks.
+         * Right now the policy is such:
+         *      If >1 of the ready tasks is idle mode we send all of them off
+         *      if more than 2 network connectivity tasks are ready we send them all off.
+         *      If more than 4 tasks total are ready we send them all off.
+         *      TODO: It would be nice to consolidate these sort of high-level policies somewhere.
+         */
+        private void maybeQueueReadyTasksForExecutionH() {
+            synchronized (mTasks) {
+                int idleCount = 0;
+                int connectivityCount = 0;
+                List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>();
+                for (TaskStatus ts : mTasks.getTasks()) {
+                    final boolean criteriaSatisfied = ts.isReady();
+                    final boolean isRunning = isCurrentlyActive(ts);
+                    if (criteriaSatisfied && !isRunning) {
+                        if (ts.hasIdleConstraint()) {
+                            idleCount++;
+                        }
+                        if (ts.hasConnectivityConstraint() || ts.hasMeteredConstraint()) {
+                            connectivityCount++;
+                        }
+                        runnableTasks.add(ts);
+                    } else if (!criteriaSatisfied && isRunning) {
+                        cancelTaskOnServiceContext(ts);
+                    }
+                }
+                if (idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT ||
+                        runnableTasks.size() >= MIN_READY_TASKS_COUNT) {
+                    for (TaskStatus ts : runnableTasks) {
+                        synchronized (mPendingTasks) {
+                            mPendingTasks.add(ts);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Checks the state of the pending queue against any available
+         * {@link com.android.server.task.TaskServiceContext} that can run a new task.
+         * {@link com.android.server.task.TaskServiceContext}.
+         */
+        private void maybeRunNextPendingTaskH() {
+            TaskStatus nextPending;
+            synchronized (mPendingTasks) {
+                nextPending = mPendingTasks.poll();
+            }
+            if (nextPending == null) {
+                return;
+            }
+
+            synchronized (mActiveServices) {
+                for (TaskServiceContext tsc : mActiveServices) {
+                    if (tsc.isAvailable()) {
+                        if (tsc.executeRunnableTask(nextPending)) {
+                            return;
+                        }
+                    }
+                }
+            }
+        }
     }
 
     /**
-     * @param ts TaskStatus we are querying against.
-     * @return Whether or not the task represented by the status object is currently being run or
-     * is pending.
+     * Binder stub trampoline implementation
      */
-    private boolean isCurrentlyActive(TaskStatus ts) {
-        TaskServiceContext serviceContext = mActiveServices.get(ts.getServiceToken());
-        if (serviceContext == null) {
-            return false;
-        }
-        return serviceContext.hasTaskPending(ts);
-    }
+    final class TaskManagerStub extends ITaskManager.Stub {
+        /** Cache determination of whether a given app can persist tasks
+         * key is uid of the calling app; value is undetermined/true/false
+         */
+        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
 
-    /**
-     * Post a message to {@link #mHandler} to run through the list of tasks and start/stop any that
-     * are eligible.
-     */
-    private void postCheckTasksMessage() {
-        mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
+        // Determine whether the caller is allowed to persist tasks, with a small cache
+        // because the lookup is expensive enough that we'd like to avoid repeating it.
+        // This must be called from within the calling app's binder identity!
+        private boolean canCallerPersistTasks() {
+            final boolean canPersist;
+            final int callingUid = Binder.getCallingUid();
+            synchronized (mPersistCache) {
+                Boolean cached = mPersistCache.get(callingUid);
+                if (cached) {
+                    canPersist = cached.booleanValue();
+                } else {
+                    // Persisting tasks is tantamount to running at boot, so we permit
+                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+                    // permission
+                    int result = getContext().checkCallingPermission(
+                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
+                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
+                    mPersistCache.put(callingUid, canPersist);
+                }
+            }
+            return canPersist;
+        }
+
+        // ITaskManager implementation
+        @Override
+        public int schedule(Task task) throws RemoteException {
+            final boolean canPersist = canCallerPersistTasks();
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return TaskManagerService.this.schedule(task, uid, canPersist);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public List<Task> getAllPendingTasks() throws RemoteException {
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return TaskManagerService.this.getPendingTasks(uid);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public void cancelAll() throws RemoteException {
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                TaskManagerService.this.cancelTaskForUid(uid);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public void cancel(int taskId) throws RemoteException {
+            final int uid = Binder.getCallingUid();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                TaskManagerService.this.cancelTask(uid, taskId);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        /**
+         * "dumpsys" infrastructure
+         */
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+            long identityToken = Binder.clearCallingIdentity();
+            try {
+                TaskManagerService.this.dumpInternal(pw);
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+    };
+
+    void dumpInternal(PrintWriter pw) {
+        synchronized (mTasks) {
+            pw.print("Registered tasks:");
+            if (mTasks.size() > 0) {
+                for (TaskStatus ts : mTasks.getTasks()) {
+                    pw.println();
+                    ts.dump(pw, "  ");
+                }
+            } else {
+                pw.println();
+                pw.println("No tasks scheduled.");
+            }
+        }
+        pw.println();
     }
 }
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index b51cbb3..75e9212 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -36,20 +37,18 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.task.controllers.TaskStatus;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding
- * is reused to start concurrent tasks on the TaskService. Information here is unique
- * to the service.
- * Functionality provided by this class:
- *     - Managages wakelock for the service.
- *     - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
- *     -
+ * Handles client binding and lifecycle of a task. A task will only execute one at a time on an
+ * instance of this class.
  */
 public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection {
+    private static final boolean DEBUG = true;
     private static final String TAG = "TaskServiceContext";
     /** Define the maximum # of tasks allowed to run on a service at once. */
     private static final int defaultMaxActiveTasksPerService =
@@ -66,10 +65,10 @@
     };
 
     // States that a task occupies while interacting with the client.
-    private static final int VERB_STARTING = 0;
-    private static final int VERB_EXECUTING = 1;
-    private static final int VERB_STOPPING = 2;
-    private static final int VERB_PENDING = 3;
+    static final int VERB_BINDING = 0;
+    static final int VERB_STARTING = 1;
+    static final int VERB_EXECUTING = 2;
+    static final int VERB_STOPPING = 3;
 
     // Messages that result from interactions with the client service.
     /** System timed out waiting for a response. */
@@ -77,178 +76,173 @@
     /** Received a callback from client. */
     private static final int MSG_CALLBACK = 1;
     /** Run through list and start any ready tasks.*/
-    private static final int MSG_CHECK_PENDING = 2;
-    /** Cancel an active task. */
+    private static final int MSG_SERVICE_BOUND = 2;
+    /** Cancel a task. */
     private static final int MSG_CANCEL = 3;
-    /** Add a pending task. */
-    private static final int MSG_ADD_PENDING = 4;
-    /** Client crashed, so we need to wind things down. */
-    private static final int MSG_SHUTDOWN = 5;
+    /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/
+    private static final int MSG_SHUTDOWN_EXECUTION = 4;
 
-    /** Used to identify this task service context when communicating with the TaskManager. */
-    final int token;
-    final ComponentName component;
-    final int userId;
-    ITaskService service;
     private final Handler mCallbackHandler;
-    /** Tasks that haven't been sent to the client for execution yet. */
-    private final SparseArray<ActiveTask> mPending;
+    /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
+    private final TaskCompletedListener mCompletedListener;
     /** Used for service binding, etc. */
     private final Context mContext;
-    /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
-    final private TaskCompletedListener mCompletedListener;
-    private final PowerManager.WakeLock mWakeLock;
+    private PowerManager.WakeLock mWakeLock;
 
-    /** Whether this service is actively bound. */
-    boolean mBound;
+    // Execution state.
+    private TaskParams mParams;
+    @VisibleForTesting
+    int mVerb;
+    private AtomicBoolean mCancelled = new AtomicBoolean();
 
-    TaskServiceContext(TaskManagerService taskManager, Looper looper, TaskStatus taskStatus) {
-        mContext = taskManager.getContext();
-        this.component = taskStatus.getServiceComponent();
-        this.token = taskStatus.getServiceToken();
-        this.userId = taskStatus.getUserId();
+    /** All the information maintained about the task currently being executed. */
+    private TaskStatus mRunningTask;
+    /** Binder to the client service. */
+    ITaskService service;
+
+    private final Object mAvailableLock = new Object();
+    /** Whether this context is free. */
+    @GuardedBy("mAvailableLock")
+    private boolean mAvailable;
+
+    TaskServiceContext(TaskManagerService service, Looper looper) {
+        this(service.getContext(), service, looper);
+    }
+
+    @VisibleForTesting
+    TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) {
+        mContext = context;
         mCallbackHandler = new TaskServiceHandler(looper);
-        mPending = new SparseArray<ActiveTask>();
-        mCompletedListener = taskManager;
-        final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mCompletedListener = completedListener;
+    }
+
+    /**
+     * Give a task to this context for execution. Callers must first check {@link #isAvailable()}
+     * to make sure this is a valid context.
+     * @param ts The status of the task that we are going to run.
+     * @return True if the task was accepted and is going to run.
+     */
+    boolean executeRunnableTask(TaskStatus ts) {
+        synchronized (mAvailableLock) {
+            if (!mAvailable) {
+                Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
+                return false;
+            }
+            mAvailable = false;
+        }
+
+        final PowerManager pm =
+                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                TM_WAKELOCK_PREFIX + component.getPackageName());
-        mWakeLock.setWorkSource(new WorkSource(taskStatus.getUid()));
+                TM_WAKELOCK_PREFIX + ts.getServiceComponent().getPackageName());
+        mWakeLock.setWorkSource(new WorkSource(ts.getUid()));
         mWakeLock.setReferenceCounted(false);
+
+        mRunningTask = ts;
+        mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
+
+        mVerb = VERB_BINDING;
+        final Intent intent = new Intent().setComponent(ts.getServiceComponent());
+        boolean binding = mContext.bindServiceAsUser(intent, this,
+                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
+                new UserHandle(ts.getUserId()));
+        if (!binding) {
+            if (DEBUG) {
+                Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable.");
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /** Used externally to query the running task. Will return null if there is no task running. */
+    TaskStatus getRunningTask() {
+        return mRunningTask;
+    }
+
+    /** Called externally when a task that was scheduled for execution should be cancelled. */
+    void cancelExecutingTask() {
+        mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
+    }
+
+    /**
+     * @return Whether this context is available to handle incoming work.
+     */
+    boolean isAvailable() {
+        synchronized (mAvailableLock) {
+            return mAvailable;
+        }
     }
 
     @Override
     public void taskFinished(int taskId, boolean reschedule) {
+        if (!verifyCallingUid()) {
+            return;
+        }
         mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
                 .sendToTarget();
     }
 
     @Override
     public void acknowledgeStopMessage(int taskId, boolean reschedule) {
+        if (!verifyCallingUid()) {
+            return;
+        }
         mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
                 .sendToTarget();
     }
 
     @Override
     public void acknowledgeStartMessage(int taskId, boolean ongoing) {
+        if (!verifyCallingUid()) {
+            return;
+        }
         mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget();
     }
 
     /**
-     * Queue up this task to run on the client. This will execute the task as quickly as possible.
-     * @param ts Status of the task to run.
-     */
-    public void addPendingTask(TaskStatus ts) {
-        final TaskParams params = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
-        final ActiveTask newTask = new ActiveTask(params, VERB_PENDING);
-        mCallbackHandler.obtainMessage(MSG_ADD_PENDING, newTask).sendToTarget();
-        if (!mBound) {
-            Intent intent = new Intent().setComponent(component);
-            boolean binding = mContext.bindServiceAsUser(intent, this,
-                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
-                    new UserHandle(userId));
-            if (!binding) {
-                Log.e(TAG, component.getShortClassName() + " unavailable.");
-                cancelPendingTask(ts);
-            }
-        }
-    }
-
-    /**
-     * Called externally when a task that was scheduled for execution should be cancelled.
-     * @param ts The status of the task to cancel.
-     */
-    public void cancelPendingTask(TaskStatus ts) {
-        mCallbackHandler.obtainMessage(MSG_CANCEL, ts.getTaskId(), -1 /* arg2 */)
-                .sendToTarget();
-    }
-
-    /**
-     * MSG_TIMEOUT is sent with the {@link com.android.server.task.TaskServiceContext.ActiveTask}
-     * set in the {@link Message#obj} field. This makes it easier to remove timeouts for a given
-     * ActiveTask.
-     * @param op Operation that is taking place.
-     */
-    private void scheduleOpTimeOut(ActiveTask op) {
-        mCallbackHandler.removeMessages(MSG_TIMEOUT, op);
-
-        final long timeoutMillis = (op.verb == VERB_EXECUTING) ?
-                EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
-        if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-            Slog.d(TAG, "Scheduling time out for '" + component.getShortClassName() + "' tId: " +
-                    op.params.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
-        }
-        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, op);
-        mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
-    }
-
-    /**
-     * @return true if this task is pending or active within this context.
-     */
-    public boolean hasTaskPending(TaskStatus taskStatus) {
-        synchronized (mPending) {
-            return mPending.get(taskStatus.getTaskId()) != null;
-        }
-    }
-
-    public boolean isBound() {
-        return mBound;
-    }
-
-    /**
-     * We acquire/release the wakelock on onServiceConnected/unbindService. This mirrors the work
+     * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
      * we intend to send to the client - we stop sending work when the service is unbound so until
      * then we keep the wakelock.
-     * @param name The concrete component name of the service that has
-     * been connected.
+     * @param name The concrete component name of the service that has been connected.
      * @param service The IBinder of the Service's communication channel,
      */
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
-        mBound = true;
+        if (!name.equals(mRunningTask.getServiceComponent())) {
+            mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+            return;
+        }
         this.service = ITaskService.Stub.asInterface(service);
-        // Remove all timeouts. We've just connected to the client so there are no other
-        // MSG_TIMEOUTs at this point.
+        // Remove all timeouts.
         mCallbackHandler.removeMessages(MSG_TIMEOUT);
         mWakeLock.acquire();
-        mCallbackHandler.obtainMessage(MSG_CHECK_PENDING).sendToTarget();
+        mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
     }
 
     /**
-     * When the client service crashes we can have a couple tasks executing, in various stages of
-     * undress. We'll cancel all of them and request that they be rescheduled.
+     * If the client service crashes we reschedule this task and clean up.
      * @param name The concrete component name of the service whose
      */
     @Override
     public void onServiceDisconnected(ComponentName name) {
-        // Service disconnected... probably client crashed.
-        startShutdown();
+        mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
     }
 
     /**
-     * We don't just shutdown outright - we make sure the scheduler isn't going to send us any more
-     * tasks, then we do the shutdown.
+     * This class is reused across different clients, and passes itself in as a callback. Check
+     * whether the client exercising the callback is the client we expect.
+     * @return True if the binder calling is coming from the client we expect.
      */
-    private void startShutdown() {
-        mCompletedListener.onAllTasksCompleted(token);
-        mCallbackHandler.obtainMessage(MSG_SHUTDOWN).sendToTarget();
-    }
-
-    /** Tracks a task across its various state changes. */
-    private static class ActiveTask {
-        final TaskParams params;
-        int verb;
-        AtomicBoolean cancelled = new AtomicBoolean();
-
-        ActiveTask(TaskParams params, int verb) {
-            this.params = params;
-            this.verb = verb;
+    private boolean verifyCallingUid() {
+        if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) {
+            if (DEBUG) {
+                Slog.d(TAG, "Stale callback received, ignoring.");
+            }
+            return false;
         }
-
-        @Override
-        public String toString() {
-            return params.getTaskId() + " " + VERB_STRINGS[verb];
-        }
+        return true;
     }
 
     /**
@@ -264,52 +258,67 @@
         @Override
         public void handleMessage(Message message) {
             switch (message.what) {
-                case MSG_ADD_PENDING:
-                    if (message.obj != null) {
-                        ActiveTask pendingTask = (ActiveTask) message.obj;
-                        mPending.put(pendingTask.params.getTaskId(), pendingTask);
-                    }
-                    // fall through.
-                case MSG_CHECK_PENDING:
-                    checkPendingTasksH();
+                case MSG_SERVICE_BOUND:
+                    handleServiceBoundH();
                     break;
                 case MSG_CALLBACK:
-                    ActiveTask receivedCallback = mPending.get(message.arg1);
-                    removeMessages(MSG_TIMEOUT, receivedCallback);
-
-                    if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-                        Log.d(TAG, "MSG_CALLBACK of : " + receivedCallback);
+                    if (DEBUG) {
+                        Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask);
                     }
+                    removeMessages(MSG_TIMEOUT);
 
-                    if (receivedCallback.verb == VERB_STARTING) {
+                    if (mVerb == VERB_STARTING) {
                         final boolean workOngoing = message.arg2 == 1;
-                        handleStartedH(receivedCallback, workOngoing);
-                    } else if (receivedCallback.verb == VERB_EXECUTING ||
-                            receivedCallback.verb == VERB_STOPPING) {
+                        handleStartedH(workOngoing);
+                    } else if (mVerb == VERB_EXECUTING ||
+                            mVerb == VERB_STOPPING) {
                         final boolean reschedule = message.arg2 == 1;
-                        handleFinishedH(receivedCallback, reschedule);
+                        handleFinishedH(reschedule);
                     } else {
-                        if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-                            Log.d(TAG, "Unrecognised callback: " + receivedCallback);
+                        if (DEBUG) {
+                            Slog.d(TAG, "Unrecognised callback: " + mRunningTask);
                         }
                     }
                     break;
                 case MSG_CANCEL:
-                    ActiveTask cancelled = mPending.get(message.arg1);
-                    handleCancelH(cancelled);
+                    handleCancelH();
                     break;
                 case MSG_TIMEOUT:
-                    // Timeout msgs have the ActiveTask ref so we can remove them easily.
-                    handleOpTimeoutH((ActiveTask) message.obj);
+                    handleOpTimeoutH();
                     break;
-                case MSG_SHUTDOWN:
-                    handleShutdownH();
-                    break;
+                case MSG_SHUTDOWN_EXECUTION:
+                    closeAndCleanupTaskH(true /* needsReschedule */);
                 default:
                     Log.e(TAG, "Unrecognised message: " + message);
             }
         }
 
+        /** Start the task on the service. */
+        private void handleServiceBoundH() {
+            if (mVerb != VERB_BINDING) {
+                Slog.e(TAG, "Sending onStartTask for a task that isn't pending. "
+                        + VERB_STRINGS[mVerb]);
+                closeAndCleanupTaskH(false /* reschedule */);
+                return;
+            }
+            if (mCancelled.get()) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Task cancelled while waiting for bind to complete. "
+                            + mRunningTask);
+                }
+                closeAndCleanupTaskH(true /* reschedule */);
+                return;
+            }
+            try {
+                mVerb = VERB_STARTING;
+                scheduleOpTimeOut();
+                service.startTask(mParams);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error sending onStart message to '" +
+                        mRunningTask.getServiceComponent().getShortClassName() + "' ", e);
+            }
+        }
+
         /**
          * State behaviours.
          * VERB_STARTING   -> Successful start, change task to VERB_EXECUTING and post timeout.
@@ -317,24 +326,25 @@
          *     _EXECUTING  -> Error
          *     _STOPPING   -> Error
          */
-        private void handleStartedH(ActiveTask started, boolean workOngoing) {
-            switch (started.verb) {
+        private void handleStartedH(boolean workOngoing) {
+            switch (mVerb) {
                 case VERB_STARTING:
-                    started.verb = VERB_EXECUTING;
+                    mVerb = VERB_EXECUTING;
                     if (!workOngoing) {
                         // Task is finished already so fast-forward to handleFinished.
-                        handleFinishedH(started, false);
+                        handleFinishedH(false);
                         return;
-                    } else if (started.cancelled.get()) {
-                        // Cancelled *while* waiting for acknowledgeStartMessage from client.
-                        handleCancelH(started);
-                        return;
-                    } else {
-                        scheduleOpTimeOut(started);
                     }
+                    if (mCancelled.get()) {
+                        // Cancelled *while* waiting for acknowledgeStartMessage from client.
+                        handleCancelH();
+                        return;
+                    }
+                    scheduleOpTimeOut();
                     break;
                 default:
-                    Log.e(TAG, "Handling started task but task wasn't starting! " + started);
+                    Log.e(TAG, "Handling started task but task wasn't starting! Was "
+                            + VERB_STRINGS[mVerb] + ".");
                     return;
             }
         }
@@ -345,155 +355,104 @@
          *     _STARTING   -> Error
          *     _PENDING    -> Error
          */
-        private void handleFinishedH(ActiveTask executedTask, boolean reschedule) {
-            switch (executedTask.verb) {
+        private void handleFinishedH(boolean reschedule) {
+            switch (mVerb) {
                 case VERB_EXECUTING:
                 case VERB_STOPPING:
-                    closeAndCleanupTaskH(executedTask, reschedule);
+                    closeAndCleanupTaskH(reschedule);
                     break;
                 default:
-                    Log.e(TAG, "Got an execution complete message for a task that wasn't being" +
-                            "executed. " + executedTask);
+                    Slog.e(TAG, "Got an execution complete message for a task that wasn't being" +
+                            "executed. Was " + VERB_STRINGS[mVerb] + ".");
             }
         }
 
         /**
          * A task can be in various states when a cancel request comes in:
-         * VERB_PENDING    -> Remove from queue.
-         *     _STARTING   -> Mark as cancelled and wait for {@link #acknowledgeStartMessage(int)}.
+         * VERB_BINDING    -> Cancelled before bind completed. Mark as cancelled and wait for
+         *                    {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
+         *     _STARTING   -> Mark as cancelled and wait for
+         *                    {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)}
          *     _EXECUTING  -> call {@link #sendStopMessageH}}.
          *     _ENDING     -> No point in doing anything here, so we ignore.
          */
-        private void handleCancelH(ActiveTask cancelledTask) {
-            switch (cancelledTask.verb) {
-                case VERB_PENDING:
-                    mPending.remove(cancelledTask.params.getTaskId());
-                    break;
+        private void handleCancelH() {
+            switch (mVerb) {
+                case VERB_BINDING:
                 case VERB_STARTING:
-                    cancelledTask.cancelled.set(true);
+                    mCancelled.set(true);
                     break;
                 case VERB_EXECUTING:
-                    cancelledTask.verb = VERB_STOPPING;
-                    sendStopMessageH(cancelledTask);
+                    sendStopMessageH();
                     break;
                 case VERB_STOPPING:
                     // Nada.
                     break;
                 default:
-                    Log.e(TAG, "Cancelling a task without a valid verb: " + cancelledTask);
+                    Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb);
                     break;
             }
         }
 
-        /**
-         * This TaskServiceContext is shutting down. Remove all the tasks from the pending queue
-         * and reschedule them as if they had failed.
-         * Before posting this message, caller must invoke
-         * {@link com.android.server.task.TaskCompletedListener#onAllTasksCompleted(int)}.
-         */
-        private void handleShutdownH() {
-            for (int i = 0; i < mPending.size(); i++) {
-                ActiveTask at = mPending.valueAt(i);
-                closeAndCleanupTaskH(at, true /* needsReschedule */);
-            }
-            mWakeLock.release();
-            mContext.unbindService(TaskServiceContext.this);
-            service = null;
-            mBound = false;
-        }
-
-        /**
-         * MSG_TIMEOUT gets processed here.
-         * @param timedOutTask The task that timed out.
-         */
-        private void handleOpTimeoutH(ActiveTask timedOutTask) {
+        /** Process MSG_TIMEOUT here. */
+        private void handleOpTimeoutH() {
             if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
-                Log.d(TAG, "MSG_TIMEOUT of " + component.getShortClassName() + " : "
-                        + timedOutTask.params.getTaskId());
+                Log.d(TAG, "MSG_TIMEOUT of " +
+                        mRunningTask.getServiceComponent().getShortClassName() + " : "
+                        + mParams.getTaskId());
             }
 
-            final int taskId = timedOutTask.params.getTaskId();
-            switch (timedOutTask.verb) {
+            final int taskId = mParams.getTaskId();
+            switch (mVerb) {
                 case VERB_STARTING:
                     // Client unresponsive - wedged or failed to respond in time. We don't really
                     // know what happened so let's log it and notify the TaskManager
                     // FINISHED/NO-RETRY.
                     Log.e(TAG, "No response from client for onStartTask '" +
-                            component.getShortClassName() + "' tId: " + taskId);
-                    closeAndCleanupTaskH(timedOutTask, false /* needsReschedule */);
+                            mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+                            + taskId);
+                    closeAndCleanupTaskH(false /* needsReschedule */);
                     break;
                 case VERB_STOPPING:
                     // At least we got somewhere, so fail but ask the TaskManager to reschedule.
                     Log.e(TAG, "No response from client for onStopTask, '" +
-                            component.getShortClassName() + "' tId: " + taskId);
-                    closeAndCleanupTaskH(timedOutTask, true /* needsReschedule */);
+                            mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+                            + taskId);
+                    closeAndCleanupTaskH(true /* needsReschedule */);
                     break;
                 case VERB_EXECUTING:
                     // Not an error - client ran out of time.
                     Log.i(TAG, "Client timed out while executing (no taskFinished received)." +
                             " Reporting failure and asking for reschedule. "  +
-                            component.getShortClassName() + "' tId: " + taskId);
-                    sendStopMessageH(timedOutTask);
+                            mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
+                            + taskId);
+                    sendStopMessageH();
                     break;
                 default:
                     Log.e(TAG, "Handling timeout for an unknown active task state: "
-                            + timedOutTask);
+                            + mRunningTask);
                     return;
             }
         }
 
         /**
-         * Called on the handler thread. Checks the state of the pending queue and starts the task
-         * if it can. The task only starts if there is capacity on the service.
+         * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
+         * VERB_STOPPING.
          */
-        private void checkPendingTasksH() {
-            if (!mBound) {
-                return;
-            }
-            for (int i = 0; i < mPending.size() && i < defaultMaxActiveTasksPerService; i++) {
-                ActiveTask at = mPending.valueAt(i);
-                if (at.verb != VERB_PENDING) {
-                    continue;
-                }
-                sendStartMessageH(at);
-            }
-        }
-
-        /**
-         * Already running, need to stop. Rund on handler.
-         * @param stoppingTask Task we are sending onStopMessage for. This task will be moved from
-         *                     VERB_EXECUTING -> VERB_STOPPING.
-         */
-        private void sendStopMessageH(ActiveTask stoppingTask) {
-            mCallbackHandler.removeMessages(MSG_TIMEOUT, stoppingTask);
-            if (stoppingTask.verb != VERB_EXECUTING) {
-                Log.e(TAG, "Sending onStopTask for a task that isn't started. " + stoppingTask);
-                // TODO: Handle error?
+        private void sendStopMessageH() {
+            mCallbackHandler.removeMessages(MSG_TIMEOUT);
+            if (mVerb != VERB_EXECUTING) {
+                Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask);
+                closeAndCleanupTaskH(false /* reschedule */);
                 return;
             }
             try {
-                service.stopTask(stoppingTask.params);
-                stoppingTask.verb = VERB_STOPPING;
-                scheduleOpTimeOut(stoppingTask);
+                mVerb = VERB_STOPPING;
+                scheduleOpTimeOut();
+                service.stopTask(mParams);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error sending onStopTask to client.", e);
-                closeAndCleanupTaskH(stoppingTask, false);
-            }
-        }
-
-        /** Start the task on the service. */
-        private void sendStartMessageH(ActiveTask pendingTask) {
-            if (pendingTask.verb != VERB_PENDING) {
-                Log.e(TAG, "Sending onStartTask for a task that isn't pending. " + pendingTask);
-                // TODO: Handle error?
-            }
-            try {
-                service.startTask(pendingTask.params);
-                pendingTask.verb = VERB_STARTING;
-                scheduleOpTimeOut(pendingTask);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error sending onStart message to '" + component.getShortClassName()
-                        + "' ", e);
+                closeAndCleanupTaskH(false);
             }
         }
 
@@ -503,13 +462,42 @@
          * or from acknowledging the stop message we sent. Either way, we're done tracking it and
          * we want to clean up internally.
          */
-        private void closeAndCleanupTaskH(ActiveTask completedTask, boolean reschedule) {
-            removeMessages(MSG_TIMEOUT, completedTask);
-            mPending.remove(completedTask.params.getTaskId());
-            if (mPending.size() == 0) {
-                startShutdown();
+        private void closeAndCleanupTaskH(boolean reschedule) {
+            removeMessages(MSG_TIMEOUT);
+            mWakeLock.release();
+            mContext.unbindService(TaskServiceContext.this);
+            mWakeLock = null;
+
+            mRunningTask = null;
+            mParams = null;
+            mVerb = -1;
+            mCancelled.set(false);
+
+            service = null;
+
+            mCompletedListener.onTaskCompleted(mRunningTask, reschedule);
+            synchronized (mAvailableLock) {
+                mAvailable = true;
             }
-            mCompletedListener.onTaskCompleted(token, completedTask.params.getTaskId(), reschedule);
+        }
+
+        /**
+         * Called when sending a message to the client, over whose execution we have no control. If we
+         * haven't received a response in a certain amount of time, we want to give up and carry on
+         * with life.
+         */
+        private void scheduleOpTimeOut() {
+            mCallbackHandler.removeMessages(MSG_TIMEOUT);
+
+            final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
+                    EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
+            if (DEBUG) {
+                Slog.d(TAG, "Scheduling time out for '" +
+                        mRunningTask.getServiceComponent().getShortClassName() + "' tId: " +
+                        mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
+            }
+            Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+            mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
         }
     }
 }
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 3bfc8a5..f72ab22 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -16,12 +16,18 @@
 
 package com.android.server.task;
 
+import android.app.task.Task;
 import android.content.Context;
-import android.content.Task;
+import android.util.ArraySet;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.server.task.controllers.TaskStatus;
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
 /**
  * Maintain a list of classes, and accessor methods/logic for these tasks.
  * This class offers the following functionality:
@@ -35,53 +41,122 @@
  *       - This class is <strong>not</strong> thread-safe.
  */
 public class TaskStore {
-
-    /**
-     * Master list, indexed by {@link com.android.server.task.controllers.TaskStatus#hashCode()}.
-     */
-    final SparseArray<TaskStatus> mTasks;
+    private static final String TAG = "TaskManagerStore";
+    /** Threshold to adjust how often we want to write to the db. */
+    private static final int MAX_OPS_BEFORE_WRITE = 1;
+    final ArraySet<TaskStatus> mTasks;
     final Context mContext;
 
+    private int mDirtyOperations;
+
     TaskStore(Context context) {
-        mTasks = intialiseTaskMapFromDisk();
+        mTasks = intialiseTasksFromDisk();
         mContext = context;
+        mDirtyOperations = 0;
     }
 
     /**
-     * Add a task to the master list, persisting it if necessary.
-     * Will first check to see if the task already exists. If so, it will replace it.
-     * {@link android.content.pm.PackageManager} is queried to see if the calling package has
-     * permission to
-     * @param task Task to add.
-     * @return The initialised TaskStatus object if this operation was successful, null if it
-     * failed.
+     * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists,
+     * it will be replaced.
+     * @param taskStatus Task to add.
+     * @return true if the operation succeeded.
      */
-    public TaskStatus addNewTaskForUser(Task task, int userId, int uId,
-                                                     boolean canPersistTask) {
-        TaskStatus taskStatus = TaskStatus.getForTaskAndUser(task, userId, uId);
-        if (canPersistTask && task.isPeriodic()) {
-            if (writeStatusToDisk()) {
-                mTasks.put(taskStatus.hashCode(), taskStatus);
+    public boolean add(TaskStatus taskStatus) {
+        if (taskStatus.isPersisted()) {
+            if (!maybeWriteStatusToDisk()) {
+                return false;
             }
         }
-        return taskStatus;
+        mTasks.remove(taskStatus);
+        mTasks.add(taskStatus);
+        return true;
+    }
+
+    public int size() {
+        return mTasks.size();
     }
 
     /**
-     * Remove the provided task. Will also delete the task if it was persisted. Note that this
-     * function does not return the validity of the operation, as we assume a delete will always
-     * succeed.
-     * @param task Task to remove.
+     * Remove the provided task. Will also delete the task if it was persisted.
+     * @return The TaskStatus that was removed, or null if an invalid token was provided.
      */
-    public void remove(Task task) {
+    public boolean remove(TaskStatus taskStatus) {
+        boolean removed = mTasks.remove(taskStatus);
+        if (!removed) {
+            Slog.e(TAG, "Error removing task: " + taskStatus);
+            return false;
+        } else {
+            maybeWriteStatusToDisk();
+        }
+        return true;
+    }
 
+    /**
+     * Removes all TaskStatus objects for a given uid from the master list. Note that it is
+     * possible to remove a task that is pending/active. This operation will succeed, and the
+     * removal will take effect when the task has completed executing.
+     * @param uid Uid of the requesting app.
+     * @return True if at least one task was removed, false if nothing matching the provided uId
+     * was found.
+     */
+    public boolean removeAllByUid(int uid) {
+        Iterator<TaskStatus> it = mTasks.iterator();
+        boolean removed = false;
+        while (it.hasNext()) {
+            TaskStatus ts = it.next();
+            if (ts.getUid() == uid) {
+                it.remove();
+                removed = true;
+            }
+        }
+        if (removed) {
+            maybeWriteStatusToDisk();
+        }
+        return removed;
+    }
+
+    /**
+     * Remove the TaskStatus that matches the provided uId and taskId.  Note that it is possible
+     * to remove a task that is pending/active. This operation will succeed, and the removal will
+     * take effect when the task has completed executing.
+     * @param uid Uid of the requesting app.
+     * @param taskId Task id, specified at schedule-time.
+     * @return true if a removal occurred, false if the provided parameters didn't match anything.
+     */
+    public boolean remove(int uid, int taskId) {
+        Iterator<TaskStatus> it = mTasks.iterator();
+        while (it.hasNext()) {
+            TaskStatus ts = it.next();
+            if (ts.getUid() == uid && ts.getTaskId() == taskId) {
+                it.remove();
+                maybeWriteStatusToDisk();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return The live array of TaskStatus objects.
+     */
+    public Set<TaskStatus> getTasks() {
+        return mTasks;
     }
 
     /**
      * Every time the state changes we write all the tasks in one swathe, instead of trying to
      * track incremental changes.
+     * @return Whether the operation was successful. This will only fail for e.g. if the system is
+     * low on storage. If this happens, we continue as normal
      */
-    private boolean writeStatusToDisk() {
+    private boolean maybeWriteStatusToDisk() {
+        mDirtyOperations++;
+        if (mDirtyOperations > MAX_OPS_BEFORE_WRITE) {
+            for (TaskStatus ts : mTasks) {
+                //
+            }
+            mDirtyOperations = 0;
+        }
         return true;
     }
 
@@ -90,14 +165,7 @@
      * @return
      */
     // TODO: Implement this.
-    private SparseArray<TaskStatus> intialiseTaskMapFromDisk() {
-        return new SparseArray<TaskStatus>();
-    }
-
-    /**
-     * @return The live array of TaskStatus objects.
-     */
-    public SparseArray<TaskStatus> getTasks() {
-        return mTasks;
+    private ArraySet<TaskStatus> intialiseTasksFromDisk() {
+        return new ArraySet<TaskStatus>();
     }
 }
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index 6a4e1f3..474af8f 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -25,6 +25,7 @@
 import android.net.NetworkInfo;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.server.task.TaskManagerService;
 
@@ -32,21 +33,33 @@
 import java.util.List;
 
 /**
- *
+ * Handles changes in connectivity.
+ * We are only interested in metered vs. unmetered networks, and we're interested in them on a
+ * per-user basis.
  */
 public class ConnectivityController extends StateController {
     private static final String TAG = "TaskManager.Connectivity";
+    private static final boolean DEBUG = true;
 
     private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
     private final BroadcastReceiver mConnectivityChangedReceiver =
             new ConnectivityChangedReceiver();
+    /** Singleton. */
+    private static ConnectivityController mSingleton;
 
     /** Track whether the latest active network is metered. */
     private boolean mMetered;
     /** Track whether the latest active network is connected. */
     private boolean mConnectivity;
 
-    public ConnectivityController(TaskManagerService service) {
+    public static synchronized ConnectivityController get(TaskManagerService taskManager) {
+        if (mSingleton == null) {
+            mSingleton = new ConnectivityController(taskManager);
+        }
+        return mSingleton;
+    }
+
+    private ConnectivityController(TaskManagerService service) {
         super(service);
         // Register connectivity changed BR.
         IntentFilter intentFilter = new IntentFilter();
@@ -56,7 +69,7 @@
     }
 
     @Override
-    public void maybeTrackTaskState(TaskStatus taskStatus) {
+    public synchronized void maybeStartTrackingTask(TaskStatus taskStatus) {
         if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) {
             taskStatus.connectivityConstraintSatisfied.set(mConnectivity);
             taskStatus.meteredConstraintSatisfied.set(mMetered);
@@ -65,7 +78,7 @@
     }
 
     @Override
-    public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+    public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
         mTrackedTasks.remove(taskStatus);
     }
 
@@ -73,16 +86,20 @@
      * @param userId Id of the user for whom we are updating the connectivity state.
      */
     private void updateTrackedTasks(int userId) {
+        boolean changed = false;
         for (TaskStatus ts : mTrackedTasks) {
-            if (ts.userId != userId) {
+            if (ts.getUserId() != userId) {
                 continue;
             }
             boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
             boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
             if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
-                    mStateChangedListener.onTaskStateChanged(ts);
+                    changed = true;
             }
         }
+        if (changed) {
+            mStateChangedListener.onControllerStateChanged();
+        }
     }
 
     class ConnectivityChangedReceiver extends BroadcastReceiver {
@@ -106,7 +123,7 @@
                         context.getSystemService(Context.CONNECTIVITY_SERVICE);
                 final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
                 // This broadcast gets sent a lot, only update if the active network has changed.
-                if (activeNetwork.getType() == networkType) {
+                if (activeNetwork != null && activeNetwork.getType() == networkType) {
                     final int userid = context.getUserId();
                     mMetered = false;
                     mConnectivity =
@@ -117,7 +134,9 @@
                     updateTrackedTasks(userid);
                 }
             } else {
-                Log.w(TAG, "Unrecognised action in intent: " + action);
+                if (DEBUG) {
+                    Slog.d(TAG, "Unrecognised action in intent: " + action);
+                }
             }
         }
     };
diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/task/controllers/IdleController.java
index a319a31..9489644 100644
--- a/services/core/java/com/android/server/task/controllers/IdleController.java
+++ b/services/core/java/com/android/server/task/controllers/IdleController.java
@@ -49,7 +49,7 @@
     private static Object sCreationLock = new Object();
     private static volatile IdleController sController;
 
-    public IdleController getController(TaskManagerService service) {
+    public static IdleController get(TaskManagerService service) {
         synchronized (sCreationLock) {
             if (sController == null) {
                 sController = new IdleController(service);
@@ -67,7 +67,7 @@
      * StateController interface
      */
     @Override
-    public void maybeTrackTaskState(TaskStatus taskStatus) {
+    public void maybeStartTrackingTask(TaskStatus taskStatus) {
         if (taskStatus.hasIdleConstraint()) {
             synchronized (mTrackedTasks) {
                 mTrackedTasks.add(taskStatus);
@@ -77,7 +77,7 @@
     }
 
     @Override
-    public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+    public void maybeStopTrackingTask(TaskStatus taskStatus) {
         synchronized (mTrackedTasks) {
             mTrackedTasks.remove(taskStatus);
         }
@@ -90,9 +90,9 @@
         synchronized (mTrackedTasks) {
             for (TaskStatus task : mTrackedTasks) {
                 task.idleConstraintSatisfied.set(isIdle);
-                mStateChangedListener.onTaskStateChanged(task);
             }
         }
+        mStateChangedListener.onControllerStateChanged();
     }
 
     /**
diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java
index e1cd662..ed31eac 100644
--- a/services/core/java/com/android/server/task/controllers/StateController.java
+++ b/services/core/java/com/android/server/task/controllers/StateController.java
@@ -42,10 +42,10 @@
      * Also called when updating a task, so implementing controllers have to be aware of
      * preexisting tasks.
      */
-    public abstract void maybeTrackTaskState(TaskStatus taskStatus);
+    public abstract void maybeStartTrackingTask(TaskStatus taskStatus);
     /**
      * Remove task - this will happen if the task is cancelled, completed, etc.
      */
-    public abstract void removeTaskStateIfTracked(TaskStatus taskStatus);
+    public abstract void maybeStopTrackingTask(TaskStatus taskStatus);
 
 }
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d96fedc..b7f84ec 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -16,17 +16,18 @@
 
 package com.android.server.task.controllers;
 
+import android.app.task.Task;
 import android.content.ComponentName;
-import android.content.Task;
-import android.content.pm.PackageParser;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.os.UserHandle;
 
+import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Uniquely identifies a task internally.
- * Created from the public {@link android.content.Task} object when it lands on the scheduler.
+ * Created from the public {@link android.app.task.Task} object when it lands on the scheduler.
  * Contains current state of the requirements of the task, as well as a function to evaluate
  * whether it's ready to run.
  * This object is shared among the various controllers - hence why the different fields are atomic.
@@ -36,80 +37,88 @@
  * @hide
  */
 public class TaskStatus {
-    final int taskId;
-    final int userId;
+    final Task task;
     final int uId;
-    final ComponentName component;
-    final Bundle extras;
 
+    /** At reschedule time we need to know whether to update task on disk. */
+    final boolean persisted;
+
+    // Constraints.
     final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
-    final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
 
-    private final boolean hasChargingConstraint;
-    private final boolean hasTimingConstraint;
-    private final boolean hasIdleConstraint;
-    private final boolean hasMeteredConstraint;
-    private final boolean hasConnectivityConstraint;
-
+    /**
+     * Earliest point in the future at which this task will be eligible to run. A value of 0
+     * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
+     */
     private long earliestRunTimeElapsedMillis;
+    /**
+     * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE}
+     * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
+     */
     private long latestRunTimeElapsedMillis;
 
-    /** Provide a unique handle to the service that this task will be run on. */
+    private final int numFailures;
+
+    /** Provide a handle to the service that this task will be run on. */
     public int getServiceToken() {
-        return component.hashCode() + userId;
+        return uId;
     }
 
-    /** Generate a TaskStatus object for a given task and uid. */
-    // TODO: reimplement this to reuse these objects instead of creating a new one each time?
-    public static TaskStatus getForTaskAndUser(Task task, int userId, int uId) {
-        return new TaskStatus(task, userId, uId);
-    }
-
-    /** Set up the state of a newly scheduled task. */
-    TaskStatus(Task task, int userId, int uId) {
-        this.taskId = task.getTaskId();
-        this.userId = userId;
-        this.component = task.getService();
-        this.extras = task.getExtras();
+    /** Create a newly scheduled task. */
+    public TaskStatus(Task task, int uId, boolean persisted) {
+        this.task = task;
         this.uId = uId;
+        this.numFailures = 0;
+        this.persisted = persisted;
 
-        hasChargingConstraint = task.isRequireCharging();
-        hasIdleConstraint = task.isRequireDeviceIdle();
-
+        final long elapsedNow = SystemClock.elapsedRealtime();
         // Timing constraints
         if (task.isPeriodic()) {
-            long elapsedNow = SystemClock.elapsedRealtime();
             earliestRunTimeElapsedMillis = elapsedNow;
             latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis();
-            hasTimingConstraint = true;
-        } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) {
-            earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ?
-                    task.getMinLatencyMillis() : Long.MAX_VALUE;
-            latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ?
-                    task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
-            hasTimingConstraint = true;
         } else {
-            hasTimingConstraint = false;
+            earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ?
+                    elapsedNow + task.getMinLatencyMillis() : 0L;
+            latestRunTimeElapsedMillis = task.hasLateConstraint() ?
+                    elapsedNow + task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
         }
+    }
 
-        // Networking constraints
-        hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
-        hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
+    public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsed,
+                      long newLatestRuntimeElapsed, int backoffAttempt) {
+        this.task = rescheduling.task;
+
+        this.uId = rescheduling.getUid();
+        this.persisted = rescheduling.isPersisted();
+        this.numFailures = backoffAttempt;
+
+        earliestRunTimeElapsedMillis = newEarliestRuntimeElapsed;
+        latestRunTimeElapsedMillis = newLatestRuntimeElapsed;
+    }
+
+    public Task getTask() {
+        return task;
     }
 
     public int getTaskId() {
-        return taskId;
+        return task.getId();
+    }
+
+    public int getNumFailures() {
+        return numFailures;
     }
 
     public ComponentName getServiceComponent() {
-        return component;
+        return task.getService();
     }
 
     public int getUserId() {
-        return userId;
+        return UserHandle.getUserId(uId);
     }
 
     public int getUid() {
@@ -117,53 +126,61 @@
     }
 
     public Bundle getExtras() {
-        return extras;
+        return task.getExtras();
     }
 
-    boolean hasConnectivityConstraint() {
-        return hasConnectivityConstraint;
+    public boolean hasConnectivityConstraint() {
+        return task.getNetworkCapabilities() == Task.NetworkType.ANY;
     }
 
-    boolean hasMeteredConstraint() {
-        return hasMeteredConstraint;
+    public boolean hasMeteredConstraint() {
+        return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
     }
 
-    boolean hasChargingConstraint() {
-        return hasChargingConstraint;
+    public boolean hasChargingConstraint() {
+        return task.isRequireCharging();
     }
 
-    boolean hasTimingConstraint() {
-        return hasTimingConstraint;
+    public boolean hasTimingDelayConstraint() {
+        return earliestRunTimeElapsedMillis != 0L;
     }
 
-    boolean hasIdleConstraint() {
-        return hasIdleConstraint;
+    public boolean hasDeadlineConstraint() {
+        return latestRunTimeElapsedMillis != Long.MAX_VALUE;
     }
 
-    long getEarliestRunTime() {
+    public boolean hasIdleConstraint() {
+        return task.isRequireDeviceIdle();
+    }
+
+    public long getEarliestRunTime() {
         return earliestRunTimeElapsedMillis;
     }
 
-    long getLatestRunTime() {
+    public long getLatestRunTimeElapsed() {
         return latestRunTimeElapsedMillis;
     }
 
+    public boolean isPersisted() {
+        return persisted;
+    }
     /**
-     * @return whether this task is ready to run, based on its requirements.
+     * @return Whether or not this task is ready to run, based on its requirements.
      */
     public synchronized boolean isReady() {
-        return (!hasChargingConstraint || chargingConstraintSatisfied.get())
-                && (!hasTimingConstraint || timeConstraintSatisfied.get())
-                && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get())
-                && (!hasMeteredConstraint || meteredConstraintSatisfied.get())
-                && (!hasIdleConstraint || idleConstraintSatisfied.get());
+        return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
+                && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
+                && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
+                && (!hasMeteredConstraint() || meteredConstraintSatisfied.get())
+                && (!hasIdleConstraint() || idleConstraintSatisfied.get())
+                && (!hasDeadlineConstraint() || deadlineConstraintSatisfied.get());
     }
 
     @Override
     public int hashCode() {
-        int result = component.hashCode();
-        result = 31 * result + taskId;
-        result = 31 * result + userId;
+        int result = getServiceComponent().hashCode();
+        result = 31 * result + task.getId();
+        result = 31 * result + uId;
         return result;
     }
 
@@ -173,8 +190,15 @@
         if (!(o instanceof TaskStatus)) return false;
 
         TaskStatus that = (TaskStatus) o;
-        return ((taskId == that.taskId)
-                && (userId == that.userId)
-                && (component.equals(that.component)));
+        return ((task.getId() == that.task.getId())
+                && (uId == that.uId)
+                && (getServiceComponent().equals(that.getServiceComponent())));
+    }
+
+    // Dumpsys infrastructure
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("Task "); pw.println(task.getId());
+        pw.print(prefix); pw.print("uid="); pw.println(uId);
+        pw.print(prefix); pw.print("component="); pw.println(task.getService());
     }
 }
diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java
index 6d97a53..72f312c 100644
--- a/services/core/java/com/android/server/task/controllers/TimeController.java
+++ b/services/core/java/com/android/server/task/controllers/TimeController.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.SystemClock;
-import android.util.Log;
 
 import com.android.server.task.TaskManagerService;
 
@@ -54,8 +53,17 @@
     private AlarmManager mAlarmService = null;
     /** List of tracked tasks, sorted asc. by deadline */
     private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+    /** Singleton. */
+    private static TimeController mSingleton;
 
-    public TimeController(TaskManagerService service) {
+    public static synchronized TimeController get(TaskManagerService taskManager) {
+        if (mSingleton == null) {
+            mSingleton = new TimeController(taskManager);
+        }
+        return mSingleton;
+    }
+
+    private TimeController(TaskManagerService service) {
         super(service);
         mTaskExpiredAlarmIntent =
                 PendingIntent.getBroadcast(mContext, 0 /* ignored */,
@@ -75,8 +83,8 @@
      * list.
      */
     @Override
-    public synchronized void maybeTrackTaskState(TaskStatus task) {
-        if (task.hasTimingConstraint()) {
+    public synchronized void maybeStartTrackingTask(TaskStatus task) {
+        if (task.hasTimingDelayConstraint()) {
             ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size());
             while (it.hasPrevious()) {
                 TaskStatus ts = it.previous();
@@ -85,13 +93,13 @@
                     it.remove();
                     it.add(task);
                     break;
-                } else if (ts.getLatestRunTime() < task.getLatestRunTime()) {
+                } else if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) {
                     // Insert
                     it.add(task);
                     break;
                 }
             }
-            maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime());
+            maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTimeElapsed());
         }
     }
 
@@ -100,12 +108,12 @@
      * so, update them.
      */
     @Override
-    public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) {
+    public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
         if (mTrackedTasks.remove(taskStatus)) {
             if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) {
                 handleTaskDelayExpired();
             }
-            if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) {
+            if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTimeElapsed()) {
                 handleTaskDeadlineExpired();
             }
         }
@@ -140,10 +148,10 @@
      * back and forth.
      */
     private boolean canStopTrackingTask(TaskStatus taskStatus) {
-        final long elapsedNowMillis = SystemClock.elapsedRealtime();
-        return taskStatus.timeConstraintSatisfied.get() &&
-                (taskStatus.getLatestRunTime() == Long.MAX_VALUE ||
-                        taskStatus.getLatestRunTime() < elapsedNowMillis);
+        return (!taskStatus.hasTimingDelayConstraint() ||
+                taskStatus.timeDelayConstraintSatisfied.get()) &&
+                (!taskStatus.hasDeadlineConstraint() ||
+                        taskStatus.deadlineConstraintSatisfied.get());
     }
 
     private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
@@ -174,10 +182,10 @@
         Iterator<TaskStatus> it = mTrackedTasks.iterator();
         while (it.hasNext()) {
             TaskStatus ts = it.next();
-            final long taskDeadline = ts.getLatestRunTime();
+            final long taskDeadline = ts.getLatestRunTimeElapsed();
 
             if (taskDeadline <= nowElapsedMillis) {
-                ts.timeConstraintSatisfied.set(true);
+                ts.deadlineConstraintSatisfied.set(true);
                 mStateChangedListener.onTaskDeadlineExpired(ts);
                 it.remove();
             } else {  // Sorted by expiry time, so take the next one and stop.
@@ -199,10 +207,12 @@
         Iterator<TaskStatus> it = mTrackedTasks.iterator();
         while (it.hasNext()) {
             final TaskStatus ts = it.next();
+            if (!ts.hasTimingDelayConstraint()) {
+                continue;
+            }
             final long taskDelayTime = ts.getEarliestRunTime();
             if (taskDelayTime < nowElapsedMillis) {
-                ts.timeConstraintSatisfied.set(true);
-                mStateChangedListener.onTaskStateChanged(ts);
+                ts.timeDelayConstraintSatisfied.set(true);
                 if (canStopTrackingTask(ts)) {
                     it.remove();
                 }
@@ -212,6 +222,7 @@
                 }
             }
         }
+        mStateChangedListener.onControllerStateChanged();
         maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE);
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 47ce3b6..f18939f 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -63,6 +63,11 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_GRANT_TRUST:
+                    if (!isConnected()) {
+                        Log.w(TAG, "Agent is not connected, cannot grant trust: "
+                                + mName.flattenToShortString());
+                        return;
+                    }
                     mTrusted = true;
                     mMessage = (CharSequence) msg.obj;
                     boolean initiatedByUser = msg.arg1 != 0;
@@ -119,6 +124,7 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString());
             mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
+            mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
             setCallback(mCallback);
         }
 
@@ -179,7 +185,10 @@
 
     public void unbind() {
         if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString());
+        mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
         mContext.unbindService(mConnection);
+        mTrustAgentService = null;
+        mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
     }
 
     public boolean isConnected() {
diff --git a/services/core/java/com/android/server/trust/TrustArchive.java b/services/core/java/com/android/server/trust/TrustArchive.java
index aad156c..56950d2 100644
--- a/services/core/java/com/android/server/trust/TrustArchive.java
+++ b/services/core/java/com/android/server/trust/TrustArchive.java
@@ -33,6 +33,8 @@
     private static final int TYPE_REVOKE_TRUST = 1;
     private static final int TYPE_TRUST_TIMEOUT = 2;
     private static final int TYPE_AGENT_DIED = 3;
+    private static final int TYPE_AGENT_CONNECTED = 4;
+    private static final int TYPE_AGENT_STOPPED = 5;
 
     private static final int HISTORY_LIMIT = 200;
 
@@ -79,6 +81,14 @@
         addEvent(new Event(TYPE_AGENT_DIED, userId, agent, null, 0, false));
     }
 
+    public void logAgentConnected(int userId, ComponentName agent) {
+        addEvent(new Event(TYPE_AGENT_CONNECTED, userId, agent, null, 0, false));
+    }
+
+    public void logAgentStopped(int userId, ComponentName agent) {
+        addEvent(new Event(TYPE_AGENT_STOPPED, userId, agent, null, 0, false));
+    }
+
     private void addEvent(Event e) {
         if (mEvents.size() >= HISTORY_LIMIT) {
             mEvents.removeFirst();
@@ -152,6 +162,10 @@
                 return "TrustTimeout";
             case TYPE_AGENT_DIED:
                 return "AgentDied";
+            case TYPE_AGENT_CONNECTED:
+                return "AgentConnected";
+            case TYPE_AGENT_STOPPED:
+                return "AgentStopped";
             default:
                 return "Unknown(" + type + ")";
         }
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
new file mode 100644
index 0000000..4bdd2be
--- /dev/null
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tv;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.tv.TvInputHardwareInfo;
+import android.tv.TvStreamConfig;
+import android.view.Surface;
+
+/**
+ * Provides access to the low-level TV input hardware abstraction layer.
+ */
+final class TvInputHal {
+    public final static int SUCCESS = 0;
+    public final static int ERROR_NO_INIT = -1;
+    public final static int ERROR_STALE_CONFIG = -2;
+    public final static int ERROR_UNKNOWN = -3;
+
+    public static final int TYPE_HDMI = 1;
+    public static final int TYPE_BUILT_IN_TUNER = 2;
+    public static final int TYPE_PASSTHROUGH = 3;
+
+    public interface Callback {
+        public void onDeviceAvailable(
+                TvInputHardwareInfo info, TvStreamConfig[] configs);
+        public void onDeviceUnavailable(int deviceId);
+        public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
+    }
+
+    private native long nativeOpen();
+
+    private static native int nativeSetSurface(long ptr, int deviceId, int streamId,
+            Surface surface);
+    private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
+            int generation);
+    private static native void nativeClose(long ptr);
+
+    private long mPtr = 0l;
+    private final Callback mCallback;
+    private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
+    private final Handler mHandler;
+    private int mStreamConfigGeneration = 0;
+    private TvStreamConfig[] mStreamConfigs;
+
+    public TvInputHal(Callback callback) {
+        mCallback = callback;
+        mThread.start();
+        mHandler = new Handler(mThread.getLooper());
+    }
+
+    public void init() {
+        mPtr = nativeOpen();
+    }
+
+    public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) {
+        if (mPtr == 0) {
+            return ERROR_NO_INIT;
+        }
+        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
+            return ERROR_STALE_CONFIG;
+        }
+        if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
+            return SUCCESS;
+        } else {
+            return ERROR_UNKNOWN;
+        }
+    }
+
+    public void close() {
+        if (mPtr != 0l) {
+            nativeClose(mPtr);
+            mThread.quitSafely();
+        }
+    }
+
+    private synchronized void retrieveStreamConfigs(int deviceId) {
+        ++mStreamConfigGeneration;
+        mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration);
+    }
+
+    // Called from native
+    private void deviceAvailableFromNative(int deviceId, int type) {
+        final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                retrieveStreamConfigs(info.getDeviceId());
+                mCallback.onDeviceAvailable(info, mStreamConfigs);
+            }
+        });
+    }
+
+    private void deviceUnavailableFromNative(int deviceId) {
+        final int id = deviceId;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mCallback.onDeviceUnavailable(id);
+            }
+        });
+    }
+
+    private void streamConfigsChangedFromNative(int deviceId) {
+        final int id = deviceId;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                retrieveStreamConfigs(id);
+                mCallback.onStreamConfigurationChanged(id, mStreamConfigs);
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
new file mode 100644
index 0000000..b95b0f0
--- /dev/null
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -0,0 +1,308 @@
+/*
+ * 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.server.tv;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
+import android.tv.TvInputHardwareInfo;
+import android.tv.TvStreamConfig;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A helper class for TvInputManagerService to handle TV input hardware.
+ *
+ * This class does a basic connection management and forwarding calls to TvInputHal which eventually
+ * calls to tv_input HAL module.
+ *
+ * @hide
+ */
+class TvInputHardwareManager implements TvInputHal.Callback {
+    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
+    private final TvInputHal mHal = new TvInputHal(this);
+    private final SparseArray<Connection> mConnections = new SparseArray<Connection>();
+    private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
+    private final Context mContext;
+    private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
+
+    private final Object mLock = new Object();
+
+    public TvInputHardwareManager(Context context) {
+        mContext = context;
+        // TODO(hdmi): mHdmiManager = mContext.getSystemService(...);
+        // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient();
+        mHal.init();
+    }
+
+    @Override
+    public void onDeviceAvailable(
+            TvInputHardwareInfo info, TvStreamConfig[] configs) {
+        synchronized (mLock) {
+            Connection connection = new Connection(info);
+            connection.updateConfigsLocked(configs);
+            mConnections.put(info.getDeviceId(), connection);
+            buildInfoListLocked();
+            // TODO: notify if necessary
+        }
+    }
+
+    private void buildInfoListLocked() {
+        mInfoList.clear();
+        for (int i = 0; i < mConnections.size(); ++i) {
+            mInfoList.add(mConnections.valueAt(i).getInfoLocked());
+        }
+    }
+
+    @Override
+    public void onDeviceUnavailable(int deviceId) {
+        synchronized (mLock) {
+            Connection connection = mConnections.get(deviceId);
+            if (connection == null) {
+                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
+                return;
+            }
+            connection.resetLocked(null, null, null, null);
+            mConnections.remove(deviceId);
+            buildInfoListLocked();
+            // TODO: notify if necessary
+        }
+    }
+
+    @Override
+    public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
+        synchronized (mLock) {
+            Connection connection = mConnections.get(deviceId);
+            if (connection == null) {
+                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
+                        + deviceId);
+                return;
+            }
+            connection.updateConfigsLocked(configs);
+            try {
+                connection.getCallbackLocked().onStreamConfigChanged(configs);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "onStreamConfigurationChanged: " + e);
+            }
+        }
+    }
+
+    public List<TvInputHardwareInfo> getHardwareList() {
+        synchronized (mLock) {
+            return mInfoList;
+        }
+    }
+
+    /**
+     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
+     * the object, and if more than one process attempts to create hardware with the same deviceId,
+     * the latest service will get the object and all the other hardware are released. The
+     * release is notified via ITvInputHardwareCallback.onReleased().
+     */
+    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
+            int callingUid, int resolvedUserId) {
+        if (callback == null) {
+            throw new NullPointerException();
+        }
+        synchronized (mLock) {
+            Connection connection = mConnections.get(deviceId);
+            if (connection == null) {
+                Slog.e(TAG, "Invalid deviceId : " + deviceId);
+                return null;
+            }
+            if (connection.getCallingUidLocked() != callingUid
+                    || connection.getResolvedUserIdLocked() != resolvedUserId) {
+                TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
+                try {
+                    callback.asBinder().linkToDeath(connection, 0);
+                } catch (RemoteException e) {
+                    hardware.release();
+                    return null;
+                }
+                connection.resetLocked(hardware, callback, callingUid, resolvedUserId);
+            }
+            return connection.getHardwareLocked();
+        }
+    }
+
+    /**
+     * Release the specified hardware.
+     */
+    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
+            int resolvedUserId) {
+        synchronized (mLock) {
+            Connection connection = mConnections.get(deviceId);
+            if (connection == null) {
+                Slog.e(TAG, "Invalid deviceId : " + deviceId);
+                return;
+            }
+            if (connection.getHardwareLocked() != hardware
+                    || connection.getCallingUidLocked() != callingUid
+                    || connection.getResolvedUserIdLocked() != resolvedUserId) {
+                return;
+            }
+            connection.resetLocked(null, null, null, null);
+        }
+    }
+
+    private class Connection implements IBinder.DeathRecipient {
+        private final TvInputHardwareInfo mInfo;
+        private TvInputHardwareImpl mHardware = null;
+        private ITvInputHardwareCallback mCallback;
+        private TvStreamConfig[] mConfigs = null;
+        private Integer mCallingUid = null;
+        private Integer mResolvedUserId = null;
+
+        public Connection(TvInputHardwareInfo info) {
+            mInfo = info;
+        }
+
+        // *Locked methods assume TvInputHardwareManager.mLock is held.
+
+        public void resetLocked(TvInputHardwareImpl hardware,
+                ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) {
+            if (mHardware != null) {
+                try {
+                    mCallback.onReleased();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Connection::resetHardware: " + e);
+                }
+                mHardware.release();
+            }
+            mHardware = hardware;
+            mCallback = callback;
+            mCallingUid = callingUid;
+            mResolvedUserId = resolvedUserId;
+
+            if (mHardware != null && mCallback != null) {
+                try {
+                    mCallback.onStreamConfigChanged(getConfigsLocked());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Connection::resetHardware: " + e);
+                }
+            }
+        }
+
+        public void updateConfigsLocked(TvStreamConfig[] configs) {
+            mConfigs = configs;
+        }
+
+        public TvInputHardwareInfo getInfoLocked() {
+            return mInfo;
+        }
+
+        public ITvInputHardware getHardwareLocked() {
+            return mHardware;
+        }
+
+        public ITvInputHardwareCallback getCallbackLocked() {
+            return mCallback;
+        }
+
+        public TvStreamConfig[] getConfigsLocked() {
+            return mConfigs;
+        }
+
+        public int getCallingUidLocked() {
+            return mCallingUid;
+        }
+
+        public int getResolvedUserIdLocked() {
+            return mResolvedUserId;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                resetLocked(null, null, null, null);
+            }
+        }
+    }
+
+    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
+        private final TvInputHardwareInfo mInfo;
+        private boolean mReleased = false;
+        private final Object mImplLock = new Object();
+
+        public TvInputHardwareImpl(TvInputHardwareInfo info) {
+            mInfo = info;
+        }
+
+        public void release() {
+            synchronized (mImplLock) {
+                mReleased = true;
+            }
+        }
+
+        @Override
+        public boolean setSurface(Surface surface, TvStreamConfig config)
+                throws RemoteException {
+            synchronized (mImplLock) {
+                if (mReleased) {
+                    throw new IllegalStateException("Device already released.");
+                }
+                if (mInfo.getType() == TvInputHal.TYPE_HDMI) {
+                    if (surface != null) {
+                        // Set "Active Source" for HDMI.
+                        // TODO(hdmi): mHdmiClient.deviceSelect(...);
+                        mActiveHdmiSources.add(mInfo.getDeviceId());
+                    } else {
+                        mActiveHdmiSources.remove(mInfo.getDeviceId());
+                        if (mActiveHdmiSources.size() == 0) {
+                            // Tell HDMI that no HDMI source is active
+                            // TODO(hdmi): mHdmiClient.portSelect(null);
+                        }
+                    }
+                }
+                return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS;
+            }
+        }
+
+        @Override
+        public void setVolume(float volume) throws RemoteException {
+            synchronized (mImplLock) {
+                if (mReleased) {
+                    throw new IllegalStateException("Device already released.");
+                }
+            }
+            // TODO
+        }
+
+        @Override
+        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
+            synchronized (mImplLock) {
+                if (mReleased) {
+                    throw new IllegalStateException("Device already released.");
+                }
+            }
+            if (mInfo.getType() != TvInputHal.TYPE_HDMI) {
+                return false;
+            }
+            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
+            return false;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8ad7fff..6c38a4c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -42,11 +42,14 @@
 import android.os.UserHandle;
 import android.provider.TvContract;
 import android.tv.ITvInputClient;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
 import android.tv.ITvInputManager;
 import android.tv.ITvInputService;
 import android.tv.ITvInputServiceCallback;
 import android.tv.ITvInputSession;
 import android.tv.ITvInputSessionCallback;
+import android.tv.TvInputHardwareInfo;
 import android.tv.TvInputInfo;
 import android.tv.TvInputService;
 import android.util.Slog;
@@ -71,6 +74,7 @@
     private static final String TAG = "TvInputManagerService";
 
     private final Context mContext;
+    private final TvInputHardwareManager mTvInputHardwareManager;
 
     private final ContentResolver mContentResolver;
 
@@ -92,6 +96,7 @@
         mContentResolver = context.getContentResolver();
         mLogHandler = new LogHandler(IoThread.get().getLooper());
 
+        mTvInputHardwareManager = new TvInputHardwareManager(context);
         registerBroadcastReceivers();
 
         synchronized (mLock) {
@@ -730,6 +735,64 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
+
+        @Override
+        public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
+            if (mContext.checkCallingPermission(
+                    android.Manifest.permission.TV_INPUT_HARDWARE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return null;
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return mTvInputHardwareManager.getHardwareList();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public ITvInputHardware acquireTvInputHardware(int deviceId,
+                ITvInputHardwareCallback callback, int userId) throws RemoteException {
+            if (mContext.checkCallingPermission(
+                    android.Manifest.permission.TV_INPUT_HARDWARE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return null;
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "acquireTvInputHardware");
+            try {
+                return mTvInputHardwareManager.acquireHardware(
+                        deviceId, callback, callingUid, resolvedUserId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
+                throws RemoteException {
+            if (mContext.checkCallingPermission(
+                    android.Manifest.permission.TV_INPUT_HARDWARE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "releaseTvInputHardware");
+            try {
+                mTvInputHardwareManager.releaseHardware(
+                        deviceId, hardware, callingUid, resolvedUserId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     }
 
     private static final class UserState {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e2d2ac6..e007600 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -24,9 +24,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IRemoteCallback;
-import android.os.SystemProperties;
 import android.util.Slog;
-import android.view.View;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
@@ -117,7 +115,7 @@
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f;
 
     private static final int DEFAULT_APP_TRANSITION_DURATION = 250;
-    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 225;
+    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 275;
 
     private final Context mContext;
     private final Handler mH;
@@ -299,7 +297,7 @@
         return null;
     }
 
-    Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
+    Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
         int anim = 0;
         Context context = mContext;
         if (animAttr >= 0) {
@@ -315,7 +313,19 @@
         return null;
     }
 
-    private Animation loadAnimation(String packageName, int resId) {
+    Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
+        Context context = mContext;
+        if (resId >= 0) {
+            AttributeCache.Entry ent = getCachedAnimations(lp);
+            if (ent != null) {
+                context = ent.context;
+            }
+            return AnimationUtils.loadAnimation(context, resId);
+        }
+        return null;
+    }
+
+    private Animation loadAnimationRes(String packageName, int resId) {
         int anim = 0;
         Context context = mContext;
         if (resId >= 0) {
@@ -695,11 +705,31 @@
 
 
     Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-                            int appWidth, int appHeight, int orientation,
-                            Rect containingFrame, Rect contentInsets, boolean isFullScreen) {
+            int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
+            boolean isFullScreen, boolean isVoiceInteraction) {
         Animation a;
-        if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
-            a = loadAnimation(mNextAppTransitionPackage, enter ?
+        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
+                || transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_TO_FRONT)) {
+            a = loadAnimationRes(lp, enter
+                    ? com.android.internal.R.anim.voice_activity_open_enter
+                    : com.android.internal.R.anim.voice_activity_open_exit);
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "applyAnimation voice:"
+                    + " anim=" + a + " transit=" + transit + " isEntrance=" + enter
+                    + " Callers=" + Debug.getCallers(3));
+        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
+                || transit == TRANSIT_TASK_CLOSE
+                || transit == TRANSIT_TASK_TO_BACK)) {
+            a = loadAnimationRes(lp, enter
+                    ? com.android.internal.R.anim.voice_activity_close_enter
+                    : com.android.internal.R.anim.voice_activity_close_exit);
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "applyAnimation voice:"
+                    + " anim=" + a + " transit=" + transit + " isEntrance=" + enter
+                    + " Callers=" + Debug.getCallers(3));
+        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
+            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
                     mNextAppTransitionEnter : mNextAppTransitionExit);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
@@ -782,7 +812,7 @@
                             : WindowAnimation_wallpaperIntraCloseExitAnimation;
                     break;
             }
-            a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
+            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation:"
                     + " anim=" + a
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ca4ad8a..12c15e2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -50,6 +50,8 @@
 
     final WindowAnimator mAnimator;
 
+    final boolean voiceInteraction;
+
     int groupId = -1;
     boolean appFullscreen;
     int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -107,11 +109,13 @@
 
     boolean mDeferRemoval;
 
-    AppWindowToken(WindowManagerService _service, IApplicationToken _token) {
+    AppWindowToken(WindowManagerService _service, IApplicationToken _token,
+            boolean _voiceInteraction) {
         super(_service, _token.asBinder(),
                 WindowManager.LayoutParams.TYPE_APPLICATION, true);
         appWindowToken = this;
         appToken = _token;
+        voiceInteraction = _voiceInteraction;
         mInputApplicationHandle = new InputApplicationHandle(this);
         mAnimator = service.mAnimator;
         mAppAnimator = new AppWindowAnimator(this);
@@ -249,7 +253,7 @@
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
         if (appToken != null) {
-            pw.print(prefix); pw.println("app=true");
+            pw.print(prefix); pw.print("app=true voiceInteraction="); pw.println(voiceInteraction);
         }
         if (allAppWindows.size() > 0) {
             pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 266527d..6fdd535 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -36,6 +36,7 @@
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.WindowManagerPolicy;
+import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 
 import com.android.server.wm.WindowManagerService.LayoutFields;
@@ -50,6 +51,9 @@
 public class WindowAnimator {
     private static final String TAG = "WindowAnimator";
 
+    /** How long to give statusbar to clear the private keyguard flag when animating out */
+    private static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000;
+
     final WindowManagerService mService;
     final Context mContext;
     final WindowManagerPolicy mPolicy;
@@ -82,6 +86,8 @@
 
     boolean mInitialized = false;
 
+    boolean mKeyguardGoingAway;
+
     // forceHiding states.
     static final int KEYGUARD_NOT_SHOWN     = 0;
     static final int KEYGUARD_ANIMATING_IN  = 1;
@@ -213,6 +219,29 @@
         final WindowList windows = mService.getWindowListLocked(displayId);
         ArrayList<WindowStateAnimator> unForceHiding = null;
         boolean wallpaperInUnForceHiding = false;
+
+        if (mKeyguardGoingAway) {
+            for (int i = windows.size() - 1; i >= 0; i--) {
+                WindowState win = windows.get(i);
+                if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+                    continue;
+                }
+                final WindowStateAnimator winAnimator = win.mWinAnimator;
+                if (mPolicy.doesForceHide(win.mAttrs)) {
+                    if (!winAnimator.mAnimating) {
+                        // Create a new animation to delay until keyguard is gone on its own.
+                        winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
+                        winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
+                        winAnimator.mAnimationIsEntrance = false;
+                    }
+                } else {
+                    mKeyguardGoingAway = false;
+                    winAnimator.clearAnimation();
+                }
+                break;
+            }
+        }
+
         mForceHiding = KEYGUARD_NOT_SHOWN;
 
         for (int i = windows.size() - 1; i >= 0; i--) {
@@ -239,7 +268,7 @@
                     }
                 }
 
-                if (mPolicy.doesForceHide(win, win.mAttrs)) {
+                if (mPolicy.doesForceHide(win.mAttrs)) {
                     if (!wasAnimating && nowAnimating) {
                         if (WindowManagerService.DEBUG_ANIM ||
                                 WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -252,6 +281,11 @@
                                     getPendingLayoutChanges(displayId));
                         }
                         mService.mFocusMayChange = true;
+                    } else if (mKeyguardGoingAway && !nowAnimating) {
+                        // Timeout!!
+                        Slog.e(TAG, "Timeout waiting for animation to startup");
+                        mPolicy.startKeyguardExitAnimation(0);
+                        mKeyguardGoingAway = false;
                     }
                     if (win.isReadyForDisplay()) {
                         if (nowAnimating) {
@@ -265,7 +299,7 @@
                         }
                     }
                     if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
-                            "Force hide " + mForceHiding
+                            "Force hide " + forceHidingToString()
                             + " hasSurface=" + win.mHasSurface
                             + " policyVis=" + win.mPolicyVisibility
                             + " destroying=" + win.mDestroying
@@ -349,12 +383,18 @@
         // If we have windows that are being show due to them no longer
         // being force-hidden, apply the appropriate animation to them.
         if (unForceHiding != null) {
+            boolean startKeyguardExit = true;
             for (int i=unForceHiding.size()-1; i>=0; i--) {
                 Animation a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding);
                 if (a != null) {
                     final WindowStateAnimator winAnimator = unForceHiding.get(i);
                     winAnimator.setAnimation(a);
                     winAnimator.mAnimationIsEntrance = true;
+                    if (startKeyguardExit) {
+                        // Do one time only.
+                        mPolicy.startKeyguardExitAnimation(a.getStartOffset());
+                        startKeyguardExit = false;
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1a0dd82..7382f4ca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2215,6 +2215,11 @@
                           + attrs.token + ".  Aborting.");
                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
+                if (type == TYPE_VOICE_INTERACTION) {
+                    Slog.w(TAG, "Attempted to add voice interaction window with unknown token "
+                          + attrs.token + ".  Aborting.");
+                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+                }
                 if (type == TYPE_WALLPAPER) {
                     Slog.w(TAG, "Attempted to add wallpaper window with unknown token "
                           + attrs.token + ".  Aborting.");
@@ -2250,6 +2255,12 @@
                             + attrs.token + ".  Aborting.");
                       return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
+            } else if (type == TYPE_VOICE_INTERACTION) {
+                if (token.windowType != TYPE_VOICE_INTERACTION) {
+                    Slog.w(TAG, "Attempted to add voice interaction window with bad token "
+                            + attrs.token + ".  Aborting.");
+                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+                }
             } else if (type == TYPE_WALLPAPER) {
                 if (token.windowType != TYPE_WALLPAPER) {
                     Slog.w(TAG, "Attempted to add wallpaper window with bad token "
@@ -3173,7 +3184,7 @@
     }
 
     private boolean applyAnimationLocked(AppWindowToken atoken,
-            WindowManager.LayoutParams lp, int transit, boolean enter) {
+            WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) {
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
@@ -3189,6 +3200,7 @@
             WindowState win = atoken.findMainWindow();
             Rect containingFrame = new Rect(0, 0, width, height);
             Rect contentInsets = new Rect();
+            boolean isFullScreen = true;
             if (win != null) {
                 if (win.mContainingFrame != null) {
                     containingFrame.set(win.mContainingFrame);
@@ -3196,13 +3208,14 @@
                 if (win.mContentInsets != null) {
                     contentInsets.set(win.mContentInsets);
                 }
+                isFullScreen =
+                        ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ==
+                                SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
             }
 
-            boolean isFullScreen =
-                    ((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN)
-                            == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
             Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
-                    mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen);
+                    mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen,
+                    isVoiceInteraction);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -3422,7 +3435,7 @@
     @Override
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges) {
+            int configChanges, boolean voiceInteraction) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3448,7 +3461,7 @@
                 Slog.w(TAG, "Attempted to add existing app token: " + token);
                 return;
             }
-            atoken = new AppWindowToken(this, token);
+            atoken = new AppWindowToken(this, token, voiceInteraction);
             atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
             atoken.groupId = taskId;
             atoken.appFullscreen = fullscreen;
@@ -4200,7 +4213,7 @@
     }
 
     boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
-            boolean visible, int transit, boolean performLayout) {
+            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
         boolean delayed = false;
 
         if (wtoken.clientHidden == visible) {
@@ -4221,7 +4234,7 @@
                 if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
                     wtoken.mAppAnimator.animation = null;
                 }
-                if (applyAnimationLocked(wtoken, lp, transit, visible)) {
+                if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {
                     delayed = runningAppAnimation = true;
                 }
                 WindowState window = wtoken.findMainWindow();
@@ -4399,7 +4412,7 @@
 
             final long origId = Binder.clearCallingIdentity();
             setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,
-                    true);
+                    true, wtoken.voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             Binder.restoreCallingIdentity(origId);
         }
@@ -4546,7 +4559,7 @@
             if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken);
                 delayed = setTokenVisibilityLocked(wtoken, null, false,
-                        AppTransition.TRANSIT_UNSET, true);
+                        AppTransition.TRANSIT_UNSET, true, wtoken.voiceInteraction);
                 wtoken.inPendingTransaction = false;
                 mOpeningApps.remove(wtoken);
                 wtoken.waitingToShow = false;
@@ -5126,6 +5139,18 @@
     }
 
     @Override
+    public void keyguardGoingAway() {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
+        }
+        synchronized (mWindowMap) {
+            mAnimator.mKeyguardGoingAway = true;
+            requestTraversalLocked();
+        }
+    }
+
+    @Override
     public void closeSystemDialogs(String reason) {
         synchronized(mWindowMap) {
             final int numDisplays = mDisplayContents.size();
@@ -7148,9 +7173,7 @@
         public static final int TAP_OUTSIDE_STACK = 31;
         public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
-        public static final int REMOVE_STARTING_TIMEOUT = 33;
-
-        public static final int SHOW_DISPLAY_MASK = 34;
+        public static final int SHOW_DISPLAY_MASK = 33;
 
         @Override
         public void handleMessage(Message msg) {
@@ -8527,6 +8550,7 @@
             LayoutParams animLp = null;
             int bestAnimLayer = -1;
             boolean fullscreenAnim = false;
+            boolean voiceInteraction = false;
 
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                     "New wallpaper target=" + mWallpaperTarget
@@ -8571,6 +8595,8 @@
                     }
                 }
 
+                voiceInteraction |= wtoken.voiceInteraction;
+
                 if (wtoken.appFullscreen) {
                     WindowState ws = wtoken.findMainWindow();
                     if (ws != null) {
@@ -8643,7 +8669,7 @@
                 appAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
                 appAnimator.animation = null;
-                setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
+                setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction);
                 wtoken.updateReportedVisibilityLocked();
                 wtoken.waitingToShow = false;
 
@@ -8675,7 +8701,7 @@
                 wtoken.mAppAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
                 wtoken.mAppAnimator.animation = null;
-                setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
+                setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction);
                 wtoken.updateReportedVisibilityLocked();
                 wtoken.waitingToHide = false;
                 // Force the allDrawn flag, because we want to start
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c88382c..4a80e3e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -714,6 +714,11 @@
         return mAppToken != null ? mAppToken.appToken : null;
     }
 
+    @Override
+    public boolean isVoiceInteraction() {
+        return mAppToken != null ? mAppToken.voiceInteraction : false;
+    }
+
     boolean setInsetsChanged() {
         mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
         mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1e79dcb..e257ebc 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1658,7 +1658,7 @@
                         break;
                 }
                 if (attr >= 0) {
-                    a = mService.mAppTransition.loadAnimation(mWin.mAttrs, attr);
+                    a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
                 }
             }
             if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 51583a5..3cfb45b 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -23,6 +23,7 @@
     $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
new file mode 100644
index 0000000..f0c4f3a
--- /dev/null
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TvInputHal"
+
+//#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
+#include "JNIHelp.h"
+#include "jni.h"
+
+#include <gui/Surface.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/NativeHandle.h>
+#include <hardware/tv_input.h>
+
+namespace android {
+
+static struct {
+    jmethodID deviceAvailable;
+    jmethodID deviceUnavailable;
+    jmethodID streamConfigsChanged;
+} gTvInputHalClassInfo;
+
+static struct {
+    jclass clazz;
+} gTvStreamConfigClassInfo;
+
+static struct {
+    jclass clazz;
+
+    jmethodID constructor;
+    jmethodID streamId;
+    jmethodID type;
+    jmethodID maxWidth;
+    jmethodID maxHeight;
+    jmethodID generation;
+    jmethodID build;
+} gTvStreamConfigBuilderClassInfo;
+
+////////////////////////////////////////////////////////////////////////////////
+
+class JTvInputHal {
+public:
+    ~JTvInputHal();
+
+    static JTvInputHal* createInstance(JNIEnv* env, jobject thiz);
+
+    int setSurface(int deviceId, int streamId, const sp<Surface>& surface);
+    void getStreamConfigs(int deviceId, jobjectArray* array);
+    const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
+
+private:
+    class Connection {
+    public:
+        Connection() : mStreamId(0) {}
+
+        sp<Surface> mSurface;
+        sp<NativeHandle> mSourceHandle;
+        int mStreamId;
+    };
+
+    JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
+
+    static void notify(
+            tv_input_device_t* dev,tv_input_event_t* event, void* data);
+
+    void onDeviceAvailable(const tv_input_device_info_t& info);
+    void onDeviceUnavailable(int deviceId);
+    void onStreamConfigurationsChanged(int deviceId);
+
+    jweak mThiz;
+    tv_input_device_t* mDevice;
+    tv_input_callback_ops_t mCallback;
+
+    KeyedVector<int, Connection> mConnections;
+};
+
+JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) {
+    mThiz = env->NewWeakGlobalRef(thiz);
+    mDevice = device;
+    mCallback.notify = &JTvInputHal::notify;
+
+    mDevice->initialize(mDevice, &mCallback, this);
+}
+
+JTvInputHal::~JTvInputHal() {
+    mDevice->common.close((hw_device_t*)mDevice);
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->DeleteWeakGlobalRef(mThiz);
+    mThiz = NULL;
+}
+
+JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) {
+    tv_input_module_t* module = NULL;
+    status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
+            (hw_module_t const**)&module);
+    if (err) {
+        ALOGE("Couldn't load %s module (%s)",
+                TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
+        return 0;
+    }
+
+    tv_input_device_t* device = NULL;
+    err = module->common.methods->open(
+            (hw_module_t*)module,
+            TV_INPUT_DEFAULT_DEVICE,
+            (hw_device_t**)&device);
+    if (err) {
+        ALOGE("Couldn't open %s device (%s)",
+                TV_INPUT_DEFAULT_DEVICE, strerror(-err));
+        return 0;
+    }
+
+    return new JTvInputHal(env, thiz, device);
+}
+
+int JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) {
+    Connection& connection = mConnections.editValueFor(deviceId);
+    if (connection.mStreamId == streamId && connection.mSurface == surface) {
+        // Nothing to do
+        return NO_ERROR;
+    }
+    if (Surface::isValid(connection.mSurface)) {
+        connection.mSurface.clear();
+    }
+    if (surface == NULL) {
+        if (connection.mSurface != NULL) {
+            connection.mSurface->setSidebandStream(NULL);
+            connection.mSurface.clear();
+        }
+        if (connection.mSourceHandle != NULL) {
+            // Need to reset streams
+            if (mDevice->close_stream(
+                    mDevice, deviceId, connection.mStreamId) != 0) {
+                ALOGE("Couldn't remove stream");
+                return BAD_VALUE;
+            }
+            connection.mSourceHandle.clear();
+        }
+        return NO_ERROR;
+    }
+    connection.mSurface = surface;
+    if (connection.mSourceHandle == NULL) {
+        // Need to configure stream
+        int numConfigs = 0;
+        const tv_stream_config_t* configs = NULL;
+        if (mDevice->get_stream_configurations(
+                mDevice, deviceId, &numConfigs, &configs) != 0) {
+            ALOGE("Couldn't get stream configs");
+            return UNKNOWN_ERROR;
+        }
+        int configIndex = -1;
+        for (int i = 0; i < numConfigs; ++i) {
+            if (configs[i].stream_id == streamId) {
+                configIndex = i;
+                break;
+            }
+        }
+        if (configIndex == -1) {
+            ALOGE("Cannot find a config with given stream ID: %d", streamId);
+            return BAD_VALUE;
+        }
+        // TODO: handle buffer producer profile.
+        if (configs[configIndex].type !=
+                TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+            ALOGE("Profiles other than independent video source is not yet "
+                  "supported : type = %d", configs[configIndex].type);
+            return INVALID_OPERATION;
+        }
+        tv_stream_t stream;
+        stream.stream_id = configs[configIndex].stream_id;
+        if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
+            ALOGE("Couldn't add stream");
+            return UNKNOWN_ERROR;
+        }
+        connection.mSourceHandle = NativeHandle::create(
+                stream.sideband_stream_source_handle, false);
+        connection.mStreamId = stream.stream_id;
+        connection.mSurface->setSidebandStream(connection.mSourceHandle);
+    }
+    return NO_ERROR;
+}
+
+const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
+    const tv_stream_config_t* configs = NULL;
+    if (mDevice->get_stream_configurations(
+            mDevice, deviceId, numConfigs, &configs) != 0) {
+        ALOGE("Couldn't get stream configs");
+        return NULL;
+    }
+    return configs;
+}
+
+
+// static
+void JTvInputHal::notify(
+        tv_input_device_t* dev, tv_input_event_t* event, void* data) {
+    JTvInputHal* thiz = (JTvInputHal*)data;
+    switch (event->type) {
+        case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
+            thiz->onDeviceAvailable(event->device_info);
+        } break;
+        case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
+            thiz->onDeviceUnavailable(event->device_info.device_id);
+        } break;
+        case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
+            thiz->onStreamConfigurationsChanged(event->device_info.device_id);
+        } break;
+        default:
+            ALOGE("Unrecognizable event");
+    }
+}
+
+void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    mConnections.add(info.device_id, Connection());
+    env->CallVoidMethod(
+            mThiz,
+            gTvInputHalClassInfo.deviceAvailable,
+            info.device_id,
+            info.type);
+}
+
+void JTvInputHal::onDeviceUnavailable(int deviceId) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    mConnections.removeItem(deviceId);
+    env->CallVoidMethod(
+            mThiz,
+            gTvInputHalClassInfo.deviceUnavailable,
+            deviceId);
+}
+
+void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    mConnections.removeItem(deviceId);
+    env->CallVoidMethod(
+            mThiz,
+            gTvInputHalClassInfo.streamConfigsChanged,
+            deviceId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static jlong nativeOpen(JNIEnv* env, jobject thiz) {
+    return (jlong)JTvInputHal::createInstance(env, thiz);
+}
+
+static int nativeSetSurface(JNIEnv* env, jclass clazz,
+        jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
+    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+    sp<Surface> surface(
+            jsurface
+            ? android_view_Surface_getSurface(env, jsurface)
+            : NULL);
+    return tvInputHal->setSurface(deviceId, streamId, surface);
+}
+
+static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
+        jlong ptr, jint deviceId, jint generation) {
+    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+    int numConfigs = 0;
+    const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
+
+    jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
+    for (int i = 0; i < numConfigs; ++i) {
+        jobject builder = env->NewObject(
+                gTvStreamConfigBuilderClassInfo.clazz,
+                gTvStreamConfigBuilderClassInfo.constructor);
+        env->CallObjectMethod(
+                builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
+        env->CallObjectMethod(
+                builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
+        env->CallObjectMethod(
+                builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
+        env->CallObjectMethod(
+                builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
+        env->CallObjectMethod(
+                builder, gTvStreamConfigBuilderClassInfo.generation, generation);
+
+        jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
+
+        env->SetObjectArrayElement(result, i, config);
+
+        env->DeleteLocalRef(config);
+        env->DeleteLocalRef(builder);
+    }
+    return result;
+}
+
+static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
+    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+    delete tvInputHal;
+}
+
+static JNINativeMethod gTvInputHalMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeOpen", "()J",
+            (void*) nativeOpen },
+    { "nativeSetSurface", "(JIILandroid/view/Surface;)I",
+            (void*) nativeSetSurface },
+    { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;",
+            (void*) nativeGetStreamConfigs },
+    { "nativeClose", "(J)V",
+            (void*) nativeClose },
+};
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className)
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method" methodName)
+
+int register_android_server_tv_TvInputHal(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
+            gTvInputHalMethods, NELEM(gTvInputHalMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    jclass clazz;
+    FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
+
+    GET_METHOD_ID(
+            gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V");
+    GET_METHOD_ID(
+            gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
+    GET_METHOD_ID(
+            gTvInputHalClassInfo.streamConfigsChanged, clazz,
+            "streamConfigsChangedFromNative", "(I)V");
+
+    FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig");
+    gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
+
+    FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder");
+    gTvStreamConfigBuilderClassInfo.clazz =
+            jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
+
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.constructor,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "<init>", "()V");
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.streamId,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;");
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.type,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "type", "(I)Landroid/tv/TvStreamConfig$Builder;");
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.maxWidth,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;");
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.maxHeight,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;");
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.generation,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "generation", "(I)Landroid/tv/TvStreamConfig$Builder;");
+    GET_METHOD_ID(
+            gTvStreamConfigBuilderClassInfo.build,
+            gTvStreamConfigBuilderClassInfo.clazz,
+            "build", "()Landroid/tv/TvStreamConfig;");
+
+    return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 1feb325..bfa8286 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,7 @@
 int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
 int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
 int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
+int register_android_server_tv_TvInputHal(JNIEnv* env);
 };
 
 using namespace android;
@@ -78,6 +79,7 @@
     // TODO: remove this once replaces HdmiCecService with HdmiControlService.
     register_android_server_hdmi_HdmiCecService(env);
     register_android_server_hdmi_HdmiMhlController(env);
+    register_android_server_tv_TvInputHal(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de46b16..0f24ff6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
 import com.android.server.search.SearchManagerService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.task.TaskManagerService;
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.twilight.TwilightService;
@@ -132,6 +133,8 @@
             "com.android.server.hdmi.HdmiCecService";
     private static final String ETHERNET_SERVICE_CLASS =
             "com.android.server.ethernet.EthernetService";
+    private static final String TASK_SERVICE_CLASS =
+            "com.android.server.task.TaskManagerService";
 
     private final int mFactoryTestMode;
     private Timer mProfilerSnapshotTimer;
@@ -183,7 +186,7 @@
         // had to fallback to a different runtime because it is
         // running as root and we need to be the system user to set
         // the property. http://b/11463182
-        SystemProperties.set("persist.sys.dalvik.vm.lib.1", VMRuntime.getRuntime().vmLibrary());
+        SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
 
         // Enable the sampling profiler.
         if (SamplingProfilerIntegration.isEnabled()) {
@@ -831,6 +834,8 @@
 
             mSystemServiceManager.startService(UiModeManagerService.class);
 
+            mSystemServiceManager.startService(TaskManagerService.class);
+
             if (!disableNonCoreServices) {
                 try {
                     if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9b6daad..62ff121 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -113,7 +113,7 @@
             if (mBound) {
                 try {
                     mIWindowManager.addWindowToken(mToken,
-                            WindowManager.LayoutParams.TYPE_INPUT_METHOD);
+                            WindowManager.LayoutParams.TYPE_VOICE_INTERACTION);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed adding window token", e);
                 }
diff --git a/telecomm/java/android/telecomm/package.html b/telecomm/java/android/telecomm/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/telecomm/java/android/telecomm/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+    {@hide}
+</body>
+</html>
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/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
index fa0f995..6876f5a 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/samples/simplecamera/Camera2Source.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
 import android.os.Handler;
 import android.renderscript.Allocation;
 import android.renderscript.Element;
@@ -86,7 +87,7 @@
 
         @Override
         public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
-                CaptureResult result) {
+                TotalCaptureResult result) {
             // TODO Auto-generated method stub
             Log.v(TAG, "in onCaptureComplete");
 
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/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index db802c5..6b70631 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -902,5 +902,14 @@
                 <category android:name="com.android.test.hwui.TEST" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name=".ZOrderingActivity"
+                android:label="Reordering/Z Ordering">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/res/layout/z_ordering.xml b/tests/HwAccelerationTest/res/layout/z_ordering.xml
new file mode 100644
index 0000000..970c5fd
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/z_ordering.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:id="@+id/parent">
+    <RelativeLayout
+            android:layout_width="400dp"
+            android:layout_height="200dp"
+            android:layout_weight="1"
+            android:orientation="vertical">
+        <TextView style="@style/TopLeftReorderTextView"/>
+        <TextView style="@style/BottomLeftReorderTextView"/>
+        <TextView style="@style/TopRightReorderTextView"/>
+        <TextView style="@style/BottomRightReorderTextView"/>
+    </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index cde5d20..108709b 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -1,34 +1,37 @@
 <resources>
     <style name="ReorderTextView" parent="@android:style/TextAppearance.Medium">
-        <item name="android:layout_width">match_parent</item>
+        <item name="android:background">@drawable/appwidget_background</item>
+        <item name="android:layout_width">300dp</item>
         <item name="android:layout_height">100dp</item>
         <item name="android:gravity">center</item>
     </style>
     <style name="LeftReorderTextView" parent="@style/ReorderTextView">
-        <item name="android:translationX">20dp</item>
+        <item name="android:translationX">50dp</item>
+        <item name="android:layout_alignParentLeft">true</item>
     </style>
     <style name="RightReorderTextView" parent="@style/ReorderTextView">
-        <item name="android:translationX">-20dp</item>
+        <item name="android:translationX">-50dp</item>
+        <item name="android:layout_alignParentRight">true</item>
     </style>
 
     <style name="TopLeftReorderTextView" parent="@style/LeftReorderTextView">
-        <item name="android:background">#666</item>
-        <item name="android:text">100</item>
-        <item name="android:translationZ">100dp</item>
-    </style>
-    <style name="BottomLeftReorderTextView" parent="@style/LeftReorderTextView">
-        <item name="android:background">#bbb</item>
-        <item name="android:text">300</item>
-        <item name="android:translationZ">300dp</item>
-    </style>
-    <style name="TopRightReorderTextView" parent="@style/RightReorderTextView">
-        <item name="android:background">#888</item>
         <item name="android:text">200</item>
         <item name="android:translationZ">200dp</item>
+        <item name="android:layout_alignParentTop">true</item>
+    </style>
+    <style name="BottomLeftReorderTextView" parent="@style/LeftReorderTextView">
+        <item name="android:text">300</item>
+        <item name="android:translationZ">300dp</item>
+        <item name="android:layout_alignParentBottom">true</item>
+    </style>
+    <style name="TopRightReorderTextView" parent="@style/RightReorderTextView">
+        <item name="android:text">100</item>
+        <item name="android:translationZ">100dp</item>
+        <item name="android:layout_alignParentTop">true</item>
     </style>
     <style name="BottomRightReorderTextView" parent="@style/RightReorderTextView">
-        <item name="android:background">#ccc</item>
         <item name="android:text">400</item>
         <item name="android:translationZ">400dp</item>
+        <item name="android:layout_alignParentBottom">true</item>
     </style>
 </resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
new file mode 100644
index 0000000..45e77ed
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
@@ -0,0 +1,28 @@
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ZOrderingActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.z_ordering);
+
+        ViewGroup grandParent = (ViewGroup) findViewById(R.id.parent);
+        if (grandParent == null) throw new IllegalStateException();
+        View.OnClickListener l = new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {}
+        };
+        for (int i = 0; i < grandParent.getChildCount(); i++) {
+            ViewGroup parent = (ViewGroup) grandParent.getChildAt(i);
+            for (int j = 0; j < parent.getChildCount(); j++) {
+                parent.getChildAt(j).setOnClickListener(l);
+            }
+        }
+    }
+}
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index ac0f701..2d08163 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -2,7 +2,8 @@
         package="com.android.test.voiceinteraction">
 
     <application>
-        <activity android:name="VoiceInteractionMain" android:label="Voice Interaction">
+        <activity android:name="VoiceInteractionMain" android:label="Voice Interaction"
+                android:theme="@android:style/Theme.Quantum">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
index 9fcbf3e..563fa44 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -19,6 +19,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:background="#ffffffff"
+    android:fitsSystemWindows="true"
     >
 
     <TextView android:id="@+id/text"
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 6f5788a..a6c09f3 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
         }
         
         try {
-            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0);
+            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index cc621c4..105803e 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -826,6 +826,11 @@
         return null;
     }
 
+    @Override
+    public int[] extractThemeAttrs() {
+        return null;
+    }
+
     /**
      * Retrieve the raw TypedValue for the attribute at <var>index</var>.
      *
@@ -912,4 +917,9 @@
     public String toString() {
         return Arrays.toString(mResourceData);
     }
- }
+
+    static TypedArray obtain(Resources res, int len) {
+        return res instanceof BridgeResources ?
+                new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 31d1594..f4a9f52 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -97,6 +97,13 @@
         return found;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
+            int[] values, int[] attrs) {
+        // FIXME
+        return null;
+    }
+
     // ---- private helper methods ----
 
     private static boolean setupResources(Theme thisTheme) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 0a7899a..faa8852 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -27,4 +27,9 @@
         // pass
         return false;
     }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtain(Resources res, int len) {
+        return BridgeTypedArray.obtain(res, len);
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 802cf1c..33813d1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -18,9 +18,11 @@
 
 import java.awt.Font;
 import java.awt.Graphics2D;
+import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
 import java.awt.font.GlyphVector;
 import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -36,12 +38,12 @@
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
 
-    /* package */ static class ScriptRun {
+    /*package*/ static class ScriptRun {
         int start;
         int limit;
         boolean isRtl;
         int scriptCode;
-        FontInfo font;
+        Font font;
 
         public ScriptRun(int start, int limit, boolean isRtl) {
             this.start = start;
@@ -51,9 +53,10 @@
         }
     }
 
-    private Graphics2D mGraphics;
-    private Paint_Delegate mPaint;
+    private final Graphics2D mGraphics;
+    private final Paint_Delegate mPaint;
     private char[] mText;
+    private List<Font> mFonts;
     // Bounds of the text drawn so far.
     private RectF mBounds;
     private float mBaseline;
@@ -63,11 +66,15 @@
      * @param paint The Paint to use to get the fonts. Should not be null.
      * @param text Unidirectional text. Should not be null.
      */
-    /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+    /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
         assert (paint != null);
         mGraphics = graphics;
         mPaint = paint;
         mText = text;
+        mFonts = new ArrayList<Font>(paint.getFonts().size());
+        for (FontInfo fontInfo : paint.getFonts()) {
+            mFonts.add(fontInfo.mFont);
+        }
     }
 
     /**
@@ -94,7 +101,7 @@
         // the script runs.
         mBounds = new RectF(x, y, x, y);
         mBaseline = y;
-        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
+        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
             int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
             flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
             renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
@@ -108,16 +115,15 @@
      * much as possible. This also implements a fallback mechanism to render characters that cannot
      * be drawn using the preferred font.
      */
-    private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
+    private void renderScript(int start, int limit, Font preferredFont, int flag,
             float[] advances, int advancesIndex, boolean draw) {
-        List<FontInfo> fonts = mPaint.getFonts();
-        if (fonts == null || preferredFont == null) {
+        if (mFonts.size() == 0 || preferredFont == null) {
             return;
         }
 
         while (start < limit) {
             boolean foundFont = false;
-            int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit);
+            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
             if (canDisplayUpTo == -1) {
                 // We can draw all characters in the text.
                 render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
@@ -133,8 +139,8 @@
             // The current character cannot be drawn with the preferred font. Cycle through all the
             // fonts to check which one can draw it.
             int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-            for (FontInfo font : fonts) {
-                canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
+            for (Font font : mFonts) {
+                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
                 if (canDisplayUpTo == -1) {
                     render(start, start+charCount, font, flag, advances, advancesIndex, draw);
                     start += charCount;
@@ -160,15 +166,19 @@
      * Renders the text to the right of the bounds with the given font.
      * @param font The font to render the text with.
      */
-    private void render(int start, int limit, FontInfo font, int flag, float[] advances,
+    private void render(int start, int limit, Font font, int flag, float[] advances,
             int advancesIndex, boolean draw) {
 
-        // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
-        // the anti-aliasing set.
-        FontRenderContext f = font.mMetrics.getFontRenderContext();
-        FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(),
-                f.usesFractionalMetrics());
-        GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
+        FontRenderContext frc;
+        if (mGraphics != null) {
+            frc = mGraphics.getFontRenderContext();
+        } else {
+            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+            // Metrics obtained this way don't have anti-aliasing set. So,
+            // we create a new FontRenderContext with anti-aliasing set.
+            frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+        }
+        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
         int[] ci = gv.getGlyphCharIndices(0, ng, null);
         if (advances != null) {
@@ -206,7 +216,7 @@
     }
 
     /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
-            boolean isRtl, List<FontInfo> fonts) {
+            boolean isRtl, List<Font> fonts) {
         LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
 
         int count = limit - start;
@@ -225,10 +235,10 @@
 
     // TODO: Replace this method with one which returns the font based on the scriptCode.
     private static void setScriptFont(char[] text, ScriptRun run,
-            List<FontInfo> fonts) {
-        for (FontInfo fontInfo : fonts) {
-            if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
-                run.font = fontInfo;
+            List<Font> fonts) {
+        for (Font font : fonts) {
+            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
+                run.font = font;
                 return;
             }
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index cdbbe46..610c867 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -79,13 +79,6 @@
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate(long native_shader, long native_bitmap,
-            int shaderTileModeX, int shaderTileModeY) {
-        // pass, not needed.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     private BitmapShader_Delegate(java.awt.image.BufferedImage image,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 56c0de9..e9daffd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -979,7 +979,6 @@
             final float startX, final float startY, final int flags, long paint,
             long typeface) {
 
-        // TODO: use typeface.
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
             @Override
@@ -1097,7 +1096,7 @@
     /**
      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
      * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
      */
     private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
             GcSnapshot.Drawable drawable) {
@@ -1117,7 +1116,7 @@
      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
      * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
      */
     private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
         // get the delegate from the native int.
@@ -1190,12 +1189,6 @@
         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
     }
 
-    private void setBitmap(Bitmap_Delegate bitmap) {
-        mBitmap = bitmap;
-        assert mSnapshot.size() == 1;
-        mSnapshot.setBitmap(mBitmap);
-    }
-
     private static void drawBitmap(
             long nativeCanvas,
             Bitmap_Delegate bitmap,
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index fae8aef..59ddcc6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -78,19 +78,6 @@
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(long native_shader, long native_skiaShaderA,
-            long native_skiaShaderB, long native_mode) {
-        // pass, not needed.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(long native_shader, long native_skiaShaderA,
-            long native_skiaShaderB, int porterDuffMode) {
-        // pass, not needed.
-        return 0;
-    }
 
     // ---- Private delegate/helper methods ----
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
new file mode 100644
index 0000000..9ea4538
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -0,0 +1,227 @@
+/*
+ * 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.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Font;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
+
+/**
+ * Delegate implementing the native methods of android.graphics.FontFamily
+ *
+ * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original FontFamily class.
+ *
+ * @see DelegateManager
+ */
+public class FontFamily_Delegate {
+
+    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
+    // separately.
+    private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
+    private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
+    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
+    private static final String FONT_SUBSTRING_COMPACT = "UI";
+
+    /**
+     * A class associating {@link Font} with its metadata.
+     */
+    private static final class FontInfo {
+        Font mFont;
+        /** Regular, Bold, Italic, or BoldItalic. */
+        int mStyle;
+        /**
+         * The variant of the Font - compact or elegant.
+         * @see Paint#setElegantTextHeight(boolean)
+         */
+        boolean mIsCompact;
+    }
+
+    // ---- delegate manager ----
+    private static final DelegateManager<FontFamily_Delegate> sManager =
+            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
+
+    // ---- delegate helper data ----
+    private static String sFontLocation;
+    private static final List<FontFamily_Delegate> sPostInitDelegate = new
+            ArrayList<FontFamily_Delegate>();
+
+
+    // ---- delegate data ----
+    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+    // Path of fonts that haven't been created since sFontLoader hasn't been initialized.
+    private List<String> mPath = new ArrayList<String>();
+
+
+    // ---- Public Helper methods ----
+
+    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
+        return sManager.getDelegate(nativeFontFamily);
+    }
+
+    public static synchronized void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
+            fontFamily.init();
+        }
+        sPostInitDelegate.clear();
+    }
+
+    public Font getFont(int style, boolean isCompact) {
+        FontInfo plainFont = null;
+        FontInfo styledFont = null;  // Font matching the style but not isCompact
+        for (FontInfo font : mFonts) {
+            if (font.mStyle == style) {
+                if (font.mIsCompact == isCompact) {
+                    return font.mFont;
+                }
+                styledFont = font;
+            }
+            if (font.mStyle == Font.PLAIN) {
+                if (plainFont == null) {
+                    plainFont = font;
+                    continue;
+                }
+                if (font.mIsCompact == isCompact) {
+                    // Override the previous selection of plain font since we've found a better one.
+                    plainFont = font;
+                }
+            }
+        }
+        if (styledFont != null) {
+            return styledFont.mFont;
+        }
+
+        // No font with the mentioned style is found. Try to derive one.
+        if (plainFont != null && style > 0 && style < 4) {
+            styledFont = new FontInfo();
+            styledFont.mFont = plainFont.mFont.deriveFont(style);
+            styledFont.mStyle = style;
+            styledFont.mIsCompact = plainFont.mIsCompact;
+            // Add the font to the list of fonts so that we don't have to derive it the next time.
+            mFonts.add(styledFont);
+            return styledFont.mFont;
+        }
+        return null;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreateFamily() {
+        FontFamily_Delegate delegate = new FontFamily_Delegate();
+        if (sFontLocation != null) {
+            delegate.init();
+        } else {
+            sPostInitDelegate.add(delegate);
+        }
+        return sManager.addNewDelegate(delegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nUnrefFamily(long nativePtr) {
+        // Removing the java reference for the object doesn't mean that it's freed for garbage
+        // collection. Typeface_Delegate may still hold a reference for it.
+        sManager.removeJavaReferenceFor(nativePtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nAddFont(long nativeFamily, String path) {
+        FontFamily_Delegate delegate = getDelegate(nativeFamily);
+        if (delegate != null) {
+            if (sFontLocation == null) {
+                delegate.mPath.add(path);
+                return true;
+            }
+            return delegate.addFont(path);
+        }
+        return false;
+    }
+
+    private void init() {
+        for (String path : mPath) {
+            addFont(path);
+        }
+        mPath = null;
+    }
+
+    private boolean addFont(String path) {
+        Font font = loadFont(path);
+        if (font == null) {
+            return false;
+        }
+        FontInfo fontInfo = new FontInfo();
+        fontInfo.mFont = font;
+        addFontMetadata(fontInfo, path);
+        // TODO ensure that mFonts doesn't have the font with this style already.
+        mFonts.add(fontInfo);
+        return true;
+    }
+
+    private static void addFontMetadata(FontInfo fontInfo, String path) {
+        int style = Font.PLAIN;
+        String fontName = path.substring(path.lastIndexOf('/'), path.length());
+        if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
+            style = Font.BOLD | Font.ITALIC;
+        } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
+            style = Font.BOLD;
+        } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
+            style = Font.ITALIC;
+        }
+        fontInfo.mStyle = style;
+
+        // Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
+        // This should go away when this info is passed on by nAddFont().
+        int hyphenIndex = fontName.lastIndexOf('-');
+        fontInfo.mIsCompact = hyphenIndex > 0 &&
+                fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
+
+    }
+
+    private static Font loadFont(String path) {
+        if (path.startsWith(SYSTEM_FONTS) ) {
+            String relativePath = path.substring(SYSTEM_FONTS.length());
+            File f = new File(sFontLocation, relativePath);
+
+            try {
+                return Font.createFont(Font.TRUETYPE_FONT, f);
+            } catch (Exception e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                        String.format("Unable to load font %1$s", relativePath),
+                        null /*throwable*/, null /*data*/);
+            }
+        } else {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
+                    null /*throwable*/, null /*data*/);
+        }
+
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index ac77377..55c4b98 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -71,22 +71,6 @@
                 tileMode);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(LinearGradient thisGradient,
-            long native_shader, float x0, float y0, float x1, float y1,
-            int colors[], float positions[], int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(LinearGradient thisGradient,
-            long native_shader, float x0, float y0, float x1, float y1,
-            int color0, int color1, int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 8862f5b..f42f48f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -203,6 +203,16 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static boolean native_isAffine(long native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return true;
+        }
+
+        return (d.computeTypeMask() & kPerspective_Mask) == 0;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static boolean native_rectStaysRect(long native_object) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 83df745..911f4e7 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -53,7 +53,7 @@
 public class Paint_Delegate {
 
     /**
-     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+     * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
      */
     /*package*/ static final class FontInfo {
         Font mFont;
@@ -66,8 +66,6 @@
 
     // ---- delegate helper data ----
     private List<FontInfo> mFonts;
-    private final FontRenderContext mFontContext = new FontRenderContext(
-            new AffineTransform(), true, true);
 
     // ---- delegate data ----
     private int mFlags;
@@ -83,6 +81,7 @@
     private float mTextScaleX;
     private float mTextSkewX;
     private int mHintingMode = Paint.HINTING_ON;
+    private boolean mIsCompact = true;
 
     private Xfermode_Delegate mXfermode;
     private ColorFilter_Delegate mColorFilter;
@@ -101,8 +100,7 @@
     }
 
     /**
-     * Returns the list of {@link Font} objects. The first item is the main font, the rest
-     * are fall backs for characters not present in the main font.
+     * Returns the list of {@link Font} objects.
      */
     public List<FontInfo> getFonts() {
         return mFonts;
@@ -437,12 +435,20 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
-        return false;
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        return delegate != null && !delegate.mIsCompact;
     }
 
     @LayoutlibDelegate
     /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
-        // TODO
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mIsCompact = !elegant;
     }
 
     @LayoutlibDelegate
@@ -621,7 +627,6 @@
         int inc = count > 0 ? 1 : -1;
 
         int measureIndex = 0;
-        float measureAcc = 0;
         for (int i = index; i != index + count; i += inc, measureIndex++) {
             int start, end;
             if (i < index) {
@@ -640,7 +645,6 @@
                 measuredWidth[measureIndex] = res;
             }
 
-            measureAcc += res;
             if (res > maxWidth) {
                 // we should not return this char index, but since it's 0-based
                 // and we need to return a count, we simply return measureIndex;
@@ -818,7 +822,7 @@
             return filter;
         }
 
-        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
+        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
 
         // since none of those are supported, display a fidelity warning right away
         if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
@@ -940,52 +944,17 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_getTextWidths(long native_object, char[] text, int index,
-            int count, int bidiFlags, float[] widths) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        if (delegate.mFonts.size() > 0) {
-            // FIXME: handle multi-char characters (see measureText)
-            float totalAdvance = 0;
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : delegate.mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        float adv = info.mMetrics.charWidth(c);
-                        totalAdvance += adv;
-                        if (widths != null) {
-                            widths[i] = adv;
-                        }
-
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // no advance for this char.
-                    if (widths != null) {
-                        widths[i] = 0.f;
-                    }
-                }
-            }
-
-            return (int) totalAdvance;
-        }
-
-        return 0;
+    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+            char[] text, int index, int count, int bidiFlags, float[] widths) {
+        return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
+                index, count, bidiFlags, widths, 0);
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_getTextWidths(long native_object, String text, int start,
-            int end, int bidiFlags, float[] widths) {
-        return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
-                bidiFlags, widths);
+    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+            String text, int start, int end, int bidiFlags, float[] widths) {
+        return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
+                end - start, bidiFlags, widths);
     }
 
     @LayoutlibDelegate
@@ -997,15 +966,20 @@
 
     @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(long native_object,
+            long native_typeface /*ignored*/,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex) {
 
+        // native_typeface is passed here since Framework's old implementation did not have the
+        // typeface object associated with the Paint. Since, we follow the new framework way,
+        // we store the typeface with the paint and use it directly.
+
         if (advances != null)
             for (int i = advancesIndex; i< advancesIndex+count; i++)
                 advances[i]=0;
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+        if (delegate == null) {
             return 0.f;
         }
         boolean isRtl = isRtl(flags);
@@ -1017,7 +991,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static float native_getTextRunAdvances(long native_object,
+    /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex) {
         // FIXME: support contextStart and contextEnd
@@ -1025,8 +999,8 @@
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
-                contextEnd - contextStart, flags, advances, advancesIndex);
+        return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
+                contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
     }
 
     @LayoutlibDelegate
@@ -1076,7 +1050,7 @@
 
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+        if (delegate == null) {
             return;
         }
         delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
@@ -1150,7 +1124,7 @@
     private void updateFontObject() {
         if (mTypeface != null) {
             // Get the fonts from the TypeFace object.
-            List<Font> fonts = mTypeface.getFonts();
+            List<Font> fonts = mTypeface.getFonts(mIsCompact);
 
             // create new font objects as well as FontMetrics, based on the current text size
             // and skew info.
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index 4f16dcf..80179ee 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -68,20 +68,6 @@
                 tileMode);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(long native_shader, float x, float y, float radius,
-            int colors[], float positions[], int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(long native_shader, float x, float y, float radius,
-            int color0, int color1, int tileMode) {
-        // nothing to be done here.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
index 70a0a43..14e9960 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -76,13 +76,12 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static void nativeDestructor(long native_shader, long native_skiaShader) {
+    /*package*/ static void nativeDestructor(long native_shader) {
         sManager.removeJavaReferenceFor(native_shader);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nativeSetLocalMatrix(long native_shader, long native_skiaShader,
-            long matrix_instance) {
+    /*package*/ static void nativeSetLocalMatrix(long native_shader, long matrix_instance) {
         // get the delegate from the native int.
         Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
         if (shaderDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index f2b3e8d..95a57a9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -62,20 +62,6 @@
         return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate1(long native_shader, float cx, float cy,
-            int[] colors, float[] positions) {
-        // nothing to be done here.
-        return 0;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativePostCreate2(long native_shader, float cx, float cy,
-            int color0, int color1) {
-        // nothing to be done here.
-        return 0;
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 60cd157..9746b48 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -19,7 +19,6 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.res.AssetManager;
@@ -44,100 +43,66 @@
  */
 public final class Typeface_Delegate {
 
-    private static final String SYSTEM_FONTS = "/system/fonts/";
+    public static final String SYSTEM_FONTS = "/system/fonts/";
 
     // ---- delegate manager ----
     private static final DelegateManager<Typeface_Delegate> sManager =
             new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
 
     // ---- delegate helper data ----
-    private static final String DEFAULT_FAMILY = "sans-serif";
-
-    private static FontLoader sFontLoader;
-    private static final List<Typeface_Delegate> sPostInitDelegate =
-            new ArrayList<Typeface_Delegate>();
+    private static String sFontLocation;
 
     // ---- delegate data ----
 
-    private final String mFamily;
+    private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
     private int mStyle;
-    private List<Font> mFonts;
 
+    private static long sDefaultTypeface;
 
     // ---- Public Helper methods ----
-
-    public static synchronized void init(FontLoader fontLoader) {
-        sFontLoader = fontLoader;
-
-        for (Typeface_Delegate delegate : sPostInitDelegate) {
-            delegate.init();
-        }
-        sPostInitDelegate.clear();
+    public static synchronized void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+        FontFamily_Delegate.setFontLocation(fontLocation);
     }
 
     public static Typeface_Delegate getDelegate(long nativeTypeface) {
         return sManager.getDelegate(nativeTypeface);
     }
 
-    public static List<Font> getFonts(Typeface typeface) {
-        return getFonts(typeface.native_instance);
-    }
-
-    public static List<Font> getFonts(long native_int) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_int);
-        if (delegate == null) {
-            return null;
+    public List<Font> getFonts(boolean compact) {
+        List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
+        for (FontFamily_Delegate ffd : mFontFamilies) {
+            if (ffd != null) {
+                Font font = ffd.getFont(mStyle, compact);
+                if (font != null) {
+                    fonts.add(font);
+                }
+            }
         }
-
-        return delegate.getFonts();
-    }
-
-    public List<Font> getFonts() {
-        return mFonts;
+        return fonts;
     }
 
     // ---- native methods ----
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreate(String familyName, int style) {
-        if (familyName == null) {
-            familyName = DEFAULT_FAMILY;
-        }
-        if (style < 0) {
-            style = Typeface.NORMAL;
-        }
-
-        Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
-        if (sFontLoader != null) {
-            newDelegate.init();
-        } else {
-            // font loader has not been initialized yet, add the delegate to a list of delegates
-            // to init when the font loader is initialized.
-            // There won't be any rendering before this happens anyway.
-            sPostInitDelegate.add(newDelegate);
-        }
-
-        return sManager.addNewDelegate(newDelegate);
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Could not find font with family \"" + familyName + "\".",
+                null /*throwable*/, null /*data*/);
+        return 0;
     }
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
         if (delegate == null) {
+            delegate = sManager.getDelegate(sDefaultTypeface);
+        }
+        if (delegate == null) {
             return 0;
         }
 
-        Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
-        if (sFontLoader != null) {
-            newDelegate.init();
-        } else {
-            // font loader has not been initialized yet, add the delegate to a list of delegates
-            // to init when the font loader is initialized.
-            // There won't be any rendering before this happens anyway.
-            sPostInitDelegate.add(newDelegate);
-        }
-
-        return sManager.addNewDelegate(newDelegate);
+        return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style));
     }
 
     @LayoutlibDelegate
@@ -149,31 +114,19 @@
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromFile(String path) {
-        if (path.startsWith(SYSTEM_FONTS) ) {
-            String relativePath = path.substring(SYSTEM_FONTS.length());
-            File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Typeface.createFromFile() is not supported.,", null, null);
+        return 0;
+    }
 
-            try {
-                Font font = Font.createFont(Font.TRUETYPE_FONT, f);
-                if (font != null) {
-                    Typeface_Delegate newDelegate = new Typeface_Delegate(font);
-                    return sManager.addNewDelegate(newDelegate);
-                }
-            } catch (Exception e) {
-                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                        String.format("Unable to load font %1$s", relativePath),
-                            null /*throwable*/, null /*data*/);
-            }
-        } else {
-            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                    "Typeface.createFromFile() can only work with platform fonts located in " +
-                        SYSTEM_FONTS,
-                    null /*throwable*/, null /*data*/);
+    @LayoutlibDelegate
+    /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
+        FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
+        for (int i = 0; i < familyArray.length; i++) {
+            fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
         }
-
-
-        // return a copy of the base font
-        return nativeCreate(null, 0);
+        Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL);
+        return sManager.addNewDelegate(delegate);
     }
 
     @LayoutlibDelegate
@@ -191,24 +144,20 @@
         return delegate.mStyle;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetDefault(long native_instance) {
+        sDefaultTypeface = native_instance;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static File getSystemFontConfigLocation() {
+        return new File(sFontLocation);
+    }
+
     // ---- Private delegate/helper methods ----
 
-    private Typeface_Delegate(String family, int style) {
-        mFamily = family;
+    private Typeface_Delegate(FontFamily_Delegate[] fontFamilies, int style) {
+        mFontFamilies = fontFamilies;
         mStyle = style;
     }
-
-    private Typeface_Delegate(Font font) {
-        mFamily = font.getFamily();
-        mStyle = Typeface.NORMAL;
-
-        mFonts = sFontLoader.getFallbackFonts(mStyle);
-
-        // insert the font glyph first.
-        mFonts.add(0, font);
-    }
-
-    private void init() {
-        mFonts = sFontLoader.getFont(mFamily, mStyle);
-    }
 }
diff --git a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
new file mode 100644
index 0000000..a193330
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
@@ -0,0 +1,49 @@
+/*
+ * 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.util;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Delegate overriding some methods of android.util.Xml
+ *
+ * Through the layoutlib_create tool, the original methods of Xml have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ */
+public class Xml_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static XmlPullParser newPullParser() {
+        try {
+            KXmlParser parser = new KXmlParser();
+            // The prebuilt kxml2 library with the IDE doesn't support DOCECL.
+//            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            return parser;
+        } catch (XmlPullParserException e) {
+            throw new AssertionError();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 757cdd2..3bf2b20 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -72,7 +72,7 @@
 
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
-            boolean arg5, boolean arg6, int arg7, int arg8)
+            boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9)
             throws RemoteException {
         // TODO Auto-generated method stub
 
@@ -439,6 +439,10 @@
     }
 
     @Override
+    public void keyguardGoingAway() throws RemoteException {
+    }
+
+    @Override
     public void lockNow(Bundle options) {
         // TODO Auto-generated method stub
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index fa8050f..ffab4de 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,6 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.Result.Status;
 import com.android.ide.common.rendering.api.SessionParams;
-import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.layoutlib.bridge.impl.RenderDrawable;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -61,7 +60,7 @@
 /**
  * Main entry point of the LayoutLib Bridge.
  * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #createScene(SceneParams)}
+ * {@link #createSession(SessionParams)}
  */
 public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
 
@@ -147,8 +146,7 @@
             if (getClass() != obj.getClass()) return false;
 
             IntArray other = (IntArray) obj;
-            if (!Arrays.equals(mArray, other.mArray)) return false;
-            return true;
+            return Arrays.equals(mArray, other.mArray);
         }
     }
 
@@ -251,14 +249,7 @@
         }
 
         // load the fonts.
-        FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
-        if (fontLoader != null) {
-            Typeface_Delegate.init(fontLoader);
-        } else {
-            log.error(LayoutLog.TAG_BROKEN,
-                    "Failed create FontLoader in layout lib.", null);
-            return false;
-        }
+        Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
 
         // now parse com.android.internal.R (and only this one as android.R is a subset of
         // the internal version), and put the content in the maps.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index 936ab4f..e59ccd7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -171,7 +171,7 @@
             // Set action bar to be split, if needed.
             ActionBarContainer splitView = (ActionBarContainer) findViewById(R.id.split_action_bar);
             mActionBarView.setSplitView(splitView);
-            mActionBarView.setSplitActionBar(mSplit);
+            mActionBarView.setSplitToolbar(mSplit);
 
             inflateMenus();
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
deleted file mode 100644
index cc7338a..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2008 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.layoutlib.bridge.impl;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import android.graphics.Typeface;
-
-import java.awt.Font;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Provides {@link Font} object to the layout lib.
- * <p/>
- * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
- * fonts.xml file located alongside the ttf files.
- */
-public final class FontLoader {
-    private static final String FONTS_SYSTEM = "system_fonts.xml";
-    private static final String FONTS_VENDOR = "vendor_fonts.xml";
-    private static final String FONTS_FALLBACK = "fallback_fonts.xml";
-
-    private static final String NODE_FAMILYSET = "familyset";
-    private static final String NODE_FAMILY = "family";
-    private static final String NODE_NAME = "name";
-    private static final String NODE_FILE = "file";
-
-    private static final String ATTRIBUTE_VARIANT = "variant";
-    private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
-    private static final String FONT_SUFFIX_NONE = ".ttf";
-    private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
-    private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
-    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
-    // separately.
-    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
-    private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
-
-    // This must match the values of Typeface styles so that we can use them for indices in this
-    // array.
-    private static final int[] AWT_STYLES = new int[] {
-        Font.PLAIN,
-        Font.BOLD,
-        Font.ITALIC,
-        Font.BOLD | Font.ITALIC
-    };
-    private static int[] DERIVE_BOLD_ITALIC = new int[] {
-        Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
-    };
-    private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
-    private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
-
-    private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
-    private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
-
-    private final String mOsFontsLocation;
-
-    public static FontLoader create(String fontOsLocation) {
-        try {
-            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
-                parserFactory.setNamespaceAware(true);
-
-            // parse the system fonts
-            FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
-            List<FontInfo> systemFonts = handler.getFontList();
-
-
-            // parse the fallback fonts
-            handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
-            List<FontInfo> fallbackFonts = handler.getFontList();
-
-            return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
-        } catch (ParserConfigurationException e) {
-            // return null below
-        } catch (SAXException e) {
-            // return null below
-        } catch (FileNotFoundException e) {
-            // return null below
-        } catch (IOException e) {
-            // return null below
-        }
-
-        return null;
-    }
-
-    private static FontHandler parseFontFile(SAXParserFactory parserFactory,
-            String fontOsLocation, String fontFileName)
-            throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
-
-        SAXParser parser = parserFactory.newSAXParser();
-        File f = new File(fontOsLocation, fontFileName);
-
-        FontHandler definitionParser = new FontHandler(
-                fontOsLocation + File.separator);
-        parser.parse(new FileInputStream(f), definitionParser);
-        return definitionParser;
-    }
-
-    private FontLoader(String fontOsLocation,
-            List<FontInfo> fontList, List<FontInfo> fallBackList) {
-        mOsFontsLocation = fontOsLocation;
-        mMainFonts.addAll(fontList);
-        mFallbackFonts.addAll(fallBackList);
-    }
-
-
-    public String getOsFontsLocation() {
-        return mOsFontsLocation;
-    }
-
-    /**
-     * Returns a {@link Font} object given a family name and a style value (constant in
-     * {@link Typeface}).
-     * @param family the family name
-     * @param style a 1-item array containing the requested style. Based on the font being read
-     *              the actual style may be different. The array contains the actual style after
-     *              the method returns.
-     * @return the font object or null if no match could be found.
-     */
-    public synchronized List<Font> getFont(String family, int style) {
-        List<Font> result = new ArrayList<Font>();
-
-        if (family == null) {
-            return result;
-        }
-
-
-        // get the font objects from the main list based on family.
-        for (FontInfo info : mMainFonts) {
-            if (info.families.contains(family)) {
-                result.add(info.font[style]);
-                break;
-            }
-        }
-
-        // add all the fallback fonts for the given style
-        for (FontInfo info : mFallbackFonts) {
-            result.add(info.font[style]);
-        }
-
-        return result;
-    }
-
-
-    public synchronized List<Font> getFallbackFonts(int style) {
-        List<Font> result = new ArrayList<Font>();
-        // add all the fallback fonts
-        for (FontInfo info : mFallbackFonts) {
-            result.add(info.font[style]);
-        }
-        return result;
-    }
-
-
-    private final static class FontInfo {
-        final Font[] font = new Font[4]; // Matches the 4 type-face styles.
-        final Set<String> families;
-
-        FontInfo() {
-            families = new HashSet<String>();
-        }
-    }
-
-    private final static class FontHandler extends DefaultHandler {
-        private final String mOsFontsLocation;
-
-        private FontInfo mFontInfo = null;
-        private final StringBuilder mBuilder = new StringBuilder();
-        private List<FontInfo> mFontList = new ArrayList<FontInfo>();
-        private boolean isCompactFont = true;
-
-        private FontHandler(String osFontsLocation) {
-            super();
-            mOsFontsLocation = osFontsLocation;
-        }
-
-        public List<FontInfo> getFontList() {
-            return mFontList;
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
-         */
-        @Override
-        public void startElement(String uri, String localName, String name, Attributes attributes)
-                throws SAXException {
-            if (NODE_FAMILYSET.equals(localName)) {
-                mFontList = new ArrayList<FontInfo>();
-            } else if (NODE_FAMILY.equals(localName)) {
-                if (mFontList != null) {
-                    mFontInfo = null;
-                }
-            } else if (NODE_NAME.equals(localName)) {
-                if (mFontList != null && mFontInfo == null) {
-                    mFontInfo = new FontInfo();
-                }
-            } else if (NODE_FILE.equals(localName)) {
-                if (mFontList != null && mFontInfo == null) {
-                    mFontInfo = new FontInfo();
-                }
-                if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
-                    isCompactFont = false;
-                } else {
-                    isCompactFont = true;
-                }
-            }
-
-            mBuilder.setLength(0);
-
-            super.startElement(uri, localName, name, attributes);
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
-         */
-        @Override
-        public void characters(char[] ch, int start, int length) throws SAXException {
-            if (isCompactFont) {
-              mBuilder.append(ch, start, length);
-            }
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
-         */
-        @Override
-        public void endElement(String uri, String localName, String name) throws SAXException {
-            if (NODE_FAMILY.equals(localName)) {
-                if (mFontInfo != null) {
-                    // if has a normal font file, add to the list
-                    if (mFontInfo.font[Typeface.NORMAL] != null) {
-                        mFontList.add(mFontInfo);
-
-                        // create missing font styles, order is important.
-                        if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
-                            computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
-                        }
-                        if (mFontInfo.font[Typeface.ITALIC] == null) {
-                            computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
-                        }
-                        if (mFontInfo.font[Typeface.BOLD] == null) {
-                            computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
-                        }
-                    }
-
-                    mFontInfo = null;
-                }
-            } else if (NODE_NAME.equals(localName)) {
-                // handle a new name for an existing Font Info
-                if (mFontInfo != null) {
-                    String family = trimXmlWhitespaces(mBuilder.toString());
-                    mFontInfo.families.add(family);
-                }
-            } else if (NODE_FILE.equals(localName)) {
-                // handle a new file for an existing Font Info
-                if (isCompactFont && mFontInfo != null) {
-                    String fileName = trimXmlWhitespaces(mBuilder.toString());
-                    Font font = getFont(fileName);
-                    if (font != null) {
-                        if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
-                            mFontInfo.font[Typeface.NORMAL] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
-                            mFontInfo.font[Typeface.BOLD] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
-                            mFontInfo.font[Typeface.BOLD_ITALIC] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
-                            mFontInfo.font[Typeface.ITALIC] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
-                            mFontInfo.font[Typeface.NORMAL] = font;
-                        }
-                    }
-                }
-            }
-        }
-
-        private Font getFont(String fileName) {
-            try {
-                File file = new File(mOsFontsLocation, fileName);
-                if (file.exists()) {
-                    return Font.createFont(Font.TRUETYPE_FONT, file);
-                }
-            } catch (Exception e) {
-
-            }
-
-            return null;
-        }
-
-        private void computeDerivedFont( int toCompute, int[] basedOnList) {
-            for (int basedOn : basedOnList) {
-                if (mFontInfo.font[basedOn] != null) {
-                    mFontInfo.font[toCompute] =
-                        mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
-                    return;
-                }
-            }
-
-            // we really shouldn't stop there. This means we don't have a NORMAL font...
-            assert false;
-        }
-
-        private String trimXmlWhitespaces(String value) {
-            if (value == null) {
-                return null;
-            }
-
-            // look for carriage return and replace all whitespace around it by just 1 space.
-            int index;
-
-            while ((index = value.indexOf('\n')) != -1) {
-                // look for whitespace on each side
-                int left = index - 1;
-                while (left >= 0) {
-                    if (Character.isWhitespace(value.charAt(left))) {
-                        left--;
-                    } else {
-                        break;
-                    }
-                }
-
-                int right = index + 1;
-                int count = value.length();
-                while (right < count) {
-                    if (Character.isWhitespace(value.charAt(right))) {
-                        right++;
-                    } else {
-                        break;
-                    }
-                }
-
-                // remove all between left and right (non inclusive) and replace by a single space.
-                String leftString = null;
-                if (left >= 0) {
-                    leftString = value.substring(0, left + 1);
-                }
-                String rightString = null;
-                if (right < count) {
-                    rightString = value.substring(right);
-                }
-
-                if (leftString != null) {
-                    value = leftString;
-                    if (rightString != null) {
-                        value += " " + rightString;
-                    }
-                } else {
-                    value = rightString != null ? rightString : "";
-                }
-            }
-
-            // now we un-escape the string
-            int length = value.length();
-            char[] buffer = value.toCharArray();
-
-            for (int i = 0 ; i < length ; i++) {
-                if (buffer[i] == '\\') {
-                    if (buffer[i+1] == 'n') {
-                        // replace the char with \n
-                        buffer[i+1] = '\n';
-                    }
-
-                    // offset the rest of the buffer since we go from 2 to 1 char
-                    System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
-                    length--;
-                }
-            }
-
-            return new String(buffer, 0, length);
-        }
-
-    }
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index bb72a1e..1f7a28e 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -125,15 +125,19 @@
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
+        "android.content.res.Resources$Theme#resolveAttributes",
         "android.content.res.Resources#localeToLanguageTag",
         "android.content.res.AssetManager#newTheme",
         "android.content.res.AssetManager#deleteTheme",
         "android.content.res.AssetManager#applyThemeStyle",
         "android.content.res.TypedArray#getValueAt",
+        "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
+        "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",
         "android.os.HandlerThread#run",
         "android.text.format.DateFormat#is24HourFormat",
+        "android.util.Xml#newPullParser",
         "android.view.Choreographer#getRefreshRate",
         "android.view.Display#updateDisplayInfoLocked",
         "android.view.Display#getWindowManager",
@@ -170,6 +174,7 @@
         "android.graphics.DiscretePathEffect",
         "android.graphics.DrawFilter",
         "android.graphics.EmbossMaskFilter",
+        "android.graphics.FontFamily",
         "android.graphics.LayerRasterizer",
         "android.graphics.LightingColorFilter",
         "android.graphics.LinearGradient",
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py
index ea3dccc..aea3241 100755
--- a/tools/layoutlib/rename_font/build_font.py
+++ b/tools/layoutlib/rename_font/build_font.py
@@ -15,10 +15,10 @@
 # limitations under the License.
 
 """
-Rename the PS name of all fonts in the input directory and copy them to the
+Rename the PS name of all fonts in the input directories and copy them to the
 output directory.
 
-Usage: build_font.py /path/to/input_fonts/ /path/to/output_fonts/
+Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/
 
 """
 
@@ -30,50 +30,86 @@
 from lxml import etree
 import shutil
 import glob
+from multiprocessing import Pool
+
+# global variable
+dest_dir = '/tmp'
 
 def main(argv):
-  if len(argv) != 2:
-    print "Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/"
-    sys.exit(1)
-  if not os.path.isdir(argv[0]):
-    print argv[0] + "is not a valid directory"
-    sys.exit(1)
-  if not os.path.isdir(argv[1]):
-    print argv[1] + "is not a valid directory"
-    sys.exit(1)
+  if len(argv) < 2:
+    sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/')
+  for directory in argv:
+    if not os.path.isdir(directory):
+      sys.exit(directory + ' is not a valid directory')
+  global dest_dir
+  dest_dir = argv[-1]
+  src_dirs = argv[:-1]
   cwd = os.getcwd()
-  os.chdir(argv[1])
+  os.chdir(dest_dir)
   files = glob.glob('*')
   for filename in files:
     os.remove(filename)
   os.chdir(cwd)
-  for filename in os.listdir(argv[0]):
-    if not os.path.splitext(filename)[1].lower() == ".ttf":
-      shutil.copy(os.path.join(argv[0], filename), argv[1])
-      continue
-    print os.path.join(argv[0], filename)
-    old_ttf_path = os.path.join(argv[0], filename)
+  input_fonts = list()
+  for src_dir in src_dirs:
+    for dirname, dirnames, filenames in os.walk(src_dir):
+      for filename in filenames:
+          input_path = os.path.join(dirname, filename)
+          extension = os.path.splitext(filename)[1].lower()
+          if (extension == '.ttf'):
+            input_fonts.append(input_path)
+          elif (extension == '.xml'):
+            shutil.copy(input_path, dest_dir)
+      if '.git' in dirnames:
+          # don't go into any .git directories.
+          dirnames.remove('.git')
+  # Create as many threads as the number of CPUs
+  pool = Pool(processes=None)
+  pool.map(convert_font, input_fonts)
+
+
+class InvalidFontException(Exception):
+  pass
+
+def convert_font(input_path):
+  filename = os.path.basename(input_path)
+  print 'Converting font: ' + filename
+  # the path to the output file. The file name is the fontfilename.ttx
+  ttx_path = os.path.join(dest_dir, filename)
+  ttx_path = ttx_path[:-1] + 'x'
+  try:
     # run ttx to generate an xml file in the output folder which represents all
     # its info
-    ttx_args = ["-d", argv[1], old_ttf_path]
+    ttx_args = ['-q', '-d', dest_dir, input_path]
     ttx.main(ttx_args)
-    # the path to the output file. The file name is the fontfilename.ttx
-    ttx_path = os.path.join(argv[1], filename)
-    ttx_path = ttx_path[:-1] + "x"
     # now parse the xml file to change its PS name.
     tree = etree.parse(ttx_path)
     encoding = tree.docinfo.encoding
     root = tree.getroot()
     for name in root.iter('name'):
       [old_ps_name, version] = get_font_info(name)
-      new_ps_name = old_ps_name + version
-      update_name(name, new_ps_name)
+      if old_ps_name is not None and version is not None:
+        new_ps_name = old_ps_name + version
+        update_name(name, new_ps_name)
     tree.write(ttx_path, xml_declaration=True, encoding=encoding )
     # generate the udpated font now.
-    ttx_args = ["-d", argv[1], ttx_path]
+    ttx_args = ['-q', '-d', dest_dir, ttx_path]
     ttx.main(ttx_args)
-    # delete the temp ttx file.
+  except InvalidFontException:
+    # In case of invalid fonts, we exit.
+    print filename + ' is not a valid font'
+    raise
+  except Exception as e:
+    print 'Error converting font: ' + filename
+    print e
+    # Some fonts are too big to be handled by the ttx library.
+    # Just copy paste them.
+    shutil.copy(input_path, dest_dir)
+  try:
+    # delete the temp ttx file is it exists.
     os.remove(ttx_path)
+  except OSError:
+    pass
 
 def get_font_info(tag):
   ps_name = None
@@ -85,19 +121,17 @@
       if namerecord.attrib['nameID'] == '6':
         if ps_name is not None:
           if not sanitize(namerecord.text) == ps_name:
-            sys.exit('found multiple possibilities of the font name')
+            raise InvalidFontException('found multiple possibilities of the font name')
         else:
           ps_name = sanitize(namerecord.text)
       # nameID=5 means the font version
       if namerecord.attrib['nameID'] == '5':
         if ps_version is not None:
           if not ps_version == get_version(namerecord.text):
-            sys.exit('found multiple possibilities of the font version')
+            raise InvalidFontException('found multiple possibilities of the font version')
         else:
           ps_version = get_version(namerecord.text)
-  if ps_name is not None and ps_version is not None:
-    return [ps_name, ps_version]
-  sys.exit('didn\'t find the font name or version')
+  return [ps_name, ps_version]
 
 
 def update_name(tag, name):
@@ -110,11 +144,11 @@
   return re.sub(r'[^\w-]+', '', string)
 
 def get_version(string):
-  # The string must begin with "Version n.nn "
+  # The string must begin with 'Version n.nn '
   # to extract n.nn, we return the second entry in the split strings.
   string = string.strip()
-  if not string.startswith("Version "):
-    sys.exit('mal-formed font version')
+  if not string.startswith('Version '):
+    raise InvalidFontException('mal-formed font version')
   return sanitize(string.split()[1])
 
 if __name__ == '__main__':
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index bafc71e..1157de7d 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;