Merge "Remove unnecessary layout request from Keyguard" into lmp-preview-dev
diff --git a/api/current.txt b/api/current.txt
index c38a82f..a829bdf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5106,6 +5106,7 @@
     method public void clearForwardingIntentFilters(android.content.ComponentName);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+    method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
@@ -5139,6 +5140,7 @@
     method public boolean isProfileOwnerApp(java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
+    method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public boolean setApplicationBlocked(android.content.ComponentName, java.lang.String, boolean);
@@ -12134,83 +12136,119 @@
     field public static final int CAMERA_ERROR = 3; // 0x3
   }
 
+  public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
+    ctor public CameraCaptureSession();
+    method public abstract void abortCaptures() throws android.hardware.camera2.CameraAccessException;
+    method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void close();
+    method public abstract android.hardware.camera2.CameraDevice getDevice();
+    method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+  }
+
+  public static abstract class CameraCaptureSession.CaptureListener {
+    ctor public CameraCaptureSession.CaptureListener();
+    method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+    method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
+    method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, int);
+    method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
+  }
+
+  public static abstract class CameraCaptureSession.StateListener {
+    ctor public CameraCaptureSession.StateListener();
+    method public void onActive(android.hardware.camera2.CameraCaptureSession);
+    method public void onClosed(android.hardware.camera2.CameraCaptureSession);
+    method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
+    method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
+    method public void onReady(android.hardware.camera2.CameraCaptureSession);
+  }
+
   public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata {
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
-    method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureRequestKeys();
-    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_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;
+    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 LENS_INFO_SHADING_MAP_SIZE;
+    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_STREAMS;
+    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 {
-    method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
-    method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated void flush() throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
-    method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract deprecated void stopRepeating() throws android.hardware.camera2.CameraAccessException;
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
     field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -12219,7 +12257,7 @@
     field public static final int TEMPLATE_ZERO_SHUTTER_LAG = 5; // 0x5
   }
 
-  public static abstract class CameraDevice.CaptureListener {
+  public static abstract deprecated class CameraDevice.CaptureListener {
     ctor public CameraDevice.CaptureListener();
     method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
     method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
@@ -12229,14 +12267,14 @@
 
   public static abstract class CameraDevice.StateListener {
     ctor public CameraDevice.StateListener();
-    method public void onActive(android.hardware.camera2.CameraDevice);
-    method public void onBusy(android.hardware.camera2.CameraDevice);
+    method public deprecated void onActive(android.hardware.camera2.CameraDevice);
+    method public deprecated void onBusy(android.hardware.camera2.CameraDevice);
     method public void onClosed(android.hardware.camera2.CameraDevice);
     method public abstract void onDisconnected(android.hardware.camera2.CameraDevice);
     method public abstract void onError(android.hardware.camera2.CameraDevice, int);
-    method public void onIdle(android.hardware.camera2.CameraDevice);
+    method public deprecated void onIdle(android.hardware.camera2.CameraDevice);
     method public abstract void onOpened(android.hardware.camera2.CameraDevice);
-    method public void onUnconfigured(android.hardware.camera2.CameraDevice);
+    method public deprecated void onUnconfigured(android.hardware.camera2.CameraDevice);
     field public static final int ERROR_CAMERA_DEVICE = 4; // 0x4
     field public static final int ERROR_CAMERA_DISABLED = 3; // 0x3
     field public static final int ERROR_CAMERA_IN_USE = 1; // 0x1
@@ -12259,8 +12297,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
@@ -12380,6 +12417,8 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // 0x5
     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
+    field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG = 2; // 0x2
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG = 1; // 0x1
@@ -12428,12 +12467,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();
@@ -12446,146 +12479,170 @@
 
   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_COORDINATES;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_PROCESSING_METHOD;
+    field public static final android.hardware.camera2.CaptureRequest.Key JPEG_GPS_TIMESTAMP;
+    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_BLUE;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_GREEN;
+    field public static final android.hardware.camera2.CaptureRequest.Key TONEMAP_CURVE_RED;
+    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 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 final class CaptureResult extends android.hardware.camera2.CameraMetadata {
-    method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
+    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_COORDINATES;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_PROCESSING_METHOD;
+    field public static final android.hardware.camera2.CaptureResult.Key JPEG_GPS_TIMESTAMP;
+    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_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_BLUE;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_GREEN;
+    field public static final android.hardware.camera2.CaptureResult.Key TONEMAP_CURVE_RED;
+    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;
   }
 
 }
@@ -14056,19 +14113,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);
@@ -25026,7 +25070,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";
@@ -25064,10 +25110,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";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 18e2a95..77b1acf 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.service.trust.TrustAgentService;
 import android.util.Log;
@@ -1983,6 +1984,43 @@
     }
 
     /**
+     * Called by a device owner to create a user with the specified name. The UserHandle returned
+     * by this method should not be persisted as user handles are recycled as users are removed and
+     * created. If you need to persist an identifier for this user, use
+     * {@link UserManager#getSerialNumberForUser}.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param name the user's name
+     * @see UserHandle
+     * @return the UserHandle object for the created user, or null if the user could not be created.
+     */
+    public UserHandle createUser(ComponentName admin, String name) {
+        try {
+            return mService.createUser(admin, name);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not create a user", re);
+        }
+        return null;
+    }
+
+    /**
+     * Called by a device owner to remove a user and all associated data. The primary user can
+     * not be removed.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param userHandle the user to remove.
+     * @return {@code true} if the user was removed, {@code false} otherwise.
+     */
+    public boolean removeUser(ComponentName admin, UserHandle userHandle) {
+        try {
+            return mService.removeUser(admin, userHandle);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not remove user ", re);
+            return false;
+        }
+    }
+
+    /**
      * Called by a profile or device owner to get the application restrictions for a given target
      * application running in the managed profile.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7257158..3c08c14 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.RemoteCallback;
+import android.os.UserHandle;
 
 /**
  * Internal IPC interface to the device policy service.
@@ -128,6 +129,9 @@
     int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked);
     boolean isApplicationBlocked(in ComponentName admin, in String packageName);
 
+    UserHandle createUser(in ComponentName who, in String name);
+    boolean removeUser(in ComponentName who, in UserHandle userHandle);
+
     void enableSystemApp(in ComponentName admin, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d0ac9c9..6ae006c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1994,6 +1994,7 @@
             WIFI_PASSPOINT_SERVICE,
             WIFI_P2P_SERVICE,
             WIFI_SCANNING_SERVICE,
+            //@hide: ETHERNET_SERVICE,
             NSD_SERVICE,
             AUDIO_SERVICE,
             MEDIA_ROUTER_SERVICE,
@@ -2069,9 +2070,6 @@
      *  <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
      *  <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
      * Wi-Fi Direct connectivity.
-     *  <dt> {@link #ETHERNET_SERVICE} ("ethernet")
-     *  <dd> A {@link android.net.ethernet.EthernetManager EthernetManager} for
-     * management of Ethernet connectivity.
      * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
      * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
      * for management of input methods.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 076f657..f1391aa 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).
@@ -7347,7 +7357,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 +7368,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 +7383,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
new file mode 100644
index 0000000..8391209
--- /dev/null
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+import android.os.Handler;
+import java.util.List;
+
+/**
+ * A configured capture session for a {@link CameraDevice}, used for capturing
+ * images from the camera.
+ *
+ * <p>A CameraCaptureSession is created by providing a set of target output surfaces to
+ * {@link CameraDevice#createCaptureSession createCaptureSession}. Once created, the session is
+ * active until a new session is created by the camera device, or the camera device is closed.</p>
+ *
+ * <p>Creating a session is an expensive operation and can take several hundred milliseconds, since
+ * it requires configuring the camera device's internal pipelines and allocating memory buffers for
+ * sending images to the desired targets. While
+ * {@link CameraDevice#createCaptureSession createCaptureSession} will provide a
+ * CameraCaptureSession object immediately, configuration won't be complete until the
+ * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback is called for the
+ * first time. If configuration cannot be completed, then the
+ * {@link CameraCaptureSession.StateListener#onConfigureFailed onConfigureFailed} is called, and the
+ * session will not become active.</p>
+ *
+ * <p>Any capture requests (repeating or non-repeating) submitted before the session is ready will
+ * be queued up and will begin capture once the session becomes ready. In case the session cannot be
+ * configured and {@link StateListener#onConfigureFailed onConfigureFailed} is called, all queued
+ * capture requests are discarded.</p>
+ *
+ * <p>If a new session is created by the camera device, then the previous session is closed, and its
+ * associated {@link StateListener#onClosed onClosed} callback will be invoked.  All
+ * of the session methods will throw an IllegalStateException if called once the session is
+ * closed.</p>
+ *
+ * <p>A closed session clears any repeating requests (as if {@link #stopRepeating} had been called),
+ * but will still complete all of its in-progress capture requests as normal, before a newly
+ * created session takes over and reconfigures the camera device.</p>
+ */
+public abstract class CameraCaptureSession implements AutoCloseable {
+
+    /**
+     * Get the camera device that this session is created for
+     */
+    public abstract CameraDevice getDevice();
+
+    /**
+     * <p>Submit a request for an image to be captured by the camera device.</p>
+     *
+     * <p>The request defines all the parameters for capturing the single image,
+     * including sensor, lens, flash, and post-processing settings.</p>
+     *
+     * <p>Each request will produce one {@link CaptureResult} and produce new frames for one or more
+     * target Surfaces, set with the CaptureRequest builder's
+     * {@link CaptureRequest.Builder#addTarget} method. The target surfaces (set with
+     * {@link CaptureRequest.Builder#addTarget}) must be a subset of the surfaces provided when this
+     * capture session was created.</p>
+     *
+     * <p>Multiple requests can be in progress at once. They are processed in
+     * first-in, first-out order, with minimal delays between each
+     * capture. Requests submitted through this method have higher priority than
+     * those submitted through {@link #setRepeatingRequest} or
+     * {@link #setRepeatingBurst}, and will be processed as soon as the current
+     * repeat/repeatBurst processing completes.</p>
+     *
+     * @param request the settings for this capture
+     * @param listener The callback object to notify once this request has been
+     * processed. If null, no metadata will be produced for this capture,
+     * although image data will still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException if the request targets Surfaces that are not configured as
+     *                                  outputs for this session. Or if the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper.
+     *
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     */
+    public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler)
+            throws CameraAccessException;
+
+    /**
+     * Submit a list of requests to be captured in sequence as a burst. The
+     * burst will be captured in the minimum amount of time possible, and will
+     * not be interleaved with requests submitted by other capture or repeat
+     * calls.
+     *
+     * <p>The requests will be captured in order, each capture producing one {@link CaptureResult}
+     * and image buffers for one or more target {@link android.view.Surface surfaces}. The target
+     * surfaces (set with {@link CaptureRequest.Builder#addTarget}) must be a subset of the surfaces
+     * provided when this capture session was created.</p>
+     *
+     * <p>The main difference between this method and simply calling
+     * {@link #capture} repeatedly is that this method guarantees that no
+     * other requests will be interspersed with the burst.</p>
+     *
+     * @param requests the list of settings for this burst capture
+     * @param listener The callback object to notify each time one of the
+     * requests in the burst has been processed. If null, no metadata will be
+     * produced for any requests in this burst, although image data will still
+     * be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests target Surfaces not currently configured as
+     *                                  outputs. Or if the handler is null, the listener is not
+     *                                  null, and the calling thread has no looper.
+     *
+     * @see #capture
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     */
+    public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
+
+    /**
+     * Request endlessly repeating capture of images by this capture session.
+     *
+     * <p>With this method, the camera device will continually capture images
+     * using the settings in the provided {@link CaptureRequest}, at the maximum
+     * rate possible.</p>
+     *
+     * <p>Repeating requests are a simple way for an application to maintain a
+     * preview or other continuous stream of frames, without having to
+     * continually submit identical requests through {@link #capture}.</p>
+     *
+     * <p>Repeat requests have lower priority than those submitted
+     * through {@link #capture} or {@link #captureBurst}, so if
+     * {@link #capture} is called when a repeating request is active, the
+     * capture request will be processed before any further repeating
+     * requests are processed.<p>
+     *
+     * <p>Repeating requests are a simple way for an application to maintain a
+     * preview or other continuous stream of frames, without having to submit
+     * requests through {@link #capture} at video rates.</p>
+     *
+     * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
+     * {@link #abortCaptures} will also clear the request.</p>
+     *
+     * <p>Calling this method will replace any earlier repeating request or
+     * burst set up by this method or {@link #setRepeatingBurst}, although any
+     * in-progress burst will be completed before the new repeat request will be
+     * used.</p>
+     *
+     * @param request the request to repeat indefinitely
+     * @param listener The callback object to notify every time the
+     * request finishes processing. If null, no metadata will be
+     * produced for this stream of requests, although image data will
+     * still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests reference Surfaces that are not currently
+     *                                  configured as outputs. Or if the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingBurst
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
+
+    /**
+     * <p>Request endlessly repeating capture of a sequence of images by this
+     * capture session.</p>
+     *
+     * <p>With this method, the camera device will continually capture images,
+     * cycling through the settings in the provided list of
+     * {@link CaptureRequest CaptureRequests}, at the maximum rate possible.</p>
+     *
+     * <p>If a request is submitted through {@link #capture} or
+     * {@link #captureBurst}, the current repetition of the request list will be
+     * completed before the higher-priority request is handled. This guarantees
+     * that the application always receives a complete repeat burst captured in
+     * minimal time, instead of bursts interleaved with higher-priority
+     * captures, or incomplete captures.</p>
+     *
+     * <p>Repeating burst requests are a simple way for an application to
+     * maintain a preview or other continuous stream of frames where each
+     * request is different in a predicatable way, without having to continually
+     * submit requests through {@link #captureBurst}.</p>
+     *
+     * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
+     * ongoing burst will still be completed, however. Calling
+     * {@link #abortCaptures} will also clear the request.</p>
+     *
+     * <p>Calling this method will replace a previously-set repeating request or
+     * burst set up by this method or {@link #setRepeatingRequest}, although any
+     * in-progress burst will be completed before the new repeat burst will be
+     * used.</p>
+     *
+     * @param requests the list of requests to cycle through indefinitely
+     * @param listener The callback object to notify each time one of the
+     * requests in the repeating bursts has finished processing. If null, no
+     * metadata will be produced for this stream of requests, although image
+     * data will still be produced.
+     * @param handler the handler on which the listener should be invoked, or
+     * {@code null} to use the current thread's {@link android.os.Looper
+     * looper}.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureListener#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests reference Surfaces not currently configured
+     *                                  as outputs. Or if the handler is null, the listener is not
+     *                                  null, and the calling thread has no looper.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException;
+
+    /**
+     * <p>Cancel any ongoing repeating capture set by either
+     * {@link #setRepeatingRequest setRepeatingRequest} or
+     * {@link #setRepeatingBurst}. Has no effect on requests submitted through
+     * {@link #capture capture} or {@link #captureBurst captureBurst}.</p>
+     *
+     * <p>Any currently in-flight captures will still complete, as will any burst that is
+     * mid-capture. To ensure that the device has finished processing all of its capture requests
+     * and is in ready state, wait for the {@link StateListener#onReady} callback after
+     * calling this method.</p>
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     *
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see StateListener#onIdle
+     */
+    public abstract void stopRepeating() throws CameraAccessException;
+
+    /**
+     * Discard all captures currently pending and in-progress as fast as possible.
+     *
+     * <p>The camera device will discard all of its current work as fast as possible. Some in-flight
+     * captures may complete successfully and call {@link CaptureListener#onCaptureCompleted}, while
+     * others will trigger their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating
+     * request or a repeating burst is set, it will be cleared.</p>
+     *
+     * <p>This method is the fastest way to switch the camera device to a new session with
+     * {@link CameraDevice#createCaptureSession}, at the cost of discarding in-progress work. It
+     * must be called before the new session is created. Once all pending requests are either
+     * completed or thrown away, the {@link StateListener#onReady} callback will be called,
+     * if the session has not been closed. Otherwise, the {@link StateListener#onClosed}
+     * callback will be fired when a new session is created by the camera device.</p>
+     *
+     * <p>Cancelling will introduce at least a brief pause in the stream of data from the camera
+     * device, since once the camera device is emptied, the first new request has to make it through
+     * the entire camera pipeline before new output buffers are produced.</p>
+     *
+     * <p>This means that using {@code abortCaptures()} to simply remove pending requests is not
+     * recommended; it's best used for quickly switching output configurations, or for cancelling
+     * long in-progress requests (such as a multi-second capture).</p>
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because a new
+     *                               session has been created or the camera device has been closed.
+     *
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see #configureOutputs
+     */
+    public abstract void abortCaptures() throws CameraAccessException;
+
+    /**
+     * Close this capture session asynchronously.
+     *
+     * <p>Closing a session frees up the target output Surfaces of the session for reuse with either a
+     * new session, or to other APIs that can draw to Surfaces.</p>
+     *
+     * <p>Note that creating a new capture session with {@link CameraDevice#createCaptureSession}
+     * will close any existing capture session automatically, and call the older session listener's
+     * {@link StateListener#onClosed} callback. Using {@link CameraDevice#createCaptureSession}
+     * directly without closing is the recommended approach for quickly switching to a new session,
+     * since unchanged target outputs can be reused more efficiently.</p>
+     *
+     * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and any
+     * repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
+     * However, any in-progress capture requests submitted to the session will be completed as
+     * normal; once all captures have completed and the session has been torn down,
+     * {@link StateListener#onClosed} will be called.</p>
+     */
+    @Override
+    public abstract void close();
+
+    /**
+     * A listener for tracking the state of a camera capture session.
+     *
+     */
+    public static abstract class StateListener {
+
+        /**
+         * This method is called when the camera device has finished configuring itself, and the
+         * session can start processing capture requests.
+         *
+         * <p>If there are capture requests already queued with the session, they will start
+         * processing once this callback is invoked, and the session will call {@link #onActive}
+         * right after this callback is invoked.</p>
+         *
+         * <p>If no capture requests have been submitted, then the session will invoke
+         * {@link #onReady} right after this callback.</p>
+         *
+         * <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
+         * be invoked instead of this callback.</p>
+         *
+         */
+        public abstract void onConfigured(CameraCaptureSession session);
+
+        /**
+         * This method is called if the session cannot be configured as requested.
+         *
+         * <p>This can happen if the set of requested outputs contains unsupported sizes,
+         * or too many outputs are requested at once.</p>
+         *
+         * <p>The session is considered to be closed, and all methods called on it after this
+         * callback is invoked will throw an IllegalStateException. Any capture requests submitted
+         * to the session prior to this callback will be discarded and will not produce any
+         * callbacks on their listeners.</p>
+         */
+        public abstract void onConfigureFailed(CameraCaptureSession session);
+
+        /**
+         * This method is called every time the session has no more capture requests to process.
+         *
+         * <p>During the creation of a new session, this callback is invoked right after
+         * {@link #onConfigured} if no capture requests were submitted to the session prior to it
+         * completing configuration.</p>
+         *
+         * <p>Otherwise, this callback will be invoked any time the session finishes processing
+         * all of its active capture requests, and no repeating request or burst is set up.</p>
+         *
+         */
+        public void onReady(CameraCaptureSession session) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when the session starts actively processing capture requests.
+         *
+         * <p>If capture requests are submitted prior to {@link #onConfigured} being called,
+         * then the session will start processing those requests immediately after the callback,
+         * and this method will be immediately called after {@link #onConfigured}.
+         *
+         * <p>If the session runs out of capture requests to process and calls {@link #onReady},
+         * then this callback will be invoked again once new requests are submitted for capture.</p>
+         */
+        public void onActive(CameraCaptureSession session) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when the session is closed.
+         *
+         * <p>A session is closed when a new session is created by the parent camera device,
+         * or when the parent camera device is closed (either by the user closing the device,
+         * or due to a camera device disconnection or fatal error).</p>
+         *
+         * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and
+         * any repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
+         * However, any in-progress capture requests submitted to the session will be completed
+         * as normal.</p>
+         */
+        public void onClosed(CameraCaptureSession session) {
+            // default empty implementation
+        }
+    }
+
+    /**
+     * <p>A listener for tracking the progress of a {@link CaptureRequest}
+     * submitted to the camera device.</p>
+     *
+     * <p>This listener is called when a request triggers a capture to start,
+     * and when the capture is complete. In case on an error capturing an image,
+     * the error method is triggered instead of the completion method.</p>
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     */
+    public static abstract class CaptureListener {
+
+        /**
+         * This constant is used to indicate that no images were captured for
+         * the request.
+         *
+         * @hide
+         */
+        public static final int NO_FRAMES_CAPTURED = -1;
+
+        /**
+         * This method is called when the camera device has started capturing
+         * the output image for the request, at the beginning of image exposure.
+         *
+         * <p>This callback is invoked right as the capture of a frame begins,
+         * so it is the most appropriate time for playing a shutter sound,
+         * or triggering UI indicators of capture.</p>
+         *
+         * <p>The request that is being used for this capture is provided, along
+         * with the actual timestamp for the start of exposure. This timestamp
+         * matches the timestamp that will be included in
+         * {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field},
+         * and in the buffers sent to each output Surface. These buffer
+         * timestamps are accessible through, for example,
+         * {@link android.media.Image#getTimestamp() Image.getTimestamp()} or
+         * {@link android.graphics.SurfaceTexture#getTimestamp()}.</p>
+         *
+         * <p>For the simplest way to play a shutter sound camera shutter or a
+         * video recording start/stop sound, see the
+         * {@link android.media.MediaActionSound} class.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera the CameraDevice sending the callback
+         * @param request the request for the capture that just begun
+         * @param timestamp the timestamp at start of capture, in nanoseconds.
+         *
+         * @see android.media.MediaActionSound
+         */
+        public void onCaptureStarted(CameraDevice camera,
+                CaptureRequest request, long timestamp) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when some results from an image capture are
+         * available.
+         *
+         * <p>The result provided here will contain some subset of the fields of
+         * a full result. Multiple onCapturePartial calls may happen per
+         * capture; a given result field will only be present in one partial
+         * capture at most. The final onCaptureCompleted call will always
+         * contain all the fields, whether onCapturePartial was called or
+         * not.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera The CameraDevice sending the callback.
+         * @param request The request that was given to the CameraDevice
+         * @param result The partial output metadata from the capture, which
+         * includes a subset of the CaptureResult fields.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         *
+         * @hide
+         */
+        public void onCapturePartial(CameraDevice camera,
+                CaptureRequest request, CaptureResult result) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called when an image capture has completed and the
+         * result metadata is available.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera The CameraDevice sending the callback.
+         * @param request The request that was given to the CameraDevice
+         * @param result The output metadata from the capture, including the
+         * final capture parameters and the state of the camera system during
+         * capture.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureCompleted(CameraDevice camera,
+                CaptureRequest request, CaptureResult result) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called instead of {@link #onCaptureCompleted} when the
+         * camera device failed to produce a {@link CaptureResult} for the
+         * request.
+         *
+         * <p>Other requests are unaffected, and some or all image buffers from
+         * the capture may have been pushed to their respective output
+         * streams.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param request
+         *            The request that was given to the CameraDevice
+         * @param failure
+         *            The output failure from the capture, including the failure reason
+         *            and the frame number.
+         *
+         * @see #capture
+         * @see #captureBurst
+         * @see #setRepeatingRequest
+         * @see #setRepeatingBurst
+         */
+        public void onCaptureFailed(CameraDevice camera,
+                CaptureRequest request, CaptureFailure failure) {
+            // default empty implementation
+        }
+
+        /**
+         * This method is called independently of the others in CaptureListener,
+         * when a capture sequence finishes and all {@link CaptureResult}
+         * or {@link CaptureFailure} for it have been returned via this listener.
+         *
+         * @param camera
+         *            The CameraDevice sending the callback.
+         * @param sequenceId
+         *            A sequence ID returned by the {@link #capture} family of functions.
+         * @param lastFrameNumber
+         *            The last frame number (returned by {@link CaptureResult#getFrameNumber}
+         *            or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
+         *            The last frame number may be equal to NO_FRAMES_CAPTURED if no images
+         *            were captured for this sequence. This can happen, for example, when a
+         *            repeating request or burst is cleared right after being set.
+         *
+         * @see CaptureResult#getFrameNumber()
+         * @see CaptureFailure#getFrameNumber()
+         * @see CaptureResult#getSequenceId()
+         * @see CaptureFailure#getSequenceId()
+         */
+        public void onCaptureSequenceCompleted(CameraDevice camera,
+                int sequenceId, int lastFrameNumber) {
+            // default empty implementation
+        }
+    }
+
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7cc6d1d..c08424a 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~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -1046,6 +1196,28 @@
             new Key<android.hardware.camera2.params.StreamConfigurationMap>("android.scaler.streamConfigurationMap", android.hardware.camera2.params.StreamConfigurationMap.class);
 
     /**
+     * <p>The crop type that this camera device supports.</p>
+     * <p>When passing a non-centered crop region ({@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}) to a camera
+     * device that only supports CENTER_ONLY cropping, the camera device will move the
+     * crop region to the center of the sensor active array ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize})
+     * and keep the crop region width and height unchanged. The camera device will return the
+     * final used crop region in metadata result {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.</p>
+     * <p>Camera devices that support FREEFORM cropping will support any crop region that
+     * is inside of the active array. The camera device will apply the same crop region and
+     * return the final used crop region in capture result metadata {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.</p>
+     * <p>FULL capability devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL) will support
+     * FREEFORM cropping.</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CaptureRequest#SCALER_CROP_REGION
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     * @see #SCALER_CROPPING_TYPE_CENTER_ONLY
+     * @see #SCALER_CROPPING_TYPE_FREEFORM
+     */
+    public static final Key<Integer> SCALER_CROPPING_TYPE =
+            new Key<Integer>("android.scaler.croppingType", int.class);
+
+    /**
      * <p>Area of raw data which corresponds to only
      * active pixels.</p>
      * <p>It is smaller or equal to
@@ -1324,19 +1496,6 @@
             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);
-
-    /**
      * <p>A fixed black level offset for each of the color filter arrangement
      * (CFA) mosaic channels.</p>
      * <p>This tag specifies the zero light value for each of the CFA mosaic
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ca03dae..77640d1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -242,10 +242,126 @@
      * @see StreamConfigurationMap#getOutputFormats()
      * @see StreamConfigurationMap#getOutputSizes(int)
      * @see StreamConfigurationMap#getOutputSizes(Class)
+     * @deprecated Use {@link #createCaptureSession} instead
      */
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
 
     /**
+     * <p>Create a new camera capture session by providing the target output set of Surfaces to the
+     * camera device.</p>
+     *
+     * <p>The active capture session determines the set of potential output Surfaces for
+     * the camera device for each capture request. A given request may use all
+     * or a only some of the outputs. Once the CameraCaptureSession is created, requests can be
+     * can be submitted with {@link CameraCaptureSession#capture capture},
+     * {@link CameraCaptureSession#captureBurst captureBurst},
+     * {@link CameraCaptureSession#setRepeatingRequest setRepeatingRequest}, or
+     * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</p>
+     *
+     * <p>Surfaces suitable for inclusion as a camera output can be created for
+     * various use cases and targets:</p>
+     *
+     * <ul>
+     *
+     * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Set the size of the
+     *   Surface with {@link android.view.SurfaceHolder#setFixedSize} to be one of the sizes
+     *   returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceView.class)}
+     *   and then obtain the Surface by calling {@link android.view.SurfaceHolder#getSurface}.</li>
+     *
+     * <li>For accessing through an OpenGL texture via a
+     *   {@link android.graphics.SurfaceTexture SurfaceTexture}: Set the size of
+     *   the SurfaceTexture with
+     *   {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one
+     *   of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceTexture.class)}
+     *   before creating a Surface from the SurfaceTexture with
+     *   {@link Surface#Surface}.</li>
+     *
+     * <li>For recording with {@link android.media.MediaCodec}: Call
+     *   {@link android.media.MediaCodec#createInputSurface} after configuring
+     *   the media codec to use one of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(MediaCodec.class)}
+     *   </li>
+     *
+     * <li>For recording with {@link android.media.MediaRecorder}: Call
+     *   {@link android.media.MediaRecorder#getSurface} after configuring the media recorder to use
+     *   one of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(MediaRecorder.class)},
+     *   or configuring it to use one of the supported
+     *   {@link android.media.CamcorderProfile CamcorderProfiles}.</li>
+     *
+     * <li>For efficient YUV processing with {@link android.renderscript}:
+     *   Create a RenderScript
+     *   {@link android.renderscript.Allocation Allocation} with a supported YUV
+     *   type, the IO_INPUT flag, and one of the sizes returned by
+     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(Allocation.class)},
+     *   Then obtain the Surface with
+     *   {@link android.renderscript.Allocation#getSurface}.</li>
+     *
+     * <li>For access to raw, uncompressed or JPEG data in the application: Create a
+     *   {@link android.media.ImageReader} object with the one of the supported
+     *   {@link StreamConfigurationMap#getOutputFormats() output image formats}, and a
+     *   size from the supported
+     *   {@link StreamConfigurationMap#getOutputSizes(int) sizes for that format}. Then obtain
+     *   a Surface from it with {@link android.media.ImageReader#getSurface}.</li>
+     *
+     * </ul>
+     *
+     * </p>
+     *
+     * <p>The camera device will query each Surface's size and formats upon this
+     * call, so they must be set to a valid setting at this time (in particular:
+     * if the format is user-visible, it must be one of
+     * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of
+     * {@link StreamConfigurationMap#getOutputSizes(int)}).</p>
+     *
+     * <p>It can take several hundred milliseconds for the session's configuration to complete,
+     * since camera hardware may need to be powered on or reconfigured. Once the configuration is
+     * complete and the session is ready to actually capture data, the provided
+     * {@link CameraCaptureSession.StateListener}'s
+     * {@link CameraCaptureSession.StateListener#onConfigured} callback will be called.</p>
+     *
+     * <p>If a prior CameraCaptureSession already exists when a new one is created, the previous
+     * session is closed. Any in-progress capture requests made on the prior session will be
+     * completed before the new session is configured and is able to start capturing its own
+     * requests. To minimize the transition time, the {@link CameraCaptureSession#abortCaptures}
+     * call can be used to discard the remaining requests for the prior capture session before a new
+     * one is created. Note that once the new session is created, the old one can no longer have its
+     * captures aborted.</p>
+     *
+     * <p>Using larger resolution outputs, or more outputs, can result in slower
+     * output rate from the device.</p>
+     *
+     * <p>Configuring a session with an empty or null list will close the current session, if
+     * any. This can be used to release the current session's target surfaces for another use.</p>
+     *
+     * @param outputs The new set of Surfaces that should be made available as
+     *                targets for captured image data.
+     * @param listener The listener to notify about the status of the new capture session.
+     * @param handler The handler on which the listener should be invoked, or {@code null} to use
+     *                the current thread's {@link android.os.Looper looper}.
+     * <!--
+     * @return A new camera capture session to use, or null if an empty/null set of Surfaces is
+     *         provided.
+     * -->
+     * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+     *                                  the listener is null, or the handler is null but the current
+     *                                  thread has no looper.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see CameraCaptureSession
+     * @see StreamConfigurationMap#getOutputFormats()
+     * @see StreamConfigurationMap#getOutputSizes(int)
+     * @see StreamConfigurationMap#getOutputSizes(Class)
+     */
+    public void createCaptureSession(List<Surface> outputs,
+            CameraCaptureSession.StateListener listener, Handler handler)
+            throws CameraAccessException;
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
      * initialized with template for a target use case. The settings are chosen
      * to be the best options for the specific camera device, so it is not
@@ -314,6 +430,7 @@
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
             throws CameraAccessException;
@@ -358,6 +475,7 @@
      * @see #capture
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
             Handler handler) throws CameraAccessException;
@@ -416,6 +534,7 @@
      * @see #setRepeatingBurst
      * @see #stopRepeating
      * @see #flush
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
             Handler handler) throws CameraAccessException;
@@ -474,6 +593,7 @@
      * @see #setRepeatingRequest
      * @see #stopRepeating
      * @see #flush
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
             Handler handler) throws CameraAccessException;
@@ -498,6 +618,7 @@
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see StateListener#onIdle
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public void stopRepeating() throws CameraAccessException;
 
@@ -534,25 +655,24 @@
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
      * @see #configureOutputs
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public void flush() throws CameraAccessException;
 
     /**
-     * Close the connection to this camera device.
+     * Close the connection to this camera device as quickly as possible.
      *
-     * <p>After this call, all calls to
-     * the camera device interface will throw a {@link IllegalStateException},
-     * except for calls to close(). Once the device has fully shut down, the
-     * {@link StateListener#onClosed} callback will be called, and the camera is
-     * free to be re-opened.</p>
+     * <p>Immediately after this call, all calls to the camera device or active session interface
+     * will throw a {@link IllegalStateException}, except for calls to close(). Once the device has
+     * fully shut down, the {@link StateListener#onClosed} callback will be called, and the camera
+     * is free to be re-opened.</p>
      *
-     * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the
-     * device's {@link StateListener} will occur, and any remaining submitted capture requests will
-     * not fire their {@link CaptureListener} callbacks.</p>
+     * <p>Immediately after this call, besides the final {@link StateListener#onClosed} calls, no
+     * further callbacks from the device or the active session will occur, and any remaining
+     * submitted capture requests will be discarded, as if
+     * {@link CameraCaptureSession#abortCaptures} had been called, except that no success or failure
+     * callbacks will be invoked.</p>
      *
-     * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close}
-     * once the flush completes. This will discard some capture requests, but results in faster
-     * shutdown.</p>
      */
     @Override
     public void close();
@@ -569,6 +689,7 @@
      * @see #captureBurst
      * @see #setRepeatingRequest
      * @see #setRepeatingBurst
+     * @deprecated Use {@link CameraCaptureSession} instead
      */
     public static abstract class CaptureListener {
 
@@ -834,6 +955,7 @@
          * <p>The default implementation of this method does nothing.</p>
          *
          * @param camera the camera device has that become unconfigured
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onUnconfigured(CameraDevice camera) {
             // Default empty implementation
@@ -863,6 +985,7 @@
          * @see CameraDevice#captureBurst
          * @see CameraDevice#setRepeatingBurst
          * @see CameraDevice#setRepeatingRequest
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onActive(CameraDevice camera) {
             // Default empty implementation
@@ -896,6 +1019,7 @@
          *
          * @see CameraDevice#configureOutputs
          * @see CameraDevice#flush
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onBusy(CameraDevice camera) {
             // Default empty implementation
@@ -943,6 +1067,7 @@
          * @see CameraDevice#configureOutputs
          * @see CameraDevice#stopRepeating
          * @see CameraDevice#flush
+         * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
          */
         public void onIdle(CameraDevice camera) {
             // Default empty implementation
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index cb463a6..03b342c 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -199,11 +199,7 @@
     }
 
     /**
-     * Open a connection to a camera with the given ID. Use
-     * {@link #getCameraIdList} to get the list of available camera
-     * devices. Note that even if an id is listed, open may fail if the device
-     * is disconnected between the calls to {@link #getCameraIdList} and
-     * {@link #openCamera}.
+     * Helper for openning a connection to a camera with the given ID.
      *
      * @param cameraId The unique identifier of the camera device to open
      * @param listener The listener for the camera. Must not be null.
@@ -216,20 +212,22 @@
      * @throws SecurityException if the application does not have permission to
      * access the camera
      * @throws IllegalArgumentException if listener or handler is null.
+     * @return A handle to the newly-created camera device.
      *
      * @see #getCameraIdList
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      */
-    private void openCameraDeviceUserAsync(String cameraId,
+    private CameraDevice openCameraDeviceUserAsync(String cameraId,
             CameraDevice.StateListener listener, Handler handler)
             throws CameraAccessException {
+        CameraDevice device = null;
         try {
 
             synchronized (mLock) {
 
                 ICameraDeviceUser cameraUser;
 
-                android.hardware.camera2.impl.CameraDevice device =
+                android.hardware.camera2.impl.CameraDevice deviceImpl =
                         new android.hardware.camera2.impl.CameraDevice(
                                 cameraId,
                                 listener,
@@ -237,7 +235,7 @@
 
                 BinderHolder holder = new BinderHolder();
 
-                ICameraDeviceCallbacks callbacks = device.getCallbacks();
+                ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
                 int id = Integer.parseInt(cameraId);
                 try {
                     mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
@@ -257,7 +255,8 @@
                 // TODO: factor out listener to be non-nested, then move setter to constructor
                 // For now, calling setRemoteDevice will fire initial
                 // onOpened/onUnconfigured callbacks.
-                device.setRemoteDevice(cameraUser);
+                deviceImpl.setRemoteDevice(cameraUser);
+                device = deviceImpl;
             }
 
         } catch (NumberFormatException e) {
@@ -268,6 +267,7 @@
         } catch (RemoteException e) {
             // impossible
         }
+        return device;
     }
 
     /**
@@ -278,20 +278,26 @@
      * is disconnected between the calls to {@link #getCameraIdList} and
      * {@link #openCamera}.</p>
      *
-     * <p>If the camera successfully opens after this function call returns,
-     * {@link CameraDevice.StateListener#onOpened} will be invoked with the
-     * newly opened {@link CameraDevice} in the unconfigured state.</p>
+     * <p>Once the camera is successfully opened, {@link CameraDevice.StateListener#onOpened} will
+     * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
+     * for operation by calling {@link CameraDevice#createCaptureSession} and
+     * {@link CameraDevice#createCaptureRequest}</p>
      *
+     * <!--
+     * <p>Since the camera device will be opened asynchronously, any asynchronous operations done
+     * on the returned CameraDevice instance will be queued up until the device startup has
+     * completed and the listener's {@link CameraDevice.StateListener#onOpened onOpened} method is
+     * called. The pending operations are then processed in order.</p>
+     * -->
      * <p>If the camera becomes disconnected during initialization
      * after this function call returns,
      * {@link CameraDevice.StateListener#onDisconnected} with a
      * {@link CameraDevice} in the disconnected state (and
      * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
      *
-     * <p>If the camera fails to initialize after this function call returns,
-     * {@link CameraDevice.StateListener#onError} will be invoked with a
-     * {@link CameraDevice} in the error state (and
-     * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
+     * <p>If opening the camera device fails, then the device listener's
+     * {@link CameraDevice.StateListener#onError onError} method will be called, and subsequent
+     * calls on the camera device will throw an {@link IllegalStateException}.</p>
      *
      * @param cameraId
      *             The unique identifier of the camera device to open
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index a11390d..4cde601 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
@@ -336,7 +247,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>
@@ -357,7 +267,6 @@
      * result.</p>
      *
      * @see CaptureRequest#BLACK_LEVEL_LOCK
-     * @see CameraCharacteristics#SENSOR_BASE_GAIN_FACTOR
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
      * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
@@ -446,6 +355,22 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5;
 
     //
+    // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
+    //
+
+    /**
+     * <p>The camera device will only support centered crop regions.</p>
+     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+     */
+    public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0;
+
+    /**
+     * <p>The camera device will support arbitrarily chosen crop regions.</p>
+     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+     */
+    public static final int SCALER_CROPPING_TYPE_FREEFORM = 1;
+
+    //
     // Enumeration values for CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
     //
 
@@ -1365,8 +1290,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;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 54ffd6b..a4aa296 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
@@ -569,9 +704,9 @@
      * should be nonnegative.</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 current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
-     * will ignore the sections outside the region and output the
-     * used sections in the frame metadata.</p>
+     * 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
@@ -644,9 +779,9 @@
      * 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 current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
-     * will ignore the sections outside the region and output the
-     * used sections in the frame metadata.</p>
+     * 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>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -731,9 +866,9 @@
      * 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 current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
-     * will ignore the sections outside the region and output the
-     * used sections in the frame metadata.</p>
+     * 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>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1126,8 +1261,11 @@
      * output, cropping to a smaller region if necessary to
      * maintain the stream's aspect ratio.</p>
      * <p>HAL2.x uses only (x, y, width)</p>
-     * <p>Any additional per-stream cropping must be done to
-     * maximize the final pixel area of the stream.</p>
+     * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+     * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
+     * it is not croppable. The crop region will be ignored by raw streams.</p>
+     * <p>For non-raw streams, any additional per-stream cropping will
+     * be done to maximize the final pixel area of the stream.</p>
      * <p>For example, if the crop region is set to a 4:3 aspect
      * ratio, then 4:3 streams should use the exact crop
      * region. 16:9 streams should further crop vertically
@@ -1308,8 +1446,16 @@
      * camera device. Applications can request lens shading map data by setting
      * {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
      * lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
-     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+     * applied by the camera device for this capture request.</p>
+     * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+     * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
+     * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
+     * to get best results, it is recommended that the applications wait for the AE and AWB to
+     * be converged before using the returned shading map data.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureRequest#CONTROL_AWB_MODE
      * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
      * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -1458,7 +1604,7 @@
      * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
      * These values are always available, and as close as possible to the
      * actually used nonlinear/nonglobal transforms.</p>
-     * <p>If a request is sent with TRANSFORM_MATRIX with the camera device's
+     * <p>If a request is sent with CONTRAST_CURVE with the camera device's
      * provided curve in FAST or HIGH_QUALITY, the image's tonemap will be
      * roughly the same.</p>
      *
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index f91fcb9..61d491b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -17,9 +17,12 @@
 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>
  *
@@ -36,7 +39,98 @@
  * <p>{@link CameraCharacteristics} objects are immutable.</p>
  *
  */
-public final class CaptureResult extends CameraMetadata {
+public final 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 +149,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,9 +165,85 @@
         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();
     }
 
     /**
@@ -110,6 +283,7 @@
      * @return int frame number
      */
     public int getFrameNumber() {
+        // TODO: @hide REQUEST_FRAME_COUNT
         return get(REQUEST_FRAME_COUNT);
     }
 
@@ -369,9 +543,9 @@
      * should be nonnegative.</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 current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
-     * will ignore the sections outside the region and output the
-     * used sections in the frame metadata.</p>
+     * 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
@@ -642,9 +816,9 @@
      * 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 current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
-     * will ignore the sections outside the region and output the
-     * used sections in the frame metadata.</p>
+     * 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>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1126,9 +1300,9 @@
      * 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 current {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, the camera device
-     * will ignore the sections outside the region and output the
-     * used sections in the frame metadata.</p>
+     * 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>
      *
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
@@ -1749,8 +1923,11 @@
      * output, cropping to a smaller region if necessary to
      * maintain the stream's aspect ratio.</p>
      * <p>HAL2.x uses only (x, y, width)</p>
-     * <p>Any additional per-stream cropping must be done to
-     * maximize the final pixel area of the stream.</p>
+     * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+     * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
+     * it is not croppable. The crop region will be ignored by raw streams.</p>
+     * <p>For non-raw streams, any additional per-stream cropping will
+     * be done to maximize the final pixel area of the stream.</p>
      * <p>For example, if the crop region is set to a 4:3 aspect
      * ratio, then 4:3 streams should use the exact crop
      * region. 16:9 streams should further crop vertically
@@ -1885,21 +2062,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
@@ -2006,8 +2168,16 @@
      * camera device. Applications can request lens shading map data by setting
      * {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} to ON, and then the camera device will provide
      * lens shading map data in {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap}, with size specified
-     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}.</p>
+     * by {@link CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE android.lens.info.shadingMapSize}; the returned shading map data will be the one
+     * applied by the camera device for this capture request.</p>
+     * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+     * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
+     * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
+     * to get best results, it is recommended that the applications wait for the AE and AWB to
+     * be converged before using the returned shading map data.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureRequest#CONTROL_AWB_MODE
      * @see CameraCharacteristics#LENS_INFO_SHADING_MAP_SIZE
      * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -2338,7 +2508,7 @@
      * {@link CaptureRequest#TONEMAP_CURVE_GREEN android.tonemap.curveGreen}, and {@link CaptureRequest#TONEMAP_CURVE_BLUE android.tonemap.curveBlue}.
      * These values are always available, and as close as possible to the
      * actually used nonlinear/nonglobal transforms.</p>
-     * <p>If a request is sent with TRANSFORM_MATRIX with the camera device's
+     * <p>If a request is sent with CONTRAST_CURVE with the camera device's
      * provided curve in FAST or HIGH_QUALITY, the image's tonemap will be
      * roughly the same.</p>
      *
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
new file mode 100644
index 0000000..54568ed
--- /dev/null
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -0,0 +1,368 @@
+/*
+ * 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.hardware.camera2;
+
+import android.graphics.Bitmap;
+import android.graphics.ImageFormat;
+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;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
+ *
+ * <p>
+ * This class is designed to be used with the {@link android.graphics.ImageFormat#RAW_SENSOR}
+ * buffers available from {@link android.hardware.camera2.CameraDevice}, or with Bayer-type raw
+ * pixel data that is otherwise generated by an application.  The DNG metadata tags will be
+ * generated from a {@link android.hardware.camera2.CaptureResult} object or set directly.
+ * </p>
+ *
+ * <p>
+ * The DNG file format is a cross-platform file format that is used to store pixel data from
+ * camera sensors with minimal pre-processing applied.  DNG files allow for pixel data to be
+ * defined in a user-defined colorspace, and have associated metadata that allow for this
+ * pixel data to be converted to the standard CIE XYZ colorspace during post-processing.
+ * </p>
+ *
+ * <p>
+ * For more information on the DNG file format and associated metadata, please refer to the
+ * <a href=
+ * "https://wwwimages2.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf">
+ * Adobe DNG 1.4.0.0 specification</a>.
+ * </p>
+ */
+public final class DngCreator implements AutoCloseable {
+
+    /**
+     * Create a new DNG object.
+     *
+     * <p>
+     * It is not necessary to call any set methods to write a well-formatted DNG file.
+     * </p>
+     * <p>
+     * DNG metadata tags will be generated from the corresponding parameters in the
+     * {@link android.hardware.camera2.CaptureResult} object.  This removes or overrides
+     * all previous tags set.
+     * </p>
+     *
+     * @param characteristics an object containing the static
+     *          {@link android.hardware.camera2.CameraCharacteristics}.
+     * @param metadata a metadata object to generate tags from.
+     */
+    public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
+        if (characteristics == null || metadata == null) {
+            throw new NullPointerException("Null argument to DngCreator constructor");
+        }
+        nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());
+    }
+
+    /**
+     * Set the orientation value to write.
+     *
+     * <p>
+     * This will be written as the TIFF "Orientation" tag {@code (0x0112)}.
+     * Calling this will override any prior settings for this tag.
+     * </p>
+     *
+     * @param orientation the orientation value to set, one of:
+     *                    <ul>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_NORMAL}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_HORIZONTAL}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_180}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_VERTICAL}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_TRANSPOSE}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_90}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_TRANSVERSE}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_270}</li>
+     *                    </ul>
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setOrientation(int orientation) {
+
+        if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
+                orientation > ExifInterface.ORIENTATION_ROTATE_270) {
+            throw new IllegalArgumentException("Orientation " + orientation +
+                    " is not a valid EXIF orientation value");
+        }
+        nativeSetOrientation(orientation);
+        return this;
+    }
+
+    /**
+     * Set the thumbnail image.
+     *
+     * <p>
+     * Pixel data will be converted to a Baseline TIFF RGB image, with 8 bits per color channel.
+     * The alpha channel will be discarded.
+     * </p>
+     *
+     * <p>
+     * The given bitmap should not be altered while this object is in use.
+     * </p>
+     *
+     * @param pixels a {@link android.graphics.Bitmap} of pixel data.
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setThumbnail(Bitmap pixels) {
+        if (pixels == null) {
+            throw new NullPointerException("Null argument to setThumbnail");
+        }
+
+        Bitmap.Config config = pixels.getConfig();
+
+        if (config != Bitmap.Config.ARGB_8888) {
+            pixels = pixels.copy(Bitmap.Config.ARGB_8888, false);
+            if (pixels == null) {
+                throw new IllegalArgumentException("Unsupported Bitmap format " + config);
+            }
+            nativeSetThumbnailBitmap(pixels);
+        }
+
+        return this;
+    }
+
+    /**
+     * Set the thumbnail image.
+     *
+     * <p>
+     * Pixel data is interpreted as a {@link android.graphics.ImageFormat#YUV_420_888} image.
+     * </p>
+     *
+     * <p>
+     * The given image should not be altered while this object is in use.
+     * </p>
+     *
+     * @param pixels an {@link android.media.Image} object with the format
+     *               {@link android.graphics.ImageFormat#YUV_420_888}.
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setThumbnail(Image pixels) {
+        if (pixels == null) {
+            throw new NullPointerException("Null argument to setThumbnail");
+        }
+
+        int format = pixels.getFormat();
+        if (format != ImageFormat.YUV_420_888) {
+            throw new IllegalArgumentException("Unsupported image format " + format);
+        }
+
+        Image.Plane[] planes = pixels.getPlanes();
+        nativeSetThumbnailImage(pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
+                planes[0].getRowStride(), planes[0].getPixelStride(), planes[1].getBuffer(),
+                planes[1].getRowStride(), planes[1].getPixelStride(), planes[1].getBuffer(),
+                planes[1].getRowStride(), planes[1].getPixelStride());
+
+        return this;
+    }
+
+
+    /**
+     * Set image location metadata.
+     *
+     * <p>
+     * The given location object must contain at least a valid time, latitude, and longitude
+     * (equivalent to the values returned by {@link android.location.Location#getTime()},
+     * {@link android.location.Location#getLatitude()}, and
+     * {@link android.location.Location#getLongitude()} methods).
+     * </p>
+     *
+     * @param location an {@link android.location.Location} object to set.
+     * @return this {@link #DngCreator} object.
+     *
+     * @throws java.lang.IllegalArgumentException if the given location object doesn't
+     *          contain enough information to set location metadata.
+     */
+    public DngCreator setLocation(Location location) {
+        /*TODO*/
+        return this;
+    }
+
+    /**
+     * Set the user description string to write.
+     *
+     * <p>
+     * This is equivalent to setting the TIFF "ImageDescription" tag {@code (0x010E)}.
+     * </p>
+     *
+     * @param description the user description string.
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setDescription(String description) {
+        /*TODO*/
+        return this;
+    }
+
+    /**
+     * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
+     * the currently configured metadata.
+     *
+     * <p>
+     * Raw pixel data must have 16 bits per pixel, and the input must contain at least
+     * {@code offset + 2 * width * height)} bytes.  The width and height of
+     * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
+     * and will typically be equal to the width and height of
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+     * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
+     * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}.  If insufficient
+     * metadata is available to write a well-formatted DNG file, an
+     * {@link java.lang.IllegalStateException} will be thrown.
+     * </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.io.InputStream} 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.
+     *
+     * @throws IOException if an error was encountered in the input or output stream.
+     * @throws java.lang.IllegalStateException if not enough metadata information has been
+     *          set to write a well-formatted DNG file.
+     * @throws java.lang.IllegalArgumentException if the size passed in does not match the
+     */
+    public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
+            throws IOException {
+        if (dngOutput == null || pixels == null) {
+            throw new NullPointerException("Null argument to writeImage");
+        }
+        nativeWriteInputStream(dngOutput, pixels, offset);
+    }
+
+    /**
+     * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
+     * the currently configured metadata.
+     *
+     * <p>
+     * Raw pixel data must have 16 bits per pixel, and the input must contain at least
+     * {@code offset + 2 * width * height)} bytes.  The width and height of
+     * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
+     * and will typically be equal to the width and height of
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+     * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
+     * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}.  If insufficient
+     * metadata is available to write a well-formatted DNG file, an
+     * {@link java.lang.IllegalStateException} will be thrown.
+     * </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.
+     *
+     * @throws IOException if an error was encountered in the input or output stream.
+     * @throws java.lang.IllegalStateException if not enough metadata information has been
+     *          set to write a well-formatted DNG file.
+     */
+    public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
+            throws IOException {
+        if (dngOutput == null || pixels == null) {
+            throw new NullPointerException("Null argument to writeImage");
+        }
+        nativeWriteByteBuffer(dngOutput, pixels, offset);
+    }
+
+    /**
+     * Write the pixel data to a DNG file with the currently configured metadata.
+     *
+     * <p>
+     * For this method to succeed, the {@link android.media.Image} input must contain
+     * {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data, otherwise an
+     * {@link java.lang.IllegalArgumentException} will be thrown.
+     * </p>
+     *
+     * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+     * @param pixels an {@link android.media.Image} to write.
+     *
+     * @throws java.io.IOException if an error was encountered in the output stream.
+     * @throws java.lang.IllegalArgumentException if an image with an unsupported format was used.
+     * @throws java.lang.IllegalStateException if not enough metadata information has been
+     *          set to write a well-formatted DNG file.
+     */
+    public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
+        if (dngOutput == null || pixels == null) {
+            throw new NullPointerException("Null argument to writeImage");
+        }
+
+        int format = pixels.getFormat();
+        if (format != ImageFormat.RAW_SENSOR) {
+            throw new IllegalArgumentException("Unsupported image format " + format);
+        }
+
+        Image.Plane[] planes = pixels.getPlanes();
+        nativeWriteImage(dngOutput, pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
+                planes[0].getRowStride(), planes[0].getPixelStride());
+    }
+
+    @Override
+    public void close() {
+        nativeDestroy();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * This field is used by native code, do not access or modify.
+     */
+    private long mNativeContext;
+
+    private static native void nativeClassInit();
+
+    private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
+                                                CameraMetadataNative nativeResult);
+
+    private synchronized native void nativeDestroy();
+
+    private synchronized native void nativeSetOrientation(int orientation);
+
+    private synchronized native void nativeSetThumbnailBitmap(Bitmap bitmap);
+
+    private synchronized native void nativeSetThumbnailImage(int width, int height,
+                                                             ByteBuffer yBuffer, int yRowStride,
+                                                             int yPixStride, ByteBuffer uBuffer,
+                                                             int uRowStride, int uPixStride,
+                                                             ByteBuffer vBuffer, int vRowStride,
+                                                             int vPixStride);
+
+    private synchronized native void nativeWriteImage(OutputStream out, int width, int height,
+                                                      ByteBuffer rawBuffer, int rowStride,
+                                                      int pixStride) throws IOException;
+
+    private synchronized native void nativeWriteByteBuffer(OutputStream out, ByteBuffer rawBuffer,
+                                                           long offset) throws IOException;
+
+    private synchronized native void nativeWriteInputStream(OutputStream out, InputStream rawStream,
+                                                            long offset) throws IOException;
+
+    static {
+        nativeClassInit();
+    }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index e78ffff..b082a70 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -19,6 +19,7 @@
 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
 
 import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -253,6 +254,13 @@
     }
 
     @Override
+    public void createCaptureSession(List<Surface> outputs,
+            CameraCaptureSession.StateListener listener, Handler handler)
+            throws CameraAccessException {
+        // TODO
+    }
+
+    @Override
     public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException {
         synchronized (mLock) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 27cfd38..ab2c49a 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;
@@ -46,10 +46,14 @@
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.TypeReference;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
@@ -58,7 +62,147 @@
  * 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);
@@ -84,6 +228,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,8 +267,36 @@
         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) {
+        Preconditions.checkNotNull(key, "key must not be null");
+
         T value = getOverride(key);
         if (value != null) {
             return value;
@@ -152,6 +338,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 +386,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);
@@ -342,6 +552,18 @@
         return new StreamConfigurationMap(configurations, minFrameDurations, stallDurations);
     }
 
+    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();
 
@@ -440,6 +662,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 +754,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 +838,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/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 70cc708..5df4baf 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -47,7 +47,7 @@
     }
 
     /**
-     * Get Ethernet configuration
+     * Get Ethernet configuration.
      * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
      */
     public IpConfiguration getConfiguration() {
@@ -61,8 +61,7 @@
     }
 
     /**
-     * Set Ethernet configuration
-     * @return true if setting success
+     * Set Ethernet configuration.
      */
     public void setConfiguration(IpConfiguration config) {
         try {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index af45fa0..d3c6fd5 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);
@@ -3351,7 +3353,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/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 5ffffb5..e4f93a8 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -462,7 +462,7 @@
          * <p>
          * A value of 1 indicates the channel is included in the channel list that applications use
          * to browse channels, a value of 0 indicates the channel is not included in the list. If
-         * not specified, this value is set to 1 by default.
+         * not specified, this value is set to 1 (browsable) by default.
          * </p><p>
          * Type: INTEGER (boolean)
          * </p>
@@ -470,6 +470,36 @@
         public static final String COLUMN_BROWSABLE = "browsable";
 
         /**
+         * The flag indicating whether this TV channel is searchable or not.
+         * <p>
+         * In some regions, it is not allowed to surface search results for a given channel without
+         * broadcaster's consent. This is used to impose such restriction. A value of 1 indicates
+         * the channel is searchable and can be included in search results, a value of 0 indicates
+         * the channel and its TV programs are hidden from search. If not specified, this value is
+         * set to 1 (searchable) by default.
+         * </p>
+         * <p>
+         * Type: INTEGER (boolean)
+         * </p>
+         */
+        public static final String COLUMN_SEARCHABLE = "searchable";
+
+        /**
+         * The flag indicating whether this TV channel is locked or not.
+         * <p>
+         * This is primarily used for alternative parental control to prevent unauthorized users
+         * from watching the current channel regardless of the content rating. A value of 1
+         * indicates the channel is locked and the user is required to enter passcode to unlock it
+         * in order to watch the current program from the channel, a value of 0 indicates the
+         * channel is not locked thus the user is not prompted to enter passcode If not specified,
+         * this value is set to 0 (not locked) by default.
+         * </p><p>
+         * Type: INTEGER (boolean)
+         * </p>
+         */
+        public static final String COLUMN_LOCKED = "locked";
+
+        /**
          * Generic data used by individual TV input services.
          * <p>
          * Type: BLOB
@@ -544,6 +574,33 @@
         public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
 
         /**
+         * The comma-separated genre string of this TV program.
+         * <p>
+         * Use the same language appeared in the underlying broadcast standard, if applicable. (For
+         * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, use one of the
+         * following genres:
+         * <ul>
+         *     <li>Family/Kids</li>
+         *     <li>Sports</li>
+         *     <li>Shopping</li>
+         *     <li>Movies</li>
+         *     <li>Comedy</li>
+         *     <li>Travel</li>
+         *     <li>Drama</li>
+         *     <li>Education</li>
+         *     <li>Animal/Wildlife</li>
+         *     <li>News</li>
+         *     <li>Gaming</li>
+         *     <li>Others</li>
+         * </ul>
+         * </p><p>
+         * Type: TEXT
+         * </p>
+         */
+        public static final String COLUMN_GENRE = "genre";
+
+        /**
          * The description of this TV program that is displayed to the user by default.
          * <p>
          * The maximum length of this field is 256 characters.
@@ -566,6 +623,17 @@
         public static final String COLUMN_LONG_DESCRIPTION = "long_description";
 
         /**
+         * The comma-separated audio languages of this TV program.
+         * <p>
+         * This is used to describe available audio languages included in the program. Use
+         * 3-character language code as specified by ISO 639-2.
+         * </p><p>
+         * Type: TEXT
+         * </p>
+         */
+        public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
+
+        /**
          * Generic data used by TV input services.
          * <p>
          * Type: BLOB
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index a272296..424d860 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -41,10 +41,6 @@
  * An implementation of Canvas on top of OpenGL ES 2.0.
  */
 class GLES20Canvas extends HardwareCanvas {
-    // Must match modifiers used in the JNI layer
-    private static final int MODIFIER_NONE = 0;
-    private static final int MODIFIER_SHADER = 2;
-
     private final boolean mOpaque;
     protected long mRenderer;
 
@@ -650,13 +646,8 @@
     @Override
     public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
-                    startAngle, sweepAngle, useCenter, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom,
+                startAngle, sweepAngle, useCenter, paint.mNativePaint);
     }
 
     private static native void nDrawArc(long renderer, float left, float top,
@@ -672,7 +663,6 @@
     public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing patches
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
@@ -682,7 +672,6 @@
     public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing patches
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
@@ -694,14 +683,8 @@
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
     }
 
     private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -710,15 +693,9 @@
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
-                    matrix.native_instance, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
+                matrix.native_instance, nativePaint);
     }
 
     private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -727,55 +704,43 @@
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
 
-            int left, top, right, bottom;
-            if (src == null) {
-                left = top = 0;
-                right = bitmap.getWidth();
-                bottom = bitmap.getHeight();
-            } else {
-                left = src.left;
-                right = src.right;
-                top = src.top;
-                bottom = src.bottom;
-            }
-
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
-                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        int left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
         }
+
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
         throwIfCannotDraw(bitmap);
-        // Shaders are ignored when drawing bitmaps
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-    
-            float left, top, right, bottom;
-            if (src == null) {
-                left = top = 0;
-                right = bitmap.getWidth();
-                bottom = bitmap.getHeight();
-            } else {
-                left = src.left;
-                right = src.right;
-                top = src.top;
-                bottom = src.bottom;
-            }
-    
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
-                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+        float left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
         }
+
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
     }
 
     private static native void nDrawBitmap(long renderer, long bitmap, byte[] buffer,
@@ -805,7 +770,6 @@
             throw new ArrayIndexOutOfBoundsException();
         }
 
-        // Shaders are ignored when drawing bitmaps
         final long nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, colors, offset, stride, x, y,
                 width, height, hasAlpha, nativePaint);
@@ -817,7 +781,6 @@
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
             int width, int height, boolean hasAlpha, Paint paint) {
-        // Shaders are ignored when drawing bitmaps
         drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
     }
 
@@ -840,14 +803,9 @@
             checkRange(colors.length, colorOffset, count);
         }
 
-        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
-        try {
-            final long nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
-                    verts, vertOffset, colors, colorOffset, nativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+                verts, vertOffset, colors, colorOffset, nativePaint);
     }
 
     private static native void nDrawBitmapMesh(long renderer, long bitmap, byte[] buffer,
@@ -856,12 +814,7 @@
 
     @Override
     public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint);
     }
 
     private static native void nDrawCircle(long renderer, float cx, float cy,
@@ -906,12 +859,7 @@
         if ((offset | count) < 0 || offset + count > pts.length) {
             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
         }
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
     }
 
     private static native void nDrawLines(long renderer, float[] points,
@@ -924,12 +872,7 @@
 
     @Override
     public void drawOval(RectF oval, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint);
     }
 
     private static native void nDrawOval(long renderer, float left, float top,
@@ -944,17 +887,12 @@
 
     @Override
     public void drawPath(Path path, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            if (path.isSimplePath) {
-                if (path.rects != null) {
-                    nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
-                }
-            } else {
-                nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+        if (path.isSimplePath) {
+            if (path.rects != null) {
+                nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
             }
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        } else {
+            nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
         }
     }
 
@@ -962,12 +900,7 @@
     private static native void nDrawRects(long renderer, long region, long paint);
 
     void drawRects(float[] rects, int count, Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawRects(mRenderer, rects, count, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawRects(mRenderer, rects, count, paint.mNativePaint);
     }
 
     private static native void nDrawRects(long renderer, float[] rects, int count, long paint);
@@ -1029,12 +962,7 @@
     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
         if (count < 2) return;
 
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
     }
 
     private static native void nDrawPoints(long renderer, float[] points,
@@ -1047,12 +975,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
     }
 
     private static native void nDrawPosText(long renderer, char[] text, int index, int count,
@@ -1065,12 +988,7 @@
             throw new ArrayIndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
     }
 
     private static native void nDrawPosText(long renderer, String text, int start, int end,
@@ -1079,12 +997,7 @@
     @Override
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
         if (left == right || top == bottom) return;
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
     }
 
     private static native void nDrawRect(long renderer, float left, float top,
@@ -1108,12 +1021,7 @@
     @Override
     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             Paint paint) {
-        int modifiers = setupModifiers(paint, MODIFIER_SHADER);
-        try {
-            nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawRoundRect(mRenderer, left, top, right, bottom, rx, ry, paint.mNativePaint);
     }
 
     private static native void nDrawRoundRect(long renderer, float left, float top,
@@ -1125,13 +1033,8 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint,
-                paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawText(mRenderer, text, index, count, x, y,
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
     
     private static native void nDrawText(long renderer, char[] text, int index, int count,
@@ -1139,24 +1042,18 @@
 
     @Override
     public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
-        int modifiers = setupModifiers(paint);
-        try {
-            if (text instanceof String || text instanceof SpannedString ||
-                    text instanceof SpannableString) {
-                nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
-                        paint.mNativePaint, paint.mNativeTypeface);
-            } else if (text instanceof GraphicsOperations) {
-                ((GraphicsOperations) text).drawText(this, start, end, x, y,
-                                                         paint);
-            } else {
-                char[] buf = TemporaryBuffer.obtain(end - start);
-                TextUtils.getChars(text, start, end, buf, 0);
-                nDrawText(mRenderer, buf, 0, end - start, x, y,
-                        paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
-                TemporaryBuffer.recycle(buf);
-            }
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+                    paint.mNativePaint, paint.mNativeTypeface);
+        } else if (text instanceof GraphicsOperations) {
+            ((GraphicsOperations) text).drawText(this, start, end, x, y, paint);
+        } else {
+            char[] buf = TemporaryBuffer.obtain(end - start);
+            TextUtils.getChars(text, start, end, buf, 0);
+            nDrawText(mRenderer, buf, 0, end - start, x, y,
+                    paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
+            TemporaryBuffer.recycle(buf);
         }
     }
 
@@ -1166,13 +1063,8 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint,
-                paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawText(mRenderer, text, start, end, x, y,
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawText(long renderer, String text, int start, int end,
@@ -1180,13 +1072,8 @@
 
     @Override
     public void drawText(String text, float x, float y, Paint paint) {
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
-                    paint.mNativePaint, paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawText(mRenderer, text, 0, text.length(), x, y,
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     @Override
@@ -1196,13 +1083,8 @@
             throw new ArrayIndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
-                    paint.mBidiFlags, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
+                paint.mBidiFlags, paint.mNativePaint);
     }
 
     private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count,
@@ -1212,13 +1094,8 @@
     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
         if (text.length() == 0) return;
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
-                    paint.mBidiFlags, paint.mNativePaint);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
+                paint.mBidiFlags, paint.mNativePaint);
     }
 
     private static native void nDrawTextOnPath(long renderer, String text, int start, int end,
@@ -1234,13 +1111,8 @@
             throw new IllegalArgumentException("Unknown direction: " + dir);
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
-                    paint.mNativePaint, paint.mNativeTypeface);
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
-        }
+        nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+                paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawTextRun(long renderer, char[] text, int index, int count,
@@ -1253,27 +1125,22 @@
             throw new IndexOutOfBoundsException();
         }
 
-        int modifiers = setupModifiers(paint);
-        try {
-            int flags = dir == 0 ? 0 : 1;
-            if (text instanceof String || text instanceof SpannedString ||
-                    text instanceof SpannableString) {
-                nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
-                        contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
-            } else if (text instanceof GraphicsOperations) {
-                ((GraphicsOperations) text).drawTextRun(this, start, end,
-                        contextStart, contextEnd, x, y, flags, paint);
-            } else {
-                int contextLen = contextEnd - contextStart;
-                int len = end - start;
-                char[] buf = TemporaryBuffer.obtain(contextLen);
-                TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
-                nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
-                        x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
-                TemporaryBuffer.recycle(buf);
-            }
-        } finally {
-            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+        int flags = dir == 0 ? 0 : 1;
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+                    contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+        } else if (text instanceof GraphicsOperations) {
+            ((GraphicsOperations) text).drawTextRun(this, start, end,
+                    contextStart, contextEnd, x, y, flags, paint);
+        } else {
+            int contextLen = contextEnd - contextStart;
+            int len = end - start;
+            char[] buf = TemporaryBuffer.obtain(contextLen);
+            TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+            nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+                    x, y, flags, paint.mNativePaint, paint.mNativeTypeface);
+            TemporaryBuffer.recycle(buf);
         }
     }
 
@@ -1286,40 +1153,4 @@
             int indexOffset, int indexCount, Paint paint) {
         // TODO: Implement
     }
-
-    private int setupModifiers(Bitmap b, Paint paint) {
-        if (b.getConfig() != Bitmap.Config.ALPHA_8) {
-            return MODIFIER_NONE;
-        } else {
-            return setupModifiers(paint);
-        }
-    }
-
-    private int setupModifiers(Paint paint) {
-        int modifiers = MODIFIER_NONE;
-
-        final Shader shader = paint.getShader();
-        if (shader != null) {
-            nSetupShader(mRenderer, shader.native_shader);
-            modifiers |= MODIFIER_SHADER;
-        }
-
-        return modifiers;
-    }
-
-    private int setupModifiers(Paint paint, int flags) {
-        int modifiers = MODIFIER_NONE;
-
-        final Shader shader = paint.getShader();
-        if (shader != null && (flags & MODIFIER_SHADER) != 0) {
-            nSetupShader(mRenderer, shader.native_shader);
-            modifiers |= MODIFIER_SHADER;
-        }
-
-        return modifiers;
-    }
-
-    private static native void nSetupShader(long renderer, long shader);
-
-    private static native void nResetModifiers(long renderer, int modifiers);
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index a902ce7..3c4d83f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -579,6 +579,12 @@
     abstract void fence();
 
     /**
+     * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
+     */
+    public void notifyFramePending() {
+    }
+
+    /**
      * Describes a series of frames that should be drawn on screen as a graph.
      * Each frame is composed of 1 or more elements.
      */
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 704d516..8417887 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -291,6 +291,11 @@
     }
 
     @Override
+    public void notifyFramePending() {
+        nNotifyFramePending(mNativeProxy);
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         try {
             nDeleteProxy(mNativeProxy);
@@ -364,4 +369,5 @@
     private static native void nDestroyLayer(long nativeProxy, long layer);
 
     private static native void nFence(long nativeProxy);
+    private static native void nNotifyFramePending(long nativeProxy);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 799a406..fc7bf0e 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();
         }
     }
 
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 4fde1e4..1bb20c9 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1144,6 +1144,12 @@
     public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
 
     /**
+     * Show the recents task list app.
+     * @hide
+     */
+    public void showRecentApps();
+
+    /**
      * @return The current height of the input method window.
      */
     public int getInputMethodWindowVisibleHeightLw();
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 1152e17..43f623b 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -256,6 +256,16 @@
     }
 
     @Override
+    public void invalidateDrawable(Drawable dr) {
+        super.invalidateDrawable(dr);
+
+        if (dr == mThumb) {
+            // Handle changes to thumb width and height.
+            requestLayout();
+        }
+    }
+
+    @Override
     void onProgressRefresh(float scale, boolean fromUser) {
         super.onProgressRefresh(scale, fromUser);
 
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 1533510..3ae9508 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.RemotableViewMethod;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -156,10 +157,36 @@
             mCheckMarkWidth = 0;
         }
         mCheckMarkDrawable = d;
-        // Do padding resolution. This will call internalSetPadding() and do a requestLayout() if needed.
+
+        // Do padding resolution. This will call internalSetPadding() and do a
+        // requestLayout() if needed.
         resolvePadding();
     }
 
+    @RemotableViewMethod
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+
+        if (mCheckMarkDrawable != null) {
+            mCheckMarkDrawable.setVisible(visibility == VISIBLE, false);
+        }
+    }
+
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+
+        if (mCheckMarkDrawable != null) {
+            mCheckMarkDrawable.jumpToCurrentState();
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return who == mCheckMarkDrawable || super.verifyDrawable(who);
+    }
+
     /**
      * Gets the checkmark drawable
      *
@@ -249,6 +276,11 @@
             }
             checkMarkDrawable.setBounds(mScrollX + left, top, mScrollX + right, bottom);
             checkMarkDrawable.draw(canvas);
+
+            final Drawable background = getBackground();
+            if (background != null) {
+                background.setHotspotBounds(mScrollX + left, top, mScrollX + right, bottom);
+            }
         }
     }
     
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index ad1a023..c5c6e64 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -951,9 +951,8 @@
 
         final int[] myDrawableState = getDrawableState();
 
-        if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
-            // Handle changes to thumb width and height.
-            requestLayout();
+        if (mThumbDrawable != null) {
+            mThumbDrawable.setState(myDrawableState);
         }
 
         if (mTrackDrawable != null) {
@@ -964,6 +963,16 @@
     }
 
     @Override
+    public void invalidateDrawable(Drawable drawable) {
+        super.invalidateDrawable(drawable);
+
+        if (drawable == mThumbDrawable) {
+            // Handle changes to thumb width and height.
+            requestLayout();
+        }
+    }
+
+    @Override
     protected boolean verifyDrawable(Drawable who) {
         return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
     }
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 664f9db..5547a10 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -1026,7 +1026,7 @@
                         ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
                 if (mCursor == null) {
                     adapter = (mAdapter != null) ? mAdapter
-                            : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
+                            : new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
                 } else {
                     adapter = new SimpleCursorAdapter(mContext, layout, 
                             mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
@@ -1081,4 +1081,20 @@
         }
     }
 
+    private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
+        public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
+                CharSequence[] objects) {
+            super(context, resource, textViewResourceId, objects);
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8428f66..ed9f9bc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -328,11 +328,13 @@
 
     int mLastDischargeStepLevel;
     long mLastDischargeStepTime;
+    int mMinDischargeStepLevel;
     int mNumDischargeStepDurations;
     final long[] mDischargeStepDurations = new long[MAX_LEVEL_STEPS];
 
     int mLastChargeStepLevel;
     long mLastChargeStepTime;
+    int mMaxChargeStepLevel;
     int mNumChargeStepDurations;
     final long[] mChargeStepDurations = new long[MAX_LEVEL_STEPS];
 
@@ -887,6 +889,7 @@
             mLastTime = 0;
             mUnpluggedTime = in.readLong();
             timeBase.add(this);
+            if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
         }
 
         Timer(int type, TimeBase timeBase) {
@@ -917,6 +920,8 @@
         }
 
         public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+            if (DEBUG) Log.i(TAG, "**** WRITING TIMER #" + mType + ": mTotalTime="
+                    + computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
             out.writeInt(mCount);
             out.writeInt(mLoadedCount);
             out.writeInt(mUnpluggedCount);
@@ -2337,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: "
@@ -2347,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;
@@ -2357,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++;
         }
@@ -2382,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) {
@@ -5550,6 +5544,7 @@
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase);
         }
+        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
         mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -5581,7 +5576,6 @@
         }
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
-        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
         mOnBattery = mOnBatteryInternal = false;
         long uptime = SystemClock.uptimeMillis() * 1000;
         long realtime = SystemClock.elapsedRealtime() * 1000;
@@ -5958,6 +5952,7 @@
                 mNumDischargeStepDurations = 0;
             }
             mLastDischargeStepLevel = level;
+            mMinDischargeStepLevel = level;
             mLastDischargeStepTime = -1;
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
@@ -5996,6 +5991,7 @@
             updateTimeBasesLocked(false, !screenOn, uptime, realtime);
             mNumChargeStepDurations = 0;
             mLastChargeStepLevel = level;
+            mMaxChargeStepLevel = level;
             mLastChargeStepTime = -1;
         }
         if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
@@ -6117,19 +6113,21 @@
                     addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 if (onBattery) {
-                    if (mLastDischargeStepLevel != level) {
+                    if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
                         mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
                                 mNumDischargeStepDurations, mLastDischargeStepTime,
                                 mLastDischargeStepLevel - level, elapsedRealtime);
                         mLastDischargeStepLevel = level;
+                        mMinDischargeStepLevel = level;
                         mLastDischargeStepTime = elapsedRealtime;
                     }
                 } else {
-                    if (mLastChargeStepLevel != level) {
+                    if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                         mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
                                 mNumChargeStepDurations, mLastChargeStepTime,
                                 level - mLastChargeStepLevel, elapsedRealtime);
                         mLastChargeStepLevel = level;
+                        mMaxChargeStepLevel = level;
                         mLastChargeStepTime = elapsedRealtime;
                     }
                 }
@@ -7495,6 +7493,8 @@
             mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase,
                     in);
         }
+        mInteractive = false;
+        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
         mPhoneOn = false;
         mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
@@ -7536,8 +7536,6 @@
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOn = false;
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
-        mInteractive = false;
-        mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
         mDischargeUnplugLevel = in.readInt();
         mDischargePlugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 1366499..a01e9b7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,9 +24,9 @@
 {
     void setIcon(int index, in StatusBarIcon icon);
     void removeIcon(int index);
-    void addNotification(IBinder key, in StatusBarNotification notification);
-    void updateNotification(IBinder key, in StatusBarNotification notification);
-    void removeNotification(IBinder key);
+    void addNotification(in StatusBarNotification notification);
+    void updateNotification(in StatusBarNotification notification);
+    void removeNotification(String key);
     void disable(int state);
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel();
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 2d6cf2e..a3b417f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -39,8 +39,8 @@
     // ---- Methods below are for use by the status bar policy services ----
     // You need the STATUS_BAR_SERVICE permission
     void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
-            out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications,
-            out int[] switches, out List<IBinder> binders);
+            out List<StatusBarNotification> notifications, out int[] switches,
+            out List<IBinder> binders);
     void onPanelRevealed();
     void onPanelHidden();
     void onNotificationClick(String key);
diff --git a/core/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/jni/Android.mk b/core/jni/Android.mk
index 835a648..a159715 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 \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2d350e0..0c7eefa 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);
@@ -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/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..7935329 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/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
new file mode 100644
index 0000000..7b686e7
--- /dev/null
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -0,0 +1,774 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "DngCreator_JNI"
+
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+#include <img_utils/DngUtils.h>
+#include <img_utils/TagDefinitions.h>
+#include <img_utils/TiffIfd.h>
+#include <img_utils/TiffWriter.h>
+#include <img_utils/Output.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+#include <cutils/properties.h>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+using namespace android;
+using namespace img_utils;
+
+#define BAIL_IF_INVALID(expr, jnienv, tagId) \
+    if ((expr) != OK) { \
+        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+                "Invalid metadata for tag %x", tagId); \
+        return; \
+    }
+
+#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
+    if (entry.count == 0) { \
+        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+                "Missing metadata fields for tag %x", tagId); \
+        return; \
+    }
+
+#define ANDROID_DNGCREATOR_CTX_JNI_ID     "mNativeContext"
+
+static struct {
+    jfieldID mNativeContext;
+} gDngCreatorClassInfo;
+
+static struct {
+    jmethodID mWriteMethod;
+} gOutputStreamClassInfo;
+
+enum {
+    BITS_PER_SAMPLE = 16,
+    BYTES_PER_SAMPLE = 2,
+    TIFF_IFD_0 = 0
+};
+
+// ----------------------------------------------------------------------------
+
+// This class is not intended to be used across JNI calls.
+class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
+public:
+    JniOutputStream(JNIEnv* env, jobject outStream);
+
+    virtual ~JniOutputStream();
+
+    status_t open();
+    status_t write(const uint8_t* buf, size_t offset, size_t count);
+    status_t close();
+private:
+    enum {
+        BYTE_ARRAY_LENGTH = 1024
+    };
+    jobject mOutputStream;
+    JNIEnv* mEnv;
+    jbyteArray mByteArray;
+};
+
+JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
+        mEnv(env) {
+    mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
+    if (mByteArray == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
+    }
+}
+
+JniOutputStream::~JniOutputStream() {
+    mEnv->DeleteLocalRef(mByteArray);
+}
+
+status_t JniOutputStream::open() {
+    // Do nothing
+    return OK;
+}
+
+status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
+    while(count > 0) {
+        size_t len = BYTE_ARRAY_LENGTH;
+        len = (count > len) ? len : count;
+        mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
+
+        if (mEnv->ExceptionCheck()) {
+            return BAD_VALUE;
+        }
+
+        mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
+                0, len);
+
+        if (mEnv->ExceptionCheck()) {
+            return BAD_VALUE;
+        }
+
+        count -= len;
+        offset += len;
+    }
+    return OK;
+}
+
+status_t JniOutputStream::close() {
+    // Do nothing
+    return OK;
+}
+
+// ----------------------------------------------------------------------------
+
+extern "C" {
+
+static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
+    ALOGV("%s:", __FUNCTION__);
+    return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
+            gDngCreatorClassInfo.mNativeContext));
+}
+
+static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
+    ALOGV("%s:", __FUNCTION__);
+    TiffWriter* current = DngCreator_getCreator(env, thiz);
+    if (writer != NULL) {
+        writer->incStrong((void*) DngCreator_setCreator);
+    }
+    if (current) {
+        current->decStrong((void*) DngCreator_setCreator);
+    }
+    env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
+            reinterpret_cast<jlong>(writer.get()));
+}
+
+static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
+    ALOGV("%s:", __FUNCTION__);
+
+    gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
+            ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
+    LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
+            "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");
+    gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
+    LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
+}
+
+static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
+        jobject resultsPtr) {
+    ALOGV("%s:", __FUNCTION__);
+    CameraMetadata characteristics;
+    CameraMetadata results;
+    if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
+         jniThrowException(env, "java/lang/AssertionError",
+                "No native metadata defined for camera characteristics.");
+         return;
+    }
+    if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
+        jniThrowException(env, "java/lang/AssertionError",
+                "No native metadata defined for capture results.");
+        return;
+    }
+
+    sp<TiffWriter> writer = new TiffWriter();
+
+    writer->addIfd(TIFF_IFD_0);
+
+    status_t err = OK;
+
+    const uint32_t samplesPerPixel = 1;
+    const uint32_t bitsPerSample = BITS_PER_SAMPLE;
+    const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
+    uint32_t imageWidth = 0;
+    uint32_t imageHeight = 0;
+
+    OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+
+    // TODO: Greensplit.
+    // TODO: UniqueCameraModel
+    // TODO: Add remaining non-essential tags
+    {
+        // Set orientation
+        uint16_t orientation = 1; // Normal
+        BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
+                TAG_ORIENTATION);
+    }
+
+    {
+        // Set subfiletype
+        uint32_t subfileType = 0; // Main image
+        BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
+                TAG_NEWSUBFILETYPE);
+    }
+
+    {
+        // Set bits per sample
+        uint16_t bits = static_cast<uint16_t>(bitsPerSample);
+        BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
+                TAG_BITSPERSAMPLE);
+    }
+
+    {
+        // Set compression
+        uint16_t compression = 1; // None
+        BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
+                TAG_COMPRESSION);
+    }
+
+    {
+        // Set dimensions
+        camera_metadata_entry entry =
+                characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+        BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
+        uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
+        uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
+        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
+                TAG_IMAGEWIDTH);
+        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
+                TAG_IMAGELENGTH);
+        imageWidth = width;
+        imageHeight = height;
+    }
+
+    {
+        // Set photometric interpretation
+        uint16_t interpretation = 32803;
+        BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
+                TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
+    }
+
+    {
+        // Set blacklevel tags
+        camera_metadata_entry entry =
+                characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+        BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
+        const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
+        BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
+                TAG_BLACKLEVEL);
+
+        uint16_t repeatDim[2] = {2, 2};
+        BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
+                TAG_BLACKLEVELREPEATDIM);
+    }
+
+    {
+        // Set samples per pixel
+        uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
+        BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
+                env, TAG_SAMPLESPERPIXEL);
+    }
+
+    {
+        // Set planar configuration
+        uint16_t config = 1; // Chunky
+        BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
+                env, TAG_PLANARCONFIGURATION);
+    }
+
+    {
+        // Set CFA pattern dimensions
+        uint16_t repeatDim[2] = {2, 2};
+        BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
+                env, TAG_CFAREPEATPATTERNDIM);
+    }
+
+    {
+        // Set CFA pattern
+        camera_metadata_entry entry =
+                        characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+        BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
+        camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
+                static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
+                entry.data.u8[0]);
+        switch(cfa) {
+            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
+                uint8_t cfa[4] = {0, 1, 1, 2};
+                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+                                                env, TAG_CFAPATTERN);
+                opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+                break;
+            }
+            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
+                uint8_t cfa[4] = {1, 0, 2, 1};
+                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+                                                env, TAG_CFAPATTERN);
+                opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
+                break;
+            }
+            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
+                uint8_t cfa[4] = {1, 2, 0, 1};
+                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+                                                env, TAG_CFAPATTERN);
+                opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
+                break;
+            }
+            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
+                uint8_t cfa[4] = {2, 1, 1, 0};
+                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+                                env, TAG_CFAPATTERN);
+                opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
+                break;
+            }
+            default: {
+                jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                            "Invalid metadata for tag %d", TAG_CFAPATTERN);
+                return;
+            }
+        }
+    }
+
+    {
+        // Set CFA plane color
+        uint8_t cfaPlaneColor[3] = {0, 1, 2};
+        BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
+                env, TAG_CFAPLANECOLOR);
+    }
+
+    {
+        // Set CFA layout
+        uint16_t cfaLayout = 1;
+        BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
+                env, TAG_CFALAYOUT);
+    }
+
+    {
+        // Set DNG version information
+        uint8_t version[4] = {1, 4, 0, 0};
+        BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
+                env, TAG_DNGVERSION);
+
+        uint8_t backwardVersion[4] = {1, 1, 0, 0};
+        BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
+                env, TAG_DNGBACKWARDVERSION);
+    }
+
+    {
+        // Set whitelevel
+        camera_metadata_entry entry =
+                characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
+        BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
+        uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
+        BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
+                TAG_WHITELEVEL);
+    }
+
+    {
+        // Set default scale
+        uint32_t defaultScale[4] = {1, 1, 1, 1};
+        BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
+                env, TAG_DEFAULTSCALE);
+    }
+
+    bool singleIlluminant = false;
+    {
+        // Set calibration illuminants
+        camera_metadata_entry entry1 =
+            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
+        BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
+        camera_metadata_entry entry2 =
+            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
+        if (entry2.count == 0) {
+            singleIlluminant = true;
+        }
+        uint16_t ref1 = entry1.data.u8[0];
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
+                TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
+
+        if (!singleIlluminant) {
+            uint16_t ref2 = entry2.data.u8[0];
+            BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
+                    TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
+        }
+    }
+
+    {
+        // Set color transforms
+        camera_metadata_entry entry1 =
+            characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
+        BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
+
+        int32_t colorTransform1[entry1.count * 2];
+
+        size_t ctr = 0;
+        for(size_t i = 0; i < entry1.count; ++i) {
+            colorTransform1[ctr++] = entry1.data.r[i].numerator;
+            colorTransform1[ctr++] = entry1.data.r[i].denominator;
+        }
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
+                env, TAG_COLORMATRIX1);
+
+        if (!singleIlluminant) {
+            camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
+            BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
+            int32_t colorTransform2[entry2.count * 2];
+
+            ctr = 0;
+            for(size_t i = 0; i < entry2.count; ++i) {
+                colorTransform2[ctr++] = entry2.data.r[i].numerator;
+                colorTransform2[ctr++] = entry2.data.r[i].denominator;
+            }
+
+            BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
+                    env, TAG_COLORMATRIX2);
+        }
+    }
+
+    {
+        // Set calibration transforms
+        camera_metadata_entry entry1 =
+            characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
+        BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
+
+        int32_t calibrationTransform1[entry1.count * 2];
+
+        size_t ctr = 0;
+        for(size_t i = 0; i < entry1.count; ++i) {
+            calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
+            calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
+        }
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
+                TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
+
+        if (!singleIlluminant) {
+            camera_metadata_entry entry2 =
+                characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
+            BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
+            int32_t calibrationTransform2[entry2.count * 2];
+
+            ctr = 0;
+            for(size_t i = 0; i < entry2.count; ++i) {
+                calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
+                calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
+            }
+
+            BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
+                    TIFF_IFD_0),  env, TAG_CAMERACALIBRATION2);
+        }
+    }
+
+    {
+        // Set forward transforms
+        camera_metadata_entry entry1 =
+            characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
+        BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
+
+        int32_t forwardTransform1[entry1.count * 2];
+
+        size_t ctr = 0;
+        for(size_t i = 0; i < entry1.count; ++i) {
+            forwardTransform1[ctr++] = entry1.data.r[i].numerator;
+            forwardTransform1[ctr++] = entry1.data.r[i].denominator;
+        }
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
+                TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
+
+        if (!singleIlluminant) {
+            camera_metadata_entry entry2 =
+                characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
+            BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
+            int32_t forwardTransform2[entry2.count * 2];
+
+            ctr = 0;
+            for(size_t i = 0; i < entry2.count; ++i) {
+                forwardTransform2[ctr++] = entry2.data.r[i].numerator;
+                forwardTransform2[ctr++] = entry2.data.r[i].denominator;
+            }
+
+            BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
+                    TIFF_IFD_0),  env, TAG_FORWARDMATRIX2);
+        }
+    }
+
+    {
+        // Set camera neutral
+        camera_metadata_entry entry =
+            results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
+        BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
+        uint32_t cameraNeutral[entry.count * 2];
+
+        size_t ctr = 0;
+        for(size_t i = 0; i < entry.count; ++i) {
+            cameraNeutral[ctr++] =
+                    static_cast<uint32_t>(entry.data.r[i].numerator);
+            cameraNeutral[ctr++] =
+                    static_cast<uint32_t>(entry.data.r[i].denominator);
+        }
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
+                TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
+    }
+
+    {
+        // Setup data strips
+        // TODO: Switch to tiled implementation.
+        uint32_t offset = 0;
+        BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
+                TAG_STRIPOFFSETS);
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
+                TAG_ROWSPERSTRIP);
+
+        uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
+                bitsPerByte;
+        BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
+                TAG_STRIPBYTECOUNTS);
+    }
+
+    {
+        // Setup default crop + crop origin tags
+        uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
+        uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
+        if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
+            uint32_t defaultCropOrigin[] = {margin, margin};
+            uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
+            BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
+                    TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
+            BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
+                    TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
+        }
+    }
+
+    {
+        // Setup unique camera model tag
+        char model[PROPERTY_VALUE_MAX];
+        property_get("ro.product.model", model, "");
+
+        char manufacturer[PROPERTY_VALUE_MAX];
+        property_get("ro.product.manufacturer", manufacturer, "");
+
+        char brand[PROPERTY_VALUE_MAX];
+        property_get("ro.product.brand", brand, "");
+
+        String8 cameraModel(model);
+        cameraModel += "-";
+        cameraModel += manufacturer;
+        cameraModel += "-";
+        cameraModel += brand;
+
+        BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
+                reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
+                TAG_UNIQUECAMERAMODEL);
+    }
+
+    {
+        // Setup opcode List 2
+        camera_metadata_entry entry1 =
+                characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+        BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
+        uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
+        uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
+
+        camera_metadata_entry entry2 =
+                results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+        BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
+        if (entry2.count == lsmWidth * lsmHeight * 4) {
+
+            OpcodeListBuilder builder;
+            status_t err = builder.addGainMapsForMetadata(lsmWidth,
+                                                          lsmHeight,
+                                                          0,
+                                                          0,
+                                                          imageHeight,
+                                                          imageWidth,
+                                                          opcodeCfaLayout,
+                                                          entry2.data.f);
+            if (err == OK) {
+                size_t listSize = builder.getSize();
+                uint8_t opcodeListBuf[listSize];
+                err = builder.buildOpList(opcodeListBuf);
+                if (err == OK) {
+                    BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
+                            TIFF_IFD_0), env, TAG_OPCODELIST2);
+                } else {
+                    ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
+                    jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
+                }
+            } else {
+                ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
+                jniThrowRuntimeException(env, "failed to add lens shading map.");
+            }
+        } else {
+            ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
+        }
+    }
+
+    DngCreator_setCreator(env, thiz, writer);
+}
+
+static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
+    ALOGV("%s:", __FUNCTION__);
+    DngCreator_setCreator(env, thiz, NULL);
+}
+
+static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
+    ALOGV("%s:", __FUNCTION__);
+    jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
+    ALOGV("%s:", __FUNCTION__);
+    jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
+        jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
+        jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
+    ALOGV("%s:", __FUNCTION__);
+    jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
+}
+
+static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
+        jint height, jobject inBuffer, jint rowStride, jint pixStride) {
+    ALOGV("%s:", __FUNCTION__);
+
+    sp<JniOutputStream> out = new JniOutputStream(env, outStream);
+    if(env->ExceptionCheck()) {
+        ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
+        return;
+    }
+
+    uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
+    if (pixelBytes == NULL) {
+        ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
+        return;
+    }
+
+    TiffWriter* writer = DngCreator_getCreator(env, thiz);
+    if (writer == NULL) {
+        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+        jniThrowException(env, "java/lang/AssertionError",
+                "Write called with uninitialized DngCreator");
+        return;
+    }
+    // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
+    uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
+    uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
+    if (metadataWidth != width) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+                        "Metadata width %d doesn't match image width %d", metadataWidth, width);
+        return;
+    }
+
+    if (metadataHeight != height) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+                        "Metadata height %d doesn't match image height %d", metadataHeight, height);
+        return;
+    }
+
+    uint32_t stripOffset = writer->getTotalSize();
+
+    BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
+                    TAG_STRIPOFFSETS);
+
+    if (writer->write(out.get()) != OK) {
+        if (!env->ExceptionCheck()) {
+            jniThrowException(env, "java/io/IOException", "Failed to write metadata");
+        }
+        return;
+    }
+
+    size_t fullSize = rowStride * height;
+    jlong capacity = env->GetDirectBufferCapacity(inBuffer);
+    if (capacity < 0 || fullSize > capacity) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Invalid size %d for Image, size given in metadata is %d at current stride",
+                capacity, fullSize);
+        return;
+    }
+
+    if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
+        if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
+            if (!env->ExceptionCheck()) {
+                jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+            }
+            return;
+        }
+    } else if (pixStride == BYTES_PER_SAMPLE) {
+        for (size_t i = 0; i < height; ++i) {
+            if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
+                        env->ExceptionCheck()) {
+                if (!env->ExceptionCheck()) {
+                    jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+                }
+                return;
+            }
+        }
+    } else {
+        for (size_t i = 0; i < height; ++i) {
+            for (size_t j = 0; j < width; ++j) {
+                if (out->write(pixelBytes, i * rowStride + j * pixStride,
+                        BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
+                    if (env->ExceptionCheck()) {
+                        jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+}
+
+static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
+        jobject rawBuffer, jlong offset) {
+    ALOGV("%s:", __FUNCTION__);
+    jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
+}
+
+static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
+        jobject inStream, jlong offset) {
+    ALOGV("%s:", __FUNCTION__);
+    jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
+}
+
+} /*extern "C" */
+
+static JNINativeMethod gDngCreatorMethods[] = {
+    {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
+    {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
+            "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
+    {"nativeDestroy",           "()V",      (void*) DngCreator_destroy},
+    {"nativeSetOrientation",    "(I)V",     (void*) DngCreator_nativeSetOrientation},
+    {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
+            (void*) DngCreator_nativeSetThumbnailBitmap},
+    {"nativeSetThumbnailImage",
+            "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
+            (void*) DngCreator_nativeSetThumbnailImage},
+    {"nativeWriteImage",        "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
+            (void*) DngCreator_nativeWriteImage},
+    {"nativeWriteByteBuffer",    "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
+            (void*) DngCreator_nativeWriteByteBuffer},
+    {"nativeWriteInputStream",    "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
+            (void*) DngCreator_nativeWriteInputStream},
+};
+
+int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(env,
+                   "android/hardware/camera2/DngCreator", gDngCreatorMethods,
+                   NELEM(gDngCreatorMethods));
+}
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_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 48fb729..5bc0f62 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -299,6 +299,12 @@
     proxy->fence();
 }
 
+static void android_view_ThreadedRenderer_notifyFramePending(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->notifyFramePending();
+}
+
 #endif
 
 // ----------------------------------------------------------------------------
@@ -329,6 +335,7 @@
     { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
     { "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
+    { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
 #endif
 };
 
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 1880a15..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index aecb4d2..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index 8401f91..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index 5832865..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index 6d14962..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index aee057c..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index fb5801e..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index fdb5271..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index b8c7397..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index d0395a8..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index 59bb437..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index c053b90..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index eb30a79..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 1af0bff..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index 3b36e7d..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index c12d20a..0000000
--- a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 5bc1d90..0000000
--- a/core/res/res/drawable-hdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index e5de2c1..0000000
--- a/core/res/res/drawable-hdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..3cb4073
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..8fd1480
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..d35b579
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..543c6bc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..4fc3c40
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..c184535
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..9f9dd43
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..8c629ce
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..81134b5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..baa5860
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..d7e28366
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..6f24795
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..22f997d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..85f4471
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..ad483c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..f24c2fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..7a9e9bd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..af04902
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..32a6e94
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..c1b4b37
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..34d3ade
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..3d5db53
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..ea35437
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..48744f8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..f654517
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..16f959a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..98c754b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..5827dc2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..9850d74
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..03ab06b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..11cdd88
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..874edbf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
deleted file mode 100644
index 882365b..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
deleted file mode 100644
index f6c7094..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
deleted file mode 100644
index 0e326c9..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
deleted file mode 100644
index 8bf1170..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
deleted file mode 100644
index cedb66e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
deleted file mode 100644
index 257d7ba..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
deleted file mode 100644
index e07b36e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
deleted file mode 100644
index ef94200..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
deleted file mode 100644
index ad67004..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
deleted file mode 100644
index 50796e2..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
deleted file mode 100644
index ba7be9e..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
deleted file mode 100644
index bdbfe78..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
deleted file mode 100644
index fe89951..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
deleted file mode 100644
index 840c88f..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
deleted file mode 100644
index 621d1d2..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
deleted file mode 100644
index fd8be89..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index 7bef530..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index ae50dd5..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_qntm_alpha.png
deleted file mode 100644
index 0678dbb..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_on_qntm_alpha.png
deleted file mode 100644
index f332925..0000000
--- a/core/res/res/drawable-hdpi/btn_radio_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..da88e98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..907d92d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..9d24dc1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..8aa2605
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..b4cdf02
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..0724ed7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..c9bd4e3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..5630ec3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..4bf666c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..dffaa07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..5f86e18
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..9b50aef
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..1cf5e7f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..2bb641a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..08e7485
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..519b5a3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..0d3e1e7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..88c4a9e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..8fa2e88
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..53dd9d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..e235195
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..1721284
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..31819fa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..5de44b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..aa20f65
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..c379ba7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..e23b410
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..a9543dc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..2473b78
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..b4acc9c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..c9cf344
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..a8c390e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..e062f61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..7737646
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..65ff45e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..11aaec0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..9e1b60f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..1e45687
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..1e45687
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..6c48456
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..a4d084b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..1e1a1b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..cf09f97
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..3218e66
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..0acff03
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..c93adf4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..5d8ddc96
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..47206a4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..7d6a91f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..e062f61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..b0f0dde
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..c54f8d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..68e17ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..879d9c2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..281923e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..e91d4fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..15baded
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..3d5899f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..e2277bd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..b502e22
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..a70be2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..9442316
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..33db4a88
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..4e1cd16
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..d5254f4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..7aa0a3e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index 73e8f1c..0000000
--- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index ff6affe..0000000
--- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
index b11de9e..ac1fc23 100644
--- a/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
Binary files differ
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/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 0f44ff9..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index 9d5dda0..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index e4ce802..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index d1806ac..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index ab9315b..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index 46e90e6..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index e8c56ff..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index 59dcb7e..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index e9bd4a2..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index 1d05037..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index 91b40de..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index c531cab..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index 11bb387..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 8843210..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index 6ff2f3d..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index a03c1e2..0000000
--- a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 2ab6b7f..0000000
--- a/core/res/res/drawable-mdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index 2211d83..0000000
--- a/core/res/res/drawable-mdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..9759818
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..4eb2c4f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..e6d6b42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..03cb23a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..bfe3c3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..65bdf42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..44f9614b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..cf8ec38
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..4d624b3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..7c4eb7f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..e90dd31
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..831c0e8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..7355dfd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..be71a69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..a4a185b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..8d0386f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..70793c4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..632082b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..e7fc5fb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..91a0a33
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..3bd90d6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..5ac39ec
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..4181983
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..c8b04df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..b7b3a9f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..62bc4ed
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..ac463ad
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..12b605d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..63a3c6a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..17660c4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..7d9de3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..8aa1be2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
deleted file mode 100644
index 0a22e1a..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
deleted file mode 100644
index 2e2469c..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
deleted file mode 100644
index c1054d9..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
deleted file mode 100644
index cf8d80a..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
deleted file mode 100644
index 9d9e870..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
deleted file mode 100644
index 1bad701..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
deleted file mode 100644
index a84a54f..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
deleted file mode 100644
index 4d8050b..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
deleted file mode 100644
index 374172c..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
deleted file mode 100644
index 233036e..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
deleted file mode 100644
index 61d9b58..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
deleted file mode 100644
index 274e983..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
deleted file mode 100644
index acf16e5..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
deleted file mode 100644
index ee48241..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
deleted file mode 100644
index dbbb736..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
deleted file mode 100644
index bcabd0d..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index 713fee8..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index dcb90d0..0000000
--- a/core/res/res/drawable-mdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..a2b7fce
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..fe0d3b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..d66d00d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..2f2f5cd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..72c9495
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..7d9090f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..c5442e8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..ca80cdb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..d41a10b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..262c838
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..7f6ea8ba
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..8d50a81
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..0725a68
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..6191a4b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..1904d74
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..bec8dda
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..54ef480
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..55c5163
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..0fe2a897
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..86efab7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..c0a5ca5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..ec55175
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..3e4a690
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..da49734
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..471cda1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..d560262
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..f6096b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..9e2500b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..efbac99
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..676f0ca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..4803157
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..4f8a162
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..3617168
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..e4366f4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..ea4533b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..94aedbb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..ef84578
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..4de2321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..4de2321
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..d62fbd5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..3d87c4e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..536ed46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..f5b660d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..9e4db6c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..7de2128
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..1980c2c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..6e73ef0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..f897392
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..74a6ebd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..3617168
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..884eb66
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..8c3f26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..e5bface
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..dca86ea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..377a6b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..0859f62
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..bf5cdcd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..4cd177d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..dfe39ca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..5d3ab99
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..80922eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..aa77044
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..7b099db
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..088c86a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..3c6b9bc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..adf5100
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index 8949b52..0000000
--- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index d727683..0000000
--- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png
index 8991421..b6538e4 100644
--- a/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.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/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 25500e8..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index b136e25..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index 6a94e30..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index d386421..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index c811385..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index 58b3267..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index 0659e72..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index b4227d1..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index 714ef00..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index 139595b..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index 4491107..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index 20eb752..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index 532c9f2..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 0d78a32..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index af29678..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index 23eb9e3..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 5d820ae..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index 019c92e..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..2347643
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..70aaa01
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..01e498a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..71d1cf7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..d1e7b1d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..7db7d06
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..dadb62e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..f87f744
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..be99d87
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..f83bc05
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..870071d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..3a18414
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..f3d1187
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..4078cca
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..d4849b5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..6e2af72
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..9244174
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..8c7fe95
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..71eb1d0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..613f38a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..2d20ccc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..407f78d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..1bf24b0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..a450bd0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..63ba593
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..6d05e5a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..1c8cd8f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..b8bc564
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..3d80128
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..c21dfba
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..2dfe90d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..5f40d73
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
deleted file mode 100644
index cd11e14..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
deleted file mode 100644
index b10db83..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
deleted file mode 100644
index efeb6fb..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
deleted file mode 100644
index 83080af..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
deleted file mode 100644
index b9cc322..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
deleted file mode 100644
index 3b5f9c4..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
deleted file mode 100644
index 58c93db..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
deleted file mode 100644
index 0f1d010..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
deleted file mode 100644
index 05a7a0f..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
deleted file mode 100644
index 9345035..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
deleted file mode 100644
index 5f149b7..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
deleted file mode 100644
index 191f369..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
deleted file mode 100644
index 44e08e6..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
deleted file mode 100644
index 5a9dfa0..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
deleted file mode 100644
index ee921c6..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
deleted file mode 100644
index 567bb0c..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index 2fd964e..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index 8873cd6..0000000
--- a/core/res/res/drawable-xhdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..b54c6ff
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..fff7056
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..026462d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..26cc8de
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..c055fff
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..a22e780
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..357374c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..71d4667
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..2ed175e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..e0f7d8e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..62b0578
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..4d6ef4a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..37cee2d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..a8bc25f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..cf68d93
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..96834bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..d068dbe
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..4aabb1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..bbac8e4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..2fc7459
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..83c6d0e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..45c08d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..05b7dfb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..baf9964
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..d6e0369
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..3f35270
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..a5b34dc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..361967b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..c478bb7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..075fa0c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..d9e364b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..9924496
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..7bd99fe
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..2ef623b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..19db3e0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..984c3c5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..6454190
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..cee9393
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..cee9393
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..437ffdd
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..d2e14ad
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..4e2f5bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..f1bcfa3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..ede2fec
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..94ce017
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..647cfe3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..b3bf923
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..ae95b2b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..b8e4bd6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..ec6d6d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..c0e493f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..2494fd4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..dff391f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..b135338
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..ea09a31
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..f9a4391
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..d9606e1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..df2d9d0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..625a322
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..79e8dde
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..e99c266
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..f0329a4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..42c40b7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..807491f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..dfec9cc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..0ed59ea
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index a7a972c..0000000
--- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index dd8910b..0000000
--- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png
index 4970f56..d6a0ab2 100644
--- a/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.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/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
deleted file mode 100644
index 1881f54..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
deleted file mode 100644
index 6f8ec2d..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
deleted file mode 100644
index c954ed9..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
deleted file mode 100644
index 9d1a47e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
deleted file mode 100644
index ce63631..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
deleted file mode 100644
index 430c134..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
deleted file mode 100644
index cdebf83..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
deleted file mode 100644
index 40ceadb..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
deleted file mode 100644
index fb13eb2..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
deleted file mode 100644
index d716fba..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
deleted file mode 100644
index b8be041..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
deleted file mode 100644
index bad0c3c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
deleted file mode 100644
index a6368fb..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
deleted file mode 100644
index 234e5d1..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
deleted file mode 100644
index 3e7796d..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
deleted file mode 100644
index 0673999..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_off_qntm_alpha.png
deleted file mode 100644
index 2a17861..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_on_qntm_alpha.png
deleted file mode 100644
index 61067ac..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..b754381
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..517d7a7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..2c1d5b6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..0c6ff7e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..0796601
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..9b4e0f8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..25767eb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..cd0951f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..9ae8165
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..efd9bc6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..fccbc9d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..dddafca
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..7e37433
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..9bc22de
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..507ed10
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..6a21c7f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..0d544d9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..39da0ac
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..d5ada12
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..d4e096c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..468a9b4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..ea3cd2e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..0652cb0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..768d2b0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..1d06a90
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..8a70a80
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..bf9ec7f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..cff07b9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..40f997e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..6ba84ec
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..766610e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..810a029
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_off_pressed_qntm_alpha.png
deleted file mode 100644
index fdbbbce..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_off_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_off_qntm_alpha.png
deleted file mode 100644
index 0ec2ee6..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_off_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_on_pressed_qntm_alpha.png
deleted file mode 100644
index b46ee1c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_on_qntm_alpha.png
deleted file mode 100644
index 8737156..0000000
--- a/core/res/res/drawable-xxhdpi/btn_radio_on_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..cbc3833
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..4243895
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..b522d37
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..647b965
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..a317497
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..0e4b25f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..6e279d9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..f0840cc
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..140e9e8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..5cf8ec5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..f9624d8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..899df8c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..6543e1c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..cd758dd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..72d950c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..07bdbc9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..c9af24b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..01de3f5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..f428bc5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..ab5c008
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..5b157cf
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..1210be2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..e6b4140
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..b678e08
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..6ca2a69
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..7de608e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..b2bbcce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..6950db3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..c790756
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..ed5d888
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..81a4a63
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..db1d93a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..eff3dd0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..000a23a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..394d661
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..4e7311d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..d9dcf91
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..674142e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..674142e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..9d4026a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..bb4b426
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..a37076d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..22e9951
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..14e6b39
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..86b2c01
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..1c565df
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..b825449
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..170c234
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..5477007
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..eff3dd0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..e3fd96a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..198ac07
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100644
index 0000000..0d6a39a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png
new file mode 100644
index 0000000..f235aed
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..46aa533
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..a749469
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..ef43f00
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..eebddc3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..44b654d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..6e768c1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..2ac6dae
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..91c49ce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..4b4bd1f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..637e596
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..42d4d2a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..995d1b2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
deleted file mode 100644
index 8d79a13..0000000
--- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
deleted file mode 100644
index e0e4ef9..0000000
--- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png
index 74a259b..a8067cb 100644
--- a/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.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-xxxhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png
new file mode 100644
index 0000000..f0ff1a7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png
new file mode 100644
index 0000000..b382df3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png
new file mode 100644
index 0000000..8cb4ce2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png
new file mode 100644
index 0000000..4db2b01
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png
new file mode 100644
index 0000000..8c4709b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png
new file mode 100644
index 0000000..1ad960a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png
new file mode 100644
index 0000000..e47cc20
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png
new file mode 100644
index 0000000..c4d0d51
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png
new file mode 100644
index 0000000..915d56a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png
new file mode 100644
index 0000000..85795cb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png
new file mode 100644
index 0000000..157fd91
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png
new file mode 100644
index 0000000..9d446de
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png
new file mode 100644
index 0000000..dfac1f0
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png
new file mode 100644
index 0000000..aed6c08
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png
new file mode 100644
index 0000000..1b8bd6b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png
new file mode 100644
index 0000000..5dd0e5b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png
new file mode 100644
index 0000000..5dd0e5b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png
new file mode 100644
index 0000000..1a31ad9
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png
new file mode 100644
index 0000000..63c7f12
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png
new file mode 100644
index 0000000..847dd08
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png
new file mode 100644
index 0000000..b93f3cc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png
new file mode 100644
index 0000000..1e3dea7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png
new file mode 100644
index 0000000..5a85238
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png
new file mode 100644
index 0000000..35960ca
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png
new file mode 100644
index 0000000..6db5555
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png
new file mode 100644
index 0000000..a9c5851
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png
new file mode 100644
index 0000000..38465bd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png
new file mode 100644
index 0000000..15942dc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png
new file mode 100644
index 0000000..67d0d64
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png
new file mode 100644
index 0000000..69b5c1b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png
new file mode 100644
index 0000000..0e5d331
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png
new file mode 100644
index 0000000..f0ff1a7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png
new file mode 100644
index 0000000..01eeefe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png
new file mode 100644
index 0000000..44028af
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png
new file mode 100644
index 0000000..ec13a86
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png
new file mode 100644
index 0000000..43754eb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png
new file mode 100644
index 0000000..39d1d64
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png
new file mode 100644
index 0000000..f36f883
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png
new file mode 100644
index 0000000..7a4cc5c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png
new file mode 100644
index 0000000..80a21ec
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png
new file mode 100644
index 0000000..2141104
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png
new file mode 100644
index 0000000..203bd51
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png
new file mode 100644
index 0000000..5df6fc5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png
new file mode 100644
index 0000000..6d0fced
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png
new file mode 100644
index 0000000..8c0c372
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png
new file mode 100644
index 0000000..4fa6f53
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png
new file mode 100644
index 0000000..d3dbf7d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png
new file mode 100644
index 0000000..4ccf8de
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png
new file mode 100644
index 0000000..adef871
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png
new file mode 100644
index 0000000..adef871
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png
new file mode 100644
index 0000000..9fc3556
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png
new file mode 100644
index 0000000..7f00609
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png
new file mode 100644
index 0000000..e4aa58d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png
new file mode 100644
index 0000000..fe4e4b7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png
new file mode 100644
index 0000000..86666ca
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png
new file mode 100644
index 0000000..608faaf
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png
new file mode 100644
index 0000000..ec95422
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png
new file mode 100644
index 0000000..76e2754
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png
new file mode 100644
index 0000000..3853eac
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png
new file mode 100644
index 0000000..621aff1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png
new file mode 100644
index 0000000..d24be2a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png
new file mode 100644
index 0000000..df33892
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png
new file mode 100644
index 0000000..ff4b818
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png
new file mode 100644
index 0000000..d9793ae
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png
new file mode 100644
index 0000000..44028af
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png
new file mode 100644
index 0000000..8b202c6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png
new file mode 100644
index 0000000..3b497f3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png
new file mode 100644
index 0000000..532b6de
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png
new file mode 100644
index 0000000..403b2fe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png
new file mode 100644
index 0000000..8c5086c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png
new file mode 100644
index 0000000..d4870f8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png
new file mode 100644
index 0000000..c05adf5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png
new file mode 100644
index 0000000..99b2056
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png
new file mode 100644
index 0000000..d839358
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png
new file mode 100644
index 0000000..913f94d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png
new file mode 100644
index 0000000..7f325b3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png
new file mode 100644
index 0000000..149a9aa
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png
new file mode 100644
index 0000000..95c219e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png
new file mode 100644
index 0000000..462a128
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png
new file mode 100644
index 0000000..5911d16
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png
new file mode 100644
index 0000000..e0c6d85
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png
new file mode 100644
index 0000000..5679943
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png
new file mode 100644
index 0000000..54b636d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png
new file mode 100644
index 0000000..bf9fac0
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png
new file mode 100644
index 0000000..25d5319
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png
new file mode 100644
index 0000000..d2df595
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png
new file mode 100644
index 0000000..7700bde
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png
new file mode 100644
index 0000000..883f98b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png
new file mode 100644
index 0000000..b3b2108
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png
new file mode 100644
index 0000000..3aad596
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png
new file mode 100644
index 0000000..2017e17
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png
new file mode 100644
index 0000000..1fc2700
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png
new file mode 100644
index 0000000..bb8b0f2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png
new file mode 100644
index 0000000..66ab8f6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png
new file mode 100644
index 0000000..e3424db
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png
new file mode 100755
index 0000000..c06740b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png
new file mode 100755
index 0000000..7556167
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png
new file mode 100644
index 0000000..0c8f746
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png
new file mode 100644
index 0000000..5db9deb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png
new file mode 100644
index 0000000..3aca6d3
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png
new file mode 100644
index 0000000..746c74f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png
new file mode 100644
index 0000000..454a5b2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png
new file mode 100644
index 0000000..80ad8cc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png
new file mode 100644
index 0000000..cfd0db4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png
new file mode 100644
index 0000000..845092f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png
new file mode 100644
index 0000000..0042db4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png
new file mode 100644
index 0000000..77b2901
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png
new file mode 100644
index 0000000..fb3c238
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png
new file mode 100644
index 0000000..0d28c45
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png
new file mode 100644
index 0000000..fb07f2a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
index a8def44..2cd7ed6 100644
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ b/core/res/res/drawable/btn_borderless_quantum.xml
@@ -16,21 +16,6 @@
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
     android:tint="?attr/colorControlHighlight">
-    <item android:id="@id/mask">
-        <inset
-            android:insetLeft="4dp"
-            android:insetTop="4dp"
-            android:insetBottom="4dp"
-            android:insetRight="4dp">
-            <shape android:shape="rectangle">
-                <solid android:color="@color/white" />
-                <corners android:radius="2dp" />
-                <padding
-                    android:left="4dp"
-                    android:top="4dp"
-                    android:bottom="4dp"
-                    android:right="4dp" />
-            </shape>
-        </inset>
-    </item>
+    <item android:id="@id/mask"
+        android:drawable="@drawable/btn_qntm_alpha" />
 </ripple>
diff --git a/core/res/res/drawable/btn_check_quantum.xml b/core/res/res/drawable/btn_check_quantum.xml
deleted file mode 100644
index 6ceba60..0000000
--- a/core/res/res/drawable/btn_check_quantum.xml
+++ /dev/null
@@ -1,34 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:state_pressed="true">
-        <bitmap android:src="@drawable/btn_check_on_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item android:state_checked="true">
-        <bitmap android:src="@drawable/btn_check_on_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item android:state_pressed="true">
-        <bitmap android:src="@drawable/btn_check_off_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item>
-        <bitmap android:src="@drawable/btn_check_off_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-</selector>
diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml
index 96715a4..b16875e 100644
--- a/core/res/res/drawable/btn_check_quantum_anim.xml
+++ b/core/res/res/drawable/btn_check_quantum_anim.xml
@@ -16,88 +16,118 @@
 
 <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false" android:state_checked="true">
-        <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
-            android:tint="?attr/colorControlActivated"
-            android:alpha="?attr/disabledAlpha" />
+        <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_enabled="false">
-        <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
-            android:tint="?attr/colorControlNormal"
-            android:alpha="?attr/disabledAlpha" />
+        <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_checked="true" android:id="@+id/on">
-        <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
+        <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
     </item>
     <item android:id="@+id/off">
-        <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
+        <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlNormal" />
     </item>
-    <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+    <transition android:fromId="@+id/off" android:toId="@+id/on">
         <animation-list>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00001_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_001" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00002_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_002" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00003_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_003" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00004_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_004" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00005_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_005" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00006_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_006" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00007_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_007" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00008_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_008" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00009_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_009" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00010_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_010" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00011_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_011" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00012_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_012" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00013_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_013" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00014_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_014" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+    <transition android:fromId="@+id/on" android:toId="@+id/off">
+        <animation-list>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_000" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_001" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_002" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_003" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_004" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_005" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_006" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_007" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_008" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_009" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_010" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_011" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_012" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_013" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_014" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_check_to_off_qntm_015" android:tint="?attr/colorControlActivated" />
             </item>
         </animation-list>
     </transition>
diff --git a/core/res/res/drawable/btn_default_quantum.xml b/core/res/res/drawable/btn_default_quantum.xml
index 63473a4..61193fe 100644
--- a/core/res/res/drawable/btn_default_quantum.xml
+++ b/core/res/res/drawable/btn_default_quantum.xml
@@ -17,20 +17,7 @@
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
     android:tint="?attr/colorControlHighlight">
     <item>
-        <inset
-            android:insetLeft="4dp"
-            android:insetTop="4dp"
-            android:insetBottom="4dp"
-            android:insetRight="4dp">
-            <shape android:shape="rectangle">
-                <solid android:color="?attr/colorButtonNormal" />
-                <corners android:radius="2dp" />
-                <padding
-                    android:left="4dp"
-                    android:top="4dp"
-                    android:bottom="4dp"
-                    android:right="4dp" />
-            </shape>
-        </inset>
+        <nine-patch android:src="@drawable/btn_qntm_alpha"
+            android:tint="?attr/colorButtonNormal" />
     </item>
 </ripple>
diff --git a/core/res/res/drawable/btn_radio_quantum.xml b/core/res/res/drawable/btn_radio_quantum.xml
deleted file mode 100644
index 0f9ebce..0000000
--- a/core/res/res/drawable/btn_radio_quantum.xml
+++ /dev/null
@@ -1,36 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:state_checked="true">
-        <bitmap android:src="@drawable/btn_radio_on_qntm_alpha"
-            android:tint="?attr/colorControlNormal"
-            android:alpha="?attr/disabledAlpha" />
-    </item>
-    <item android:state_enabled="false">
-        <bitmap android:src="@drawable/btn_radio_off_qntm_alpha"
-            android:tint="?attr/colorControlNormal"
-            android:alpha="?attr/disabledAlpha" />
-    </item>
-    <item android:state_checked="true">
-        <bitmap android:src="@drawable/btn_radio_on_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item>
-        <bitmap android:src="@drawable/btn_radio_off_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-</selector>
diff --git a/core/res/res/drawable/btn_radio_quantum_anim.xml b/core/res/res/drawable/btn_radio_quantum_anim.xml
index 5068b7a..cd9b518 100644
--- a/core/res/res/drawable/btn_radio_quantum_anim.xml
+++ b/core/res/res/drawable/btn_radio_quantum_anim.xml
@@ -16,88 +16,118 @@
 
 <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false" android:state_checked="true">
-        <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
-            android:tint="?attr/colorControlActivated"
-            android:alpha="?attr/disabledAlpha" />
+        <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_enabled="false">
-        <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
-            android:tint="?attr/colorControlNormal"
-            android:alpha="?attr/disabledAlpha" />
+        <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:state_checked="true" android:id="@+id/on">
-        <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
+        <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
     </item>
     <item android:id="@+id/off">
-        <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
+        <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlNormal" />
     </item>
-    <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+    <transition android:fromId="@+id/off" android:toId="@+id/on">
         <animation-list>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00001_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_001" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00002_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_002" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00003_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_003" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00004_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_004" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00005_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_005" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00006_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_006" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00007_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_007" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00008_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_008" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00009_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_009" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00010_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_010" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00011_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_011" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00012_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_012" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00013_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_013" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00014_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_014" android:tint="?attr/colorControlActivated" />
             </item>
-            <item android:duration="33">
-                <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+    <transition android:fromId="@+id/on" android:toId="@+id/off">
+        <animation-list>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_000" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_001" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_002" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_003" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_004" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_005" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_006" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_007" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_008" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_009" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_010" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_011" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_012" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_013" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_014" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_radio_to_off_qntm_015" android:tint="?attr/colorControlActivated" />
             </item>
         </animation-list>
     </transition>
diff --git a/core/res/res/drawable/btn_toggle_quantum.xml b/core/res/res/drawable/btn_toggle_quantum.xml
new file mode 100644
index 0000000..e235598
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_quantum.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="4dp"
+    android:insetTop="4dp"
+    android:insetBottom="4dp"
+    android:insetRight="4dp">
+    <layer-list android:paddingMode="stack">
+        <item>
+            <ripple android:tint="?attr/colorControlHighlight">
+                <item>
+                    <nine-patch android:src="@drawable/btn_toggle_qntm_alpha"
+                        android:tint="?attr/colorButtonNormal" />
+                </item>
+            </ripple>
+        </item>
+        <item>
+            <selector xmlns:android="http://schemas.android.com/apk/res/android">
+                <item android:state_checked="false">
+                    <nine-patch android:src="@drawable/btn_toggle_indicator_qntm_alpha"
+                        android:tint="?attr/colorControlNormal" />
+                </item>
+                <item android:state_checked="true">
+                    <nine-patch android:src="@drawable/btn_toggle_indicator_qntm_alpha"
+                        android:tint="?attr/colorControlActivated" />
+                </item>
+            </selector>
+        </item>
+    </layer-list>
+</inset>
diff --git a/core/res/res/drawable/scrubber_control_quantum_anim.xml b/core/res/res/drawable/scrubber_control_quantum_anim.xml
new file mode 100644
index 0000000..87d3ae9
--- /dev/null
+++ b/core/res/res/drawable/scrubber_control_quantum_anim.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
+    <item android:state_enabled="false" android:state_pressed="true">
+        <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_pressed="true" android:id="@+id/pressed">
+        <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+    </item>
+    <item android:id="@+id/not_pressed">
+        <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+    </item>
+    <transition android:fromId="@+id/not_pressed" android:toId="@+id/pressed">
+        <animation-list>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+    <transition android:fromId="@+id/pressed" android:toId="@+id/not_pressed">
+        <animation-list>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/switch_inner_quantum.xml b/core/res/res/drawable/switch_inner_quantum.xml
deleted file mode 100644
index 856895e..0000000
--- a/core/res/res/drawable/switch_inner_quantum.xml
+++ /dev/null
@@ -1,36 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:state_checked="true">
-        <nine-patch android:src="@drawable/switch_on_qntm_alpha"
-            android:tint="?attr/colorControlNormal"
-            android:alpha="?attr/disabledAlpha" />
-    </item>
-    <item android:state_enabled="false">
-        <nine-patch android:src="@drawable/switch_off_qntm_alpha"
-            android:tint="?attr/colorControlNormal"
-            android:alpha="?attr/disabledAlpha" />
-    </item>
-    <item android:state_checked="true">
-        <nine-patch android:src="@drawable/switch_on_qntm_alpha"
-            android:tint="?attr/colorControlActivated" />
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/switch_off_qntm_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-</selector>
diff --git a/core/res/res/drawable/switch_thumb_quantum_anim.xml b/core/res/res/drawable/switch_thumb_quantum_anim.xml
new file mode 100644
index 0000000..1984d47
--- /dev/null
+++ b/core/res/res/drawable/switch_thumb_quantum_anim.xml
@@ -0,0 +1,128 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
+    <item android:state_enabled="false" android:state_checked="true">
+        <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_checked="true" android:id="@+id/on">
+        <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+    </item>
+    <item android:id="@+id/off">
+        <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
+    </item>
+    <transition android:fromId="@+id/off" android:toId="@+id/on">
+        <animation-list>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+    <transition android:fromId="@+id/on" android:toId="@+id/off">
+        <animation-list>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="15">
+                <bitmap android:src="@drawable/btn_switch_to_off_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/switch_track_quantum.xml b/core/res/res/drawable/switch_track_quantum.xml
index 8c4e6b71..3651a0a 100644
--- a/core/res/res/drawable/switch_track_quantum.xml
+++ b/core/res/res/drawable/switch_track_quantum.xml
@@ -15,6 +15,16 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:state_checked="true">
+        <nine-patch android:src="@drawable/switch_track_qntm_alpha"
+            android:tint="?attr/colorControlActivated"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <nine-patch android:src="@drawable/switch_track_qntm_alpha"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
     <item android:state_checked="true">
         <nine-patch android:src="@drawable/switch_track_qntm_alpha"
             android:tint="?attr/colorControlActivated" />
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/styles.xml b/core/res/res/values/styles.xml
index 4a27ebe..f6cd9e8 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -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>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index e528278..2e7a5b1 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"/>
@@ -390,7 +431,7 @@
     </style>
 
     <style name="Widget.Quantum.Button.Toggle">
-        <item name="background">@drawable/btn_toggle_holo_dark</item>
+        <item name="background">@drawable/btn_toggle_quantum</item>
         <item name="textOn">@string/capital_on</item>
         <item name="textOff">@string/capital_off</item>
         <item name="textAppearance">?attr/textAppearanceSmall</item>
@@ -459,13 +500,13 @@
 
     <style name="Widget.Quantum.CompoundButton.Switch">
         <item name="track">@drawable/switch_track_quantum</item>
-        <item name="thumb">@drawable/switch_inner_quantum</item>
+        <item name="thumb">@drawable/switch_thumb_quantum_anim</item>
         <item name="splitTrack">true</item>
         <item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item>
         <item name="textOn"></item>
         <item name="textOff"></item>
-        <item name="switchMinWidth">72dip</item>
-        <item name="switchPadding">16dip</item>
+        <item name="switchMinWidth">4dip</item>
+        <item name="switchPadding">4dip</item>
         <item name="background">?attr/selectableItemBackground</item>
     </style>
 
@@ -579,7 +620,7 @@
         <item name="indeterminateOnly">false</item>
         <item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
         <item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
-        <item name="thumb">@drawable/scrubber_control_selector_quantum</item>
+        <item name="thumb">@drawable/scrubber_control_quantum_anim</item>
         <item name="splitTrack">true</item>
         <item name="focusable">true</item>
         <item name="paddingStart">16dip</item>
@@ -784,15 +825,7 @@
     <style name="Widget.Quantum.Light.Button.Borderless" parent="Widget.Quantum.Button.Borderless"/>
     <style name="Widget.Quantum.Light.Button.Borderless.Small" parent="Widget.Quantum.Button.Borderless.Small"/>
     <style name="Widget.Quantum.Light.Button.Inset" parent="Widget.Quantum.Button.Inset"/>
-
-    <style name="Widget.Quantum.Light.Button.Toggle">
-        <item name="background">@drawable/btn_toggle_holo_light</item>
-        <item name="textOn">@string/capital_on</item>
-        <item name="textOff">@string/capital_off</item>
-        <item name="textAppearance">?attr/textAppearanceSmall</item>
-        <item name="minHeight">48dip</item>
-    </style>
-
+    <style name="Widget.Quantum.Light.Button.Toggle" parent="Widget.Quantum.Button.Toggle" />
     <style name="Widget.Quantum.Light.ButtonBar" parent="Widget.Quantum.ButtonBar"/>
     <style name="Widget.Quantum.Light.ButtonBar.AlertDialog" parent="Widget.Quantum.ButtonBar.AlertDialog"/>
 
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/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 0eae67c..90cb217 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -66,8 +66,6 @@
         mPositions = positions;
         mTileMode = tile;
         native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt);
-        native_shader = nativePostCreate1(native_instance, x0, y0, x1, y1, colors, positions,
-                tile.nativeInt);
     }
 
     /** Create a shader that draws a linear gradient along a line.
@@ -90,8 +88,6 @@
         mColor1 = color1;
         mTileMode = tile;
         native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
-        native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1,
-                tile.nativeInt);
     }
 
     /**
@@ -120,8 +116,4 @@
             int colors[], float positions[], int tileMode);
     private native long nativeCreate2(float x0, float y0, float x1, float y1,
             int color0, int color1, int tileMode);
-    private native long nativePostCreate1(long native_shader, float x0, float y0, float x1, float y1,
-            int colors[], float positions[], int tileMode);
-    private native long nativePostCreate2(long native_shader, float x0, float y0, float x1, float y1,
-            int color0, int color1, int tileMode);
 }
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index c00c612..75c951a 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -66,8 +66,6 @@
         mPositions = positions;
         mTileMode = tile;
         native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
-        native_shader = nativePostCreate1(native_instance, x, y, radius, colors, positions,
-                tile.nativeInt);
     }
 
     /** Create a shader that draws a radial gradient given the center and radius.
@@ -91,8 +89,6 @@
         mColor1 = color1;
         mTileMode = tile;
         native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
-        native_shader = nativePostCreate2(native_instance, x, y, radius, color0, color1,
-                tile.nativeInt);
     }
 
     /**
@@ -121,10 +117,5 @@
             int colors[], float positions[], int tileMode);
     private static native long nativeCreate2(float x, float y, float radius,
             int color0, int color1, int tileMode);
-
-    private static native long nativePostCreate1(long native_shader, float x, float y, float radius,
-            int colors[], float positions[], int tileMode);
-    private static native long nativePostCreate2(long native_shader, float x, float y, float radius,
-            int color0, int color1, int tileMode);
 }
 
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 94b4c4a..6870ab4 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -29,10 +29,6 @@
      * @hide 
      */
     public long native_instance;
-    /**
-     * @hide
-     */
-    public long native_shader;
 
     private Matrix mLocalMatrix;
 
@@ -78,7 +74,7 @@
      */
     public void setLocalMatrix(Matrix localM) {
         mLocalMatrix = localM;
-        nativeSetLocalMatrix(native_instance, native_shader,
+        nativeSetLocalMatrix(native_instance,
                 localM == null ? 0 : localM.native_instance);
     }
 
@@ -86,7 +82,7 @@
         try {
             super.finalize();
         } finally {
-            nativeDestructor(native_instance, native_shader);
+            nativeDestructor(native_instance);
         }
     }
 
@@ -112,7 +108,7 @@
         }
     }
 
-    private static native void nativeDestructor(long native_shader, long native_skiaShader);
+    private static native void nativeDestructor(long native_shader);
     private static native void nativeSetLocalMatrix(long native_shader,
-            long native_skiaShader, long matrix_instance);
+            long matrix_instance);
 }
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 21239f7..18a748f 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -63,7 +63,6 @@
         mColors = colors;
         mPositions = positions;
         native_instance = nativeCreate1(cx, cy, colors, positions);
-        native_shader = nativePostCreate1(native_instance, cx, cy, colors, positions);
     }
 
     /**
@@ -81,7 +80,6 @@
         mColor0 = color0;
         mColor1 = color1;
         native_instance = nativeCreate2(cx, cy, color0, color1);
-        native_shader = nativePostCreate2(native_instance, cx, cy, color0, color1);
     }
 
     /**
@@ -108,10 +106,5 @@
 
     private static native long nativeCreate1(float x, float y, int colors[], float positions[]);
     private static native long nativeCreate2(float x, float y, int color0, int color1);
-
-    private static native long nativePostCreate1(long native_shader, float cx, float cy,
-            int[] colors, float[] positions);    
-    private static native long nativePostCreate2(long native_shader, float cx, float cy,
-            int color0, int color1);
 }
 
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 46e3401..42872e9 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -80,6 +80,22 @@
         this(null, null);
     }
 
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        final boolean changed = super.setVisible(visible, restart);
+        if (mAnim != null) {
+            if (visible) {
+                if (changed || restart) {
+                    // TODO: Should this support restart?
+                    mAnim.end();
+                }
+            } else {
+                mAnim.end();
+            }
+        }
+        return changed;
+    }
+
     /**
      * Add a new drawable to the set of keyframes.
      *
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index f3fcf2c..c95ac82 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -25,6 +25,7 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -92,6 +93,9 @@
     private int mBitmapWidth;
     private int mBitmapHeight;
 
+    /** Optical insets due to gravity. */
+    private Insets mOpticalInsets = null;
+
     // Mirroring matrix for using with Shaders
     private Matrix mMirrorMatrix;
 
@@ -456,9 +460,9 @@
 
     @Override
     protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
         mApplyGravity = true;
-        Shader shader = mBitmapState.mPaint.getShader();
+
+        final Shader shader = mBitmapState.mPaint.getShader();
         if (shader != null) {
             if (needMirroring()) {
                 updateMirrorMatrix(bounds.right - bounds.left);
@@ -517,9 +521,7 @@
         final boolean needMirroring = needMirroring();
         if (shader == null) {
             if (mApplyGravity) {
-                final int layoutDirection = getLayoutDirection();
-                Gravity.apply(state.mGravity, mBitmapWidth, mBitmapHeight,
-                        getBounds(), mDstRect, layoutDirection);
+                applyGravity();
                 mApplyGravity = false;
             }
 
@@ -564,6 +566,31 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public Insets getOpticalInsets() {
+        if (mApplyGravity && mBitmapState.mPaint.getShader() == null) {
+            applyGravity();
+            mApplyGravity = false;
+        }
+        return mOpticalInsets == null ? Insets.NONE : mOpticalInsets;
+    }
+
+    private void applyGravity() {
+        final Rect bounds = getBounds();
+        final int layoutDirection = getLayoutDirection();
+        Gravity.apply(mBitmapState.mGravity, mBitmapWidth, mBitmapHeight,
+                bounds, mDstRect, layoutDirection);
+
+        final int left = mDstRect.left - bounds.left;
+        final int top = mDstRect.top - bounds.top;
+        final int right = bounds.right - mDstRect.right;
+        final int bottom = bounds.bottom - mDstRect.bottom;
+        mOpticalInsets = Insets.of(left, top, right, bottom);
+    }
+
     @Override
     public void setAlpha(int alpha) {
         final int oldAlpha = mBitmapState.mPaint.getAlpha();
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 04373d4..2aef39f 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -187,6 +187,9 @@
         }
         if (mCurrDrawable != null) {
             mCurrDrawable.setBounds(bounds);
+
+            // Must obtain optical insets after setting bounds.
+            mInsets = mCurrDrawable.getOpticalInsets();
         }
     }
 
@@ -385,7 +388,6 @@
             mCurrDrawable = d;
             mCurIndex = idx;
             if (d != null) {
-                mInsets = d.getOpticalInsets();
                 d.mutate();
                 if (mDrawableContainerState.mEnterFadeDuration > 0) {
                     mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
@@ -402,6 +404,9 @@
                 d.setBounds(getBounds());
                 d.setLayoutDirection(getLayoutDirection());
                 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+
+                // Must obtain optical insets after setting bounds.
+                mInsets = d.getOpticalInsets();
             } else {
                 mInsets = Insets.NONE;
             }
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e3ed75e..e2bd50d 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -159,7 +159,8 @@
 
     @Override
     public void setColorFilter(ColorFilter colorFilter) {
-        // TODO: support color filter
+        mVectorState.mVPathRenderer.setColorFilter(colorFilter);
+        invalidateSelf();
     }
 
     @Override
@@ -365,14 +366,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 +415,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 +484,7 @@
             if (vPath.mFillColor != 0) {
                 if (mFillPaint == null) {
                     mFillPaint = new Paint();
+                    mFillPaint.setColorFilter(mColorFilter);
                     mFillPaint.setStyle(Paint.Style.FILL);
                     mFillPaint.setAntiAlias(true);
                 }
@@ -481,6 +496,7 @@
             if (vPath.mStrokeColor != 0) {
                 if (mStrokePaint == null) {
                     mStrokePaint = new Paint();
+                    mStrokePaint.setColorFilter(mColorFilter);
                     mStrokePaint.setStyle(Paint.Style.STROKE);
                     mStrokePaint.setAntiAlias(true);
                 }
@@ -516,24 +532,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/Caches.h b/libs/hwui/Caches.h
index 2e2ee15..5367663 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -33,6 +33,7 @@
 #include "thread/TaskManager.h"
 
 #include "AssetAtlas.h"
+#include "Extensions.h"
 #include "FontRenderer.h"
 #include "GammaFontRenderer.h"
 #include "TextureCache.h"
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 3016814..937bf8d 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -230,6 +230,11 @@
             return false;
         }
 
+        if (op->mPaint && mOps[0].op->mPaint &&
+            op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) {
+            return false;
+        }
+
         /* Draw Modifiers compatibility check
          *
          * Shadows are ignored, as only text uses them, and in that case they are drawn
@@ -244,7 +249,6 @@
          */
         const DrawModifiers& lhsMod = lhs->mDrawModifiers;
         const DrawModifiers& rhsMod = rhs->mDrawModifiers;
-        if (lhsMod.mShader != rhsMod.mShader) return false;
 
         // Draw filter testing expects bit fields to be clear if filter not set.
         if (lhsMod.mHasDrawFilter != rhsMod.mHasDrawFilter) return false;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index dac86cb..96c6292 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -58,11 +58,6 @@
         caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
     }
 
-    for (size_t i = 0; i < shaders.size(); i++) {
-        caches.resourceCache.decrementRefcountLocked(shaders.itemAt(i));
-        caches.resourceCache.destructorLocked(shaders.itemAt(i));
-    }
-
     for (size_t i = 0; i < sourcePaths.size(); i++) {
         caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
     }
@@ -92,7 +87,6 @@
     bitmapResources.clear();
     ownedBitmapResources.clear();
     patchResources.clear();
-    shaders.clear();
     sourcePaths.clear();
     paints.clear();
     regions.clear();
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b2ead5b..11e78b0 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -56,7 +56,6 @@
 class OpenGLRenderer;
 class Rect;
 class Layer;
-class SkiaShader;
 
 class ClipRectOp;
 class SaveLayerOp;
@@ -127,7 +126,6 @@
     SortedVector<const SkPath*> sourcePaths;
     Vector<const SkRegion*> regions;
     Vector<const SkMatrix*> matrices;
-    Vector<SkiaShader*> shaders;
     Vector<Layer*> layers;
     uint32_t functorCount;
     bool hasDrawOps;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index e4867220..ea3e7a8 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -208,9 +208,16 @@
         if (!state.mMatrix.isSimple()) return false;
 
         // check state/paint for transparency
-        if (state.mDrawModifiers.mShader ||
-                state.mAlpha != 1.0f ||
-                (mPaint && mPaint->getAlpha() != 0xFF)) return false;
+        if (mPaint) {
+            if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
+                return false;
+            }
+            if (mPaint->getAlpha() != 0xFF) {
+                return false;
+            }
+        }
+
+        if (state.mAlpha != 1.0f) return false;
 
         SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
         return (mode == SkXfermode::kSrcOver_Mode ||
@@ -592,37 +599,6 @@
     const SkRegion* mRegion;
 };
 
-class ResetShaderOp : public StateOp {
-public:
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
-        renderer.resetShader();
-    }
-
-    virtual void output(int level, uint32_t logFlags) const {
-        OP_LOGS("ResetShader");
-    }
-
-    virtual const char* name() { return "ResetShader"; }
-};
-
-class SetupShaderOp : public StateOp {
-public:
-    SetupShaderOp(SkiaShader* shader)
-            : mShader(shader) {}
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
-        renderer.setupShader(mShader);
-    }
-
-    virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("SetupShader, shader %p", mShader);
-    }
-
-    virtual const char* name() { return "SetupShader"; }
-
-private:
-    SkiaShader* mShader;
-};
-
 class ResetPaintFilterOp : public StateOp {
 public:
     virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 606c67e..229afdf 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -47,7 +47,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 DisplayListData* DisplayListRenderer::finishRecording() {
-    mShaderMap.clear();
     mPaintMap.clear();
     mRegionMap.clear();
     mPathMap.clear();
@@ -394,15 +393,6 @@
     return DrawGlInfo::kStatusDone;
 }
 
-void DisplayListRenderer::resetShader() {
-    addStateOp(new (alloc()) ResetShaderOp());
-}
-
-void DisplayListRenderer::setupShader(SkiaShader* shader) {
-    shader = refShader(shader);
-    addStateOp(new (alloc()) SetupShaderOp(shader));
-}
-
 void DisplayListRenderer::resetPaintFilter() {
     addStateOp(new (alloc()) ResetPaintFilterOp());
 }
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index d814111..f0ae00f 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -95,9 +95,6 @@
     virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
 
     // Misc - should be implemented with SkPaint inspection
-    virtual void resetShader();
-    virtual void setupShader(SkiaShader* shader);
-
     virtual void resetPaintFilter();
     virtual void setupPaintFilter(int clearBits, int setBits);
 
@@ -269,21 +266,6 @@
         return bitmap;
     }
 
-    inline SkiaShader* refShader(SkiaShader* shader) {
-        if (!shader) return NULL;
-
-        SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
-        // TODO: We also need to handle generation ID changes in compose shaders
-        if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) {
-            shaderCopy = shader->copy();
-            // replaceValueFor() performs an add if the entry doesn't exist
-            mShaderMap.replaceValueFor(shader, shaderCopy);
-            mDisplayListData->shaders.add(shaderCopy);
-            mCaches.resourceCache.incrementRefcount(shaderCopy);
-        }
-        return shaderCopy;
-    }
-
     inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
         mDisplayListData->patchResources.add(patch);
         mCaches.resourceCache.incrementRefcount(patch);
@@ -293,7 +275,6 @@
     DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap;
     DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
     DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
-    DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap;
 
     Caches& mCaches;
     DisplayListData* mDisplayListData;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 647c281..4407ab0 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -73,7 +73,7 @@
         }
     }
     renderer->setupDrawColorFilter(paint->getColorFilter());
-    renderer->setupDrawShader();
+    renderer->setupDrawShader(paint->getShader());
     renderer->setupDrawBlending(paint);
     renderer->setupDrawProgram();
     renderer->setupDrawModelView(kModelViewMode_Translate, false,
@@ -85,7 +85,7 @@
     renderer->setupDrawTexture(0);
     renderer->setupDrawPureColorUniforms();
     renderer->setupDrawColorFilterUniforms(paint->getColorFilter());
-    renderer->setupDrawShaderUniforms(pureTranslate);
+    renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate);
     renderer->setupDrawTextGammaUniforms();
 
     return NO_ERROR;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 691f1c9..71836dd 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <SkCanvas.h>
+#include <SkShader.h>
 #include <SkTypeface.h>
 
 #include <utils/Log.h>
@@ -37,6 +38,7 @@
 #include "PathTessellator.h"
 #include "Properties.h"
 #include "ShadowTessellator.h"
+#include "SkiaShader.h"
 #include "utils/GLUtils.h"
 #include "Vector.h"
 #include "VertexBuffer.h"
@@ -1053,6 +1055,45 @@
 
 #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
 
+// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
+// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
+class LayerShader : public SkShader {
+public:
+    LayerShader(Layer* layer, const SkMatrix* localMatrix)
+    : INHERITED(localMatrix)
+    , mLayer(layer) {
+    }
+
+    virtual bool asACustomShader(void** data) const {
+        if (data) {
+            *data = static_cast<void*>(mLayer);
+        }
+        return true;
+    }
+
+    virtual bool isOpaque() const {
+        return !mLayer->isBlend();
+    }
+
+protected:
+    virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
+        LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
+    }
+
+    virtual void flatten(SkWriteBuffer&) const {
+        LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
+    }
+
+    virtual Factory getFactory() const {
+        LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
+        return NULL;
+    }
+private:
+    // Unowned.
+    Layer* mLayer;
+    typedef SkShader INHERITED;
+};
+
 void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
     if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
 
@@ -1066,21 +1107,19 @@
         paint.setAntiAlias(true);
         paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
 
-        SkiaShader* oldShader = mDrawModifiers.mShader;
-
         // create LayerShader to map SaveLayer content into subsequent draw
         SkMatrix shaderMatrix;
         shaderMatrix.setTranslate(rect.left, rect.bottom);
         shaderMatrix.preScale(1, -1);
-        SkiaLayerShader layerShader(layer, &shaderMatrix);
-        mDrawModifiers.mShader = &layerShader;
+        LayerShader layerShader(layer, &shaderMatrix);
+        paint.setShader(&layerShader);
 
         // Since the drawing primitive is defined in local drawing space,
         // we don't need to modify the draw matrix
         const SkPath* maskPath = layer->getConvexMask();
         DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
 
-        mDrawModifiers.mShader = oldShader;
+        paint.setShader(NULL);
         restore();
 
         return;
@@ -1627,9 +1666,9 @@
     mSetShaderColor = mDescription.setColorModulate(a);
 }
 
-void OpenGLRenderer::setupDrawShader() {
-    if (mDrawModifiers.mShader) {
-        mDrawModifiers.mShader->describe(mDescription, mExtensions);
+void OpenGLRenderer::setupDrawShader(const SkShader* shader) {
+    if (shader != NULL) {
+        SkiaShader::describe(&mCaches, mDescription, mExtensions, *shader);
     }
 }
 
@@ -1655,15 +1694,21 @@
     }
 }
 
+static bool isBlendedColorFilter(const SkColorFilter* filter) {
+    if (filter == NULL) {
+        return false;
+    }
+    return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+}
+
 void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) {
     SkXfermode::Mode mode = layer->getMode();
     // When the blending mode is kClear_Mode, we need to use a modulate color
     // argb=1,0,0,0
     accountForClear(mode);
+    // TODO: check shader blending, once we have shader drawing support for layers.
     bool blend = layer->isBlend() || getLayerAlpha(layer) < 1.0f ||
-            (mColorSet && mColorA < 1.0f) ||
-            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
-            layer->getColorFilter();
+            (mColorSet && mColorA < 1.0f) || isBlendedColorFilter(layer->getColorFilter());
     chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
@@ -1673,8 +1718,8 @@
     // argb=1,0,0,0
     accountForClear(mode);
     blend |= (mColorSet && mColorA < 1.0f) ||
-            (mDrawModifiers.mShader && mDrawModifiers.mShader->blend()) ||
-            (paint && paint->getColorFilter());
+            (getShader(paint) && !getShader(paint)->isOpaque()) ||
+            isBlendedColorFilter(getColorFilter(paint));
     chooseBlending(blend, mode, mDescription, swapSrcDst);
 }
 
@@ -1717,8 +1762,8 @@
     }
 }
 
-void OpenGLRenderer::setupDrawColorUniforms() {
-    if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
+void OpenGLRenderer::setupDrawColorUniforms(bool hasShader) {
+    if ((mColorSet && !hasShader) || (hasShader && mSetShaderColor)) {
         mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
     }
 }
@@ -1729,20 +1774,22 @@
     }
 }
 
-void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
-    if (mDrawModifiers.mShader) {
-        if (ignoreTransform) {
-            // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
-            // because it was built into modelView / the geometry, and the SkiaShader needs to
-            // compensate.
-            mat4 modelViewWithoutTransform;
-            modelViewWithoutTransform.loadInverse(*currentTransform());
-            modelViewWithoutTransform.multiply(mModelViewMatrix);
-            mModelViewMatrix.load(modelViewWithoutTransform);
-        }
-        mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
-                mModelViewMatrix, *mSnapshot, &mTextureUnit);
+void OpenGLRenderer::setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform) {
+    if (shader == NULL) {
+        return;
     }
+
+    if (ignoreTransform) {
+        // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform()
+        // because it was built into modelView / the geometry, and the description needs to
+        // compensate.
+        mat4 modelViewWithoutTransform;
+        modelViewWithoutTransform.loadInverse(*currentTransform());
+        modelViewWithoutTransform.multiply(mModelViewMatrix);
+        mModelViewMatrix.load(modelViewWithoutTransform);
+    }
+
+    SkiaShader::setupProgram(&mCaches, mModelViewMatrix, &mTextureUnit, mExtensions, *shader);
 }
 
 void OpenGLRenderer::setupDrawColorFilterUniforms(const SkColorFilter* filter) {
@@ -2201,7 +2248,7 @@
     // Apply a scale transform on the canvas only when a shader is in use
     // Skia handles the ratio between the dst and src rects as a scale factor
     // when a shader is set
-    bool useScaleTransform = mDrawModifiers.mShader && scaled;
+    bool useScaleTransform = getShader(paint) && scaled;
     bool ignoreTransform = false;
 
     if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) {
@@ -2359,13 +2406,13 @@
     if (isAA) setupDrawAA();
     setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, isAA);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
-    setupDrawColorUniforms();
+    setupDrawColorUniforms(getShader(paint));
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms();
+    setupDrawShaderUniforms(getShader(paint));
 
     const void* vertices = vertexBuffer.getBuffer();
     bool force = mCaches.unbindMeshBuffer();
@@ -2670,7 +2717,7 @@
     const float sy = y - shadow->top + textShadow.dy;
 
     const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha;
-    if (mDrawModifiers.mShader) {
+    if (getShader(paint)) {
         textShadow.color = SK_ColorWHITE;
     }
 
@@ -2678,7 +2725,7 @@
     setupDrawWithTexture(true);
     setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha);
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, true);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_TranslateAndScale, false,
@@ -2686,7 +2733,7 @@
     setupDrawTexture(shadow->id);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms();
+    setupDrawShaderUniforms(getShader(paint));
     setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3008,21 +3055,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Shaders
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::resetShader() {
-    mDrawModifiers.mShader = NULL;
-}
-
-void OpenGLRenderer::setupShader(SkiaShader* shader) {
-    mDrawModifiers.mShader = shader;
-    if (mDrawModifiers.mShader) {
-        mDrawModifiers.mShader->setCaches(mCaches);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // Draw filters
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -3080,7 +3112,7 @@
     setupDrawWithTexture(true);
     setupDrawAlpha8Color(paint->getColor(), alpha);
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, true);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_TranslateAndScale, false,
@@ -3088,7 +3120,7 @@
     setupDrawTexture(texture->id);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms();
+    setupDrawShaderUniforms(getShader(paint));
     setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -3254,7 +3286,7 @@
 
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
-    if (mDrawModifiers.mShader) {
+    if (getShader(paint)) {
         color |= 0x00ffffff;
     }
 
@@ -3290,15 +3322,15 @@
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawColorFilter(getColorFilter(paint));
     setupDrawBlending(paint);
     setupDrawProgram();
     setupDrawDirtyRegionsDisabled();
     setupDrawModelView(kModelViewMode_Translate, false,
             0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform);
-    setupDrawColorUniforms();
-    setupDrawShaderUniforms();
+    setupDrawColorUniforms(getShader(paint));
+    setupDrawShaderUniforms(getShader(paint));
     setupDrawColorFilterUniforms(getColorFilter(paint));
 
     if (dirty && hasLayer()) {
@@ -3314,21 +3346,21 @@
         const SkPaint* paint, bool ignoreTransform) {
     int color = paint->getColor();
     // If a shader is set, preserve only the alpha
-    if (mDrawModifiers.mShader) {
+    if (getShader(paint)) {
         color |= 0x00ffffff;
     }
 
     setupDraw();
     setupDrawNoTexture();
     setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha);
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawColorFilter(getColorFilter(paint));
     setupDrawBlending(paint);
     setupDrawProgram();
     setupDrawModelView(kModelViewMode_TranslateAndScale, false,
             left, top, right, bottom, ignoreTransform);
-    setupDrawColorUniforms();
-    setupDrawShaderUniforms(ignoreTransform);
+    setupDrawColorUniforms(getShader(paint));
+    setupDrawShaderUniforms(getShader(paint), ignoreTransform);
     setupDrawColorFilterUniforms(getColorFilter(paint));
     setupDrawSimpleMesh();
 
@@ -3441,7 +3473,7 @@
         setupDrawAlpha8Color(color, alpha);
     }
     setupDrawColorFilter(getColorFilter(paint));
-    setupDrawShader();
+    setupDrawShader(getShader(paint));
     setupDrawBlending(paint, true);
     setupDrawProgram();
     if (!dirty) setupDrawDirtyRegionsDisabled();
@@ -3449,7 +3481,7 @@
     setupDrawTexture(texture);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms(getColorFilter(paint));
-    setupDrawShaderUniforms(ignoreTransform);
+    setupDrawShaderUniforms(getShader(paint), ignoreTransform);
     setupDrawMesh(vertices, texCoords);
 
     glDrawArrays(drawMode, 0, elementsCount);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index c6d9071..fc27947 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -26,7 +26,6 @@
 #include <SkMatrix.h>
 #include <SkPaint.h>
 #include <SkRegion.h>
-#include <SkShader.h>
 #include <SkXfermode.h>
 
 #include <utils/Blur.h>
@@ -45,13 +44,15 @@
 #include "Program.h"
 #include "Rect.h"
 #include "Renderer.h"
-#include "StatefulBaseRenderer.h"
 #include "Snapshot.h"
+#include "StatefulBaseRenderer.h"
 #include "UvMapper.h"
 #include "Vertex.h"
 #include "Caches.h"
 #include "CanvasProperty.h"
 
+class SkShader;
+
 namespace android {
 namespace uirenderer {
 
@@ -59,7 +60,6 @@
 class RenderNode;
 class TextSetupFunctor;
 class VertexBuffer;
-class SkiaShader;
 
 struct DrawModifiers {
     DrawModifiers() {
@@ -70,7 +70,6 @@
         memset(this, 0, sizeof(DrawModifiers));
     }
 
-    SkiaShader* mShader;
     float mOverrideLayerAlpha;
 
     // Draw filters
@@ -217,9 +216,6 @@
     status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
             float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter);
 
-    virtual void resetShader();
-    virtual void setupShader(SkiaShader* shader);
-
     virtual void resetPaintFilter();
     virtual void setupPaintFilter(int clearBits, int setBits);
 
@@ -467,6 +463,14 @@
     }
 
     /**
+     * Safely retrieves the Shader from the given Paint. If the paint is
+     * null then null is returned.
+     */
+    static inline const SkShader* getShader(const SkPaint* paint) {
+        return paint ? paint->getShader() : NULL;
+    }
+
+    /**
      * Set to true to suppress error checks at the end of a frame.
      */
     virtual bool suppressErrorChecks() const {
@@ -838,7 +842,7 @@
     void setupDrawColor(float r, float g, float b, float a);
     void setupDrawAlpha8Color(int color, int alpha);
     void setupDrawTextGamma(const SkPaint* paint);
-    void setupDrawShader();
+    void setupDrawShader(const SkShader* shader);
     void setupDrawColorFilter(const SkColorFilter* filter);
     void setupDrawBlending(const Layer* layer, bool swapSrcDst = false);
     void setupDrawBlending(const SkPaint* paint, bool blend = true, bool swapSrcDst = false);
@@ -862,9 +866,17 @@
      */
     void setupDrawModelView(ModelViewMode mode, bool offset,
             float left, float top, float right, float bottom, bool ignoreTransform = false);
-    void setupDrawColorUniforms();
+    void setupDrawColorUniforms(bool hasShader);
     void setupDrawPureColorUniforms();
-    void setupDrawShaderUniforms(bool ignoreTransform = false);
+
+    /**
+     * Setup uniforms for the current shader.
+     *
+     * @param shader SkShader on the current paint.
+     *
+     * @param ignoreTransform Set to true to ignore the transform in shader.
+     */
+    void setupDrawShaderUniforms(const SkShader* shader, bool ignoreTransform = false);
     void setupDrawColorFilterUniforms(const SkColorFilter* paint);
     void setupDrawSimpleMesh();
     void setupDrawTexture(GLuint texture);
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index e191a26..23cab0e 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -35,7 +35,6 @@
 class Layer;
 class Matrix4;
 class SkiaColorFilter;
-class SkiaShader;
 class Patch;
 
 enum DrawOpMode {
@@ -183,9 +182,6 @@
     virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
 
     // Misc - should be implemented with SkPaint inspection
-    virtual void resetShader() = 0;
-    virtual void setupShader(SkiaShader* shader) = 0;
-
     virtual void resetPaintFilter() = 0;
     virtual void setupPaintFilter(int clearBits, int setBits) = 0;
 
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 13a3e8e..8b553d1 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -71,11 +71,6 @@
     incrementRefcount((void*) pathResource, kPath);
 }
 
-void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
-    SkSafeRef(shaderResource->getSkShader());
-    incrementRefcount((void*) shaderResource, kShader);
-}
-
 void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
     incrementRefcount((void*) patchResource, kNinePatch);
 }
@@ -104,11 +99,6 @@
     incrementRefcountLocked((void*) pathResource, kPath);
 }
 
-void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) {
-    SkSafeRef(shaderResource->getSkShader());
-    incrementRefcountLocked((void*) shaderResource, kShader);
-}
-
 void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) {
     incrementRefcountLocked((void*) patchResource, kNinePatch);
 }
@@ -132,11 +122,6 @@
     decrementRefcount((void*) pathResource);
 }
 
-void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
-    SkSafeUnref(shaderResource->getSkShader());
-    decrementRefcount((void*) shaderResource);
-}
-
 void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
     decrementRefcount((void*) patchResource);
 }
@@ -168,11 +153,6 @@
     decrementRefcountLocked((void*) pathResource);
 }
 
-void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) {
-    SkSafeUnref(shaderResource->getSkShader());
-    decrementRefcountLocked((void*) shaderResource);
-}
-
 void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
     decrementRefcountLocked((void*) patchResource);
 }
@@ -227,25 +207,6 @@
     }
 }
 
-void ResourceCache::destructor(SkiaShader* resource) {
-    Mutex::Autolock _l(mLock);
-    destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(SkiaShader* resource) {
-    ssize_t index = mCache->indexOfKey(resource);
-    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
-    if (ref == NULL) {
-        // If we're not tracking this resource, just delete it
-        delete resource;
-        return;
-    }
-    ref->destroyed = true;
-    if (ref->refCount == 0) {
-        deleteResourceReferenceLocked(resource, ref);
-    }
-}
-
 void ResourceCache::destructor(Res_png_9patch* resource) {
     Mutex::Autolock _l(mLock);
     destructorLocked(resource);
@@ -333,11 +294,6 @@
                 }
             }
             break;
-            case kShader: {
-                SkiaShader* shader = (SkiaShader*) resource;
-                delete shader;
-            }
-            break;
             case kNinePatch: {
                 if (Caches::hasInstance()) {
                     Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 4097ba4..3864d4b 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -20,7 +20,6 @@
 #include <cutils/compiler.h>
 
 #include <SkBitmap.h>
-#include <SkiaShader.h>
 
 #include <utils/KeyedVector.h>
 
@@ -36,7 +35,6 @@
  */
 enum ResourceType {
     kBitmap,
-    kShader,
     kNinePatch,
     kPath,
     kLayer
@@ -70,36 +68,30 @@
 
     void incrementRefcount(const SkPath* resource);
     void incrementRefcount(const SkBitmap* resource);
-    void incrementRefcount(SkiaShader* resource);
     void incrementRefcount(const Res_png_9patch* resource);
     void incrementRefcount(Layer* resource);
 
     void incrementRefcountLocked(const SkPath* resource);
     void incrementRefcountLocked(const SkBitmap* resource);
-    void incrementRefcountLocked(SkiaShader* resource);
     void incrementRefcountLocked(const Res_png_9patch* resource);
     void incrementRefcountLocked(Layer* resource);
 
     void decrementRefcount(const SkBitmap* resource);
     void decrementRefcount(const SkPath* resource);
-    void decrementRefcount(SkiaShader* resource);
     void decrementRefcount(const Res_png_9patch* resource);
     void decrementRefcount(Layer* resource);
 
     void decrementRefcountLocked(const SkBitmap* resource);
     void decrementRefcountLocked(const SkPath* resource);
-    void decrementRefcountLocked(SkiaShader* resource);
     void decrementRefcountLocked(const Res_png_9patch* resource);
     void decrementRefcountLocked(Layer* resource);
 
     void destructor(SkPath* resource);
     void destructor(const SkBitmap* resource);
-    void destructor(SkiaShader* resource);
     void destructor(Res_png_9patch* resource);
 
     void destructorLocked(SkPath* resource);
     void destructorLocked(const SkBitmap* resource);
-    void destructorLocked(SkiaShader* resource);
     void destructorLocked(Res_png_9patch* resource);
 
     bool recycle(SkBitmap* resource);
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6a4a0c8..c672bc4 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -21,9 +21,10 @@
 #include <SkMatrix.h>
 
 #include "Caches.h"
+#include "Layer.h"
+#include "Matrix.h"
 #include "SkiaShader.h"
 #include "Texture.h"
-#include "Matrix.h"
 
 namespace android {
 namespace uirenderer {
@@ -54,89 +55,142 @@
             a);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
-
-void SkiaShader::copyFrom(const SkiaShader& shader) {
-    mType = shader.mType;
-    mKey = shader.mKey;
-    mTileX = shader.mTileX;
-    mTileY = shader.mTileY;
-    mBlend = shader.mBlend;
-    mUnitMatrix = shader.mUnitMatrix;
-    mShaderMatrix = shader.mShaderMatrix;
-    mGenerationId = shader.mGenerationId;
-}
-
-SkiaShader::SkiaShader(): mCaches(NULL) {
-}
-
-SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
-        SkShader::TileMode tileY, const SkMatrix* matrix, bool blend):
-        mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
-        mCaches(NULL) {
-    setMatrix(matrix);
-    mGenerationId = 0;
-}
-
-SkiaShader::~SkiaShader() {
-}
-
-void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
-}
-
-void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-        GLuint* textureUnit) {
-}
-
-void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
-    mCaches->bindTexture(texture->id);
+static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
+    caches->bindTexture(texture->id);
     texture->setWrapST(wrapS, wrapT);
 }
 
-void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
-    screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
-    screenSpace.multiply(modelView);
+/**
+ * Compute the matrix to transform to screen space.
+ * @param screenSpace Output param for the computed matrix.
+ * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
+ *      or identity.
+ * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
+ * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
+ */
+static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
+        const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
+    mat4 shaderMatrix;
+    // uses implicit construction
+    shaderMatrix.loadInverse(localMatrix);
+    // again, uses implicit construction
+    screenSpace.loadMultiply(unitMatrix, shaderMatrix);
+    screenSpace.multiply(modelViewMatrix);
+}
+
+// Returns true if one is a bitmap and the other is a gradient
+static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
+    return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
+            || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
+}
+
+SkiaShaderType SkiaShader::getType(const SkShader& shader) {
+    // First check for a gradient shader.
+    switch (shader.asAGradient(NULL)) {
+        case SkShader::kNone_GradientType:
+            // Not a gradient shader. Fall through to check for other types.
+            break;
+        case SkShader::kLinear_GradientType:
+        case SkShader::kRadial_GradientType:
+        case SkShader::kSweep_GradientType:
+            return kGradient_SkiaShaderType;
+        default:
+            // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
+            return kNone_SkiaShaderType;
+    }
+
+    // The shader is not a gradient. Check for a bitmap shader.
+    if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
+        return kBitmap_SkiaShaderType;
+    }
+
+    // Check for a ComposeShader.
+    SkShader::ComposeRec rec;
+    if (shader.asACompose(&rec)) {
+        const SkiaShaderType shaderAType = getType(*rec.fShaderA);
+        const SkiaShaderType shaderBType = getType(*rec.fShaderB);
+
+        // Compose is only supported if one is a bitmap and the other is a
+        // gradient. Otherwise, return None to skip.
+        if (!bitmapAndGradient(shaderAType, shaderBType)) {
+            return kNone_SkiaShaderType;
+        }
+        return kCompose_SkiaShaderType;
+    }
+
+    if (shader.asACustomShader(NULL)) {
+        return kLayer_SkiaShaderType;
+    }
+
+    return kNone_SkiaShaderType;
+}
+
+typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader);
+
+describeProc gDescribeProc[] = {
+    InvalidSkiaShader::describe,
+    SkiaBitmapShader::describe,
+    SkiaGradientShader::describe,
+    SkiaComposeShader::describe,
+    SkiaLayerShader::describe,
+};
+
+typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+
+setupProgramProc gSetupProgramProc[] = {
+    InvalidSkiaShader::setupProgram,
+    SkiaBitmapShader::setupProgram,
+    SkiaGradientShader::setupProgram,
+    SkiaComposeShader::setupProgram,
+    SkiaLayerShader::setupProgram,
+};
+
+void SkiaShader::describe(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    gDescribeProc[getType(shader)](caches, description, extensions, shader);
+}
+
+void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+
+    gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Layer shader
 ///////////////////////////////////////////////////////////////////////////////
 
-SkiaLayerShader::SkiaLayerShader(Layer* layer, const SkMatrix* matrix):
-        SkiaShader(kBitmap, NULL, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
-                matrix, layer->isBlend()), mLayer(layer) {
-    updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaLayerShader::copy() {
-    SkiaLayerShader* copy = new SkiaLayerShader();
-    copy->copyFrom(*this);
-    copy->mLayer = mLayer;
-    return copy;
-}
-
-void SkiaLayerShader::describe(ProgramDescription& description, const Extensions& extensions) {
+void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
+        const Extensions&, const SkShader& shader) {
     description.hasBitmap = true;
 }
 
-void SkiaLayerShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot& snapshot, GLuint* textureUnit) {
-    GLuint textureSlot = (*textureUnit)++;
-    Caches::getInstance().activeTexture(textureSlot);
+void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+    Layer* layer;
+    if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
+        LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
+    }
 
-    const float width = mLayer->getWidth();
-    const float height = mLayer->getHeight();
+    GLuint textureSlot = (*textureUnit)++;
+    caches->activeTexture(textureSlot);
+
+    const float width = layer->getWidth();
+    const float height = layer->getHeight();
 
     mat4 textureTransform;
-    computeScreenSpaceMatrix(textureTransform, modelView);
+    computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
+
 
     // Uniforms
-    mLayer->bindTexture();
-    mLayer->setWrap(GL_CLAMP_TO_EDGE);
-    mLayer->setFilter(GL_LINEAR);
+    layer->bindTexture();
+    layer->setWrap(GL_CLAMP_TO_EDGE);
+    layer->setFilter(GL_LINEAR);
 
+    Program* program = caches->currentProgram;
     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
@@ -147,67 +201,99 @@
 // Bitmap shader
 ///////////////////////////////////////////////////////////////////////////////
 
-SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
-        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
-        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
-    updateLocalMatrix(matrix);
-}
+struct BitmapShaderInfo {
+    float width;
+    float height;
+    GLenum wrapS;
+    GLenum wrapT;
+    Texture* texture;
+};
 
-SkiaShader* SkiaBitmapShader::copy() {
-    SkiaBitmapShader* copy = new SkiaBitmapShader();
-    copy->copyFrom(*this);
-    copy->mBitmap = mBitmap;
-    return copy;
-}
-
-void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
-    Texture* texture = mCaches->textureCache.get(mBitmap);
-    if (!texture) return;
-    mTexture = texture;
+static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
+        BitmapShaderInfo* shaderInfo,
+        const Extensions& extensions,
+        const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
+    Texture* texture = caches->textureCache.get(&bitmap);
+    if (!texture) return false;
 
     const float width = texture->width;
     const float height = texture->height;
+    GLenum wrapS, wrapT;
 
-    description.hasBitmap = true;
+    if (description) {
+        description->hasBitmap = true;
+    }
     // The driver does not support non-power of two mirrored/repeated
     // textures, so do it ourselves
     if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
-            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
-        description.isBitmapNpot = true;
-        description.bitmapWrapS = gTileModes[mTileX];
-        description.bitmapWrapT = gTileModes[mTileY];
-        mWrapS = GL_CLAMP_TO_EDGE;
-        mWrapT = GL_CLAMP_TO_EDGE;
+            (tileModes[0] != SkShader::kClamp_TileMode ||
+             tileModes[1] != SkShader::kClamp_TileMode)) {
+        if (description) {
+            description->isBitmapNpot = true;
+            description->bitmapWrapS = gTileModes[tileModes[0]];
+            description->bitmapWrapT = gTileModes[tileModes[1]];
+        }
+        wrapS = GL_CLAMP_TO_EDGE;
+        wrapT = GL_CLAMP_TO_EDGE;
     } else {
-        mWrapS = gTileModes[mTileX];
-        mWrapT = gTileModes[mTileY];
+        wrapS = gTileModes[tileModes[0]];
+        wrapT = gTileModes[tileModes[1]];
     }
+
+    if (shaderInfo) {
+        shaderInfo->width = width;
+        shaderInfo->height = height;
+        shaderInfo->wrapS = wrapS;
+        shaderInfo->wrapT = wrapT;
+        shaderInfo->texture = texture;
+    }
+    return true;
 }
 
-void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot&, GLuint* textureUnit) {
+void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+        LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
+    }
+    bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
+}
+
+void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+    SkBitmap bitmap;
+    SkShader::TileMode xy[2];
+    if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
+        LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
+    }
+
     GLuint textureSlot = (*textureUnit)++;
     Caches::getInstance().activeTexture(textureSlot);
 
-    Texture* texture = mTexture;
-    mTexture = NULL;
-    if (!texture) return;
+    BitmapShaderInfo shaderInfo;
+    if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
+        return;
+    }
+
+    Program* program = caches->currentProgram;
+    Texture* texture = shaderInfo.texture;
+
     const AutoTexture autoCleanup(texture);
 
-    const float width = texture->width;
-    const float height = texture->height;
-
     mat4 textureTransform;
-    computeScreenSpaceMatrix(textureTransform, modelView);
+    computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
 
     // Uniforms
-    bindTexture(texture, mWrapS, mWrapT);
+    bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
     texture->setFilter(GL_LINEAR);
 
     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
             GL_FALSE, &textureTransform.data[0]);
-    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+    glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
+            1.0f / shaderInfo.height);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -225,74 +311,6 @@
     matrix->postScale(inv, inv);
 }
 
-SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
-        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
-        SkMatrix* matrix, bool blend):
-        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
-        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
-    SkPoint points[2];
-    points[0].set(bounds[0], bounds[1]);
-    points[1].set(bounds[2], bounds[3]);
-
-    SkMatrix unitMatrix;
-    toUnitMatrix(points, &unitMatrix);
-    mUnitMatrix.load(unitMatrix);
-
-    updateLocalMatrix(matrix);
-
-    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaLinearGradientShader::~SkiaLinearGradientShader() {
-    delete[] mBounds;
-    delete[] mColors;
-    delete[] mPositions;
-}
-
-SkiaShader* SkiaLinearGradientShader::copy() {
-    SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
-    copy->copyFrom(*this);
-    copy->mBounds = new float[4];
-    memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
-    copy->mColors = new uint32_t[mCount];
-    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
-    copy->mPositions = new float[mCount];
-    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
-    copy->mCount = mCount;
-    copy->mIsSimple = mIsSimple;
-    return copy;
-}
-
-void SkiaLinearGradientShader::describe(ProgramDescription& description,
-        const Extensions& extensions) {
-    description.hasGradient = true;
-    description.gradientType = ProgramDescription::kGradientLinear;
-    description.isSimpleGradient = mIsSimple;
-}
-
-void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot&, GLuint* textureUnit) {
-    if (CC_UNLIKELY(!mIsSimple)) {
-        GLuint textureSlot = (*textureUnit)++;
-        Caches::getInstance().activeTexture(textureSlot);
-
-        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
-
-        // Uniforms
-        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
-        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
-    } else {
-        bindUniformColor(program->getUniform("startColor"), mColors[0]);
-        bindUniformColor(program->getUniform("endColor"), mColors[1]);
-    }
-
-    Caches::getInstance().dither.setupProgram(program, textureUnit);
-
-    mat4 screenSpace;
-    computeScreenSpaceMatrix(screenSpace, modelView);
-    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Circular gradient shader
 ///////////////////////////////////////////////////////////////////////////////
@@ -304,37 +322,6 @@
     matrix->postScale(inv, inv);
 }
 
-SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
-        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
-        SkMatrix* matrix, bool blend):
-        SkiaSweepGradientShader(kCircularGradient, colors, positions, count, key,
-                tileMode, matrix, blend) {
-    SkMatrix unitMatrix;
-    toCircularUnitMatrix(x, y, radius, &unitMatrix);
-    mUnitMatrix.load(unitMatrix);
-
-    updateLocalMatrix(matrix);
-}
-
-SkiaShader* SkiaCircularGradientShader::copy() {
-    SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
-    copy->copyFrom(*this);
-    copy->mColors = new uint32_t[mCount];
-    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
-    copy->mPositions = new float[mCount];
-    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
-    copy->mCount = mCount;
-    copy->mIsSimple = mIsSimple;
-    return copy;
-}
-
-void SkiaCircularGradientShader::describe(ProgramDescription& description,
-        const Extensions& extensions) {
-    description.hasGradient = true;
-    description.gradientType = ProgramDescription::kGradientCircular;
-    description.isSimpleGradient = mIsSimple;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Sweep gradient shader
 ///////////////////////////////////////////////////////////////////////////////
@@ -343,74 +330,103 @@
     matrix->setTranslate(-x, -y);
 }
 
-SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
-        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
-        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
-                SkShader::kClamp_TileMode, matrix, blend),
-        mColors(colors), mPositions(positions), mCount(count) {
-    SkMatrix unitMatrix;
-    toSweepUnitMatrix(x, y, &unitMatrix);
-    mUnitMatrix.load(unitMatrix);
+///////////////////////////////////////////////////////////////////////////////
+// Common gradient code
+///////////////////////////////////////////////////////////////////////////////
 
-    updateLocalMatrix(matrix);
-
-    mIsSimple = count == 2;
+static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
+    return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
 }
 
-SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, uint32_t* colors,
-        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
-        SkMatrix* matrix, bool blend):
-        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
-        mColors(colors), mPositions(positions), mCount(count) {
-    // protected method, that doesn't setup mUnitMatrix - should be handled by subclass
+void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    SkShader::GradientInfo gradInfo;
+    gradInfo.fColorCount = 0;
+    gradInfo.fColors = NULL;
+    gradInfo.fColorOffsets = NULL;
 
-    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
-}
-
-SkiaSweepGradientShader::~SkiaSweepGradientShader() {
-    delete[] mColors;
-    delete[] mPositions;
-}
-
-SkiaShader* SkiaSweepGradientShader::copy() {
-    SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
-    copy->copyFrom(*this);
-    copy->mColors = new uint32_t[mCount];
-    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
-    copy->mPositions = new float[mCount];
-    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
-    copy->mCount = mCount;
-    copy->mIsSimple = mIsSimple;
-    return copy;
-}
-
-void SkiaSweepGradientShader::describe(ProgramDescription& description,
-        const Extensions& extensions) {
+    switch (shader.asAGradient(&gradInfo)) {
+        case SkShader::kLinear_GradientType:
+            description.gradientType = ProgramDescription::kGradientLinear;
+            break;
+        case SkShader::kRadial_GradientType:
+            description.gradientType = ProgramDescription::kGradientCircular;
+            break;
+        case SkShader::kSweep_GradientType:
+            description.gradientType = ProgramDescription::kGradientSweep;
+            break;
+        default:
+            // Do nothing. This shader is unsupported.
+            return;
+    }
     description.hasGradient = true;
-    description.gradientType = ProgramDescription::kGradientSweep;
-    description.isSimpleGradient = mIsSimple;
+    description.isSimpleGradient = isSimpleGradient(gradInfo);
 }
 
-void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot& snapshot, GLuint* textureUnit) {
-    if (CC_UNLIKELY(!mIsSimple)) {
-        GLuint textureSlot = (*textureUnit)++;
-        Caches::getInstance().activeTexture(textureSlot);
+void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions&, const SkShader& shader) {
+    // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
+    // how much space has been allocated for fColors and fColorOffsets.  10 was chosen
+    // arbitrarily, but should be >= 2.
+    // As output, it tells the number of actual colors/offsets in the gradient.
+    const int COLOR_COUNT = 10;
+    SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
+    SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
 
-        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
+    SkShader::GradientInfo gradInfo;
+    gradInfo.fColorCount = COLOR_COUNT;
+    gradInfo.fColors = colorStorage.get();
+    gradInfo.fColorOffsets = positionStorage.get();
+
+    SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
+
+    Program* program = caches->currentProgram;
+    if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+        if (gradInfo.fColorCount > COLOR_COUNT) {
+            // There was not enough room in our arrays for all the colors and offsets. Try again,
+            // now that we know the true number of colors.
+            gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
+            gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
+
+            shader.asAGradient(&gradInfo);
+        }
+        GLuint textureSlot = (*textureUnit)++;
+        caches->activeTexture(textureSlot);
+
+#ifndef SK_SCALAR_IS_FLOAT
+    #error Need to convert gradInfo.fColorOffsets to float!
+#endif
+        Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
+                gradInfo.fColorCount);
 
         // Uniforms
-        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
+        bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
         glUniform1i(program->getUniform("gradientSampler"), textureSlot);
     } else {
-       bindUniformColor(program->getUniform("startColor"), mColors[0]);
-       bindUniformColor(program->getUniform("endColor"), mColors[1]);
+        bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
+        bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
     }
 
-    mCaches->dither.setupProgram(program, textureUnit);
+    caches->dither.setupProgram(program, textureUnit);
+
+    SkMatrix unitMatrix;
+    switch (gradType) {
+        case SkShader::kLinear_GradientType:
+            toUnitMatrix(gradInfo.fPoint, &unitMatrix);
+            break;
+        case SkShader::kRadial_GradientType:
+            toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
+                    gradInfo.fRadius[0], &unitMatrix);
+            break;
+        case SkShader::kSweep_GradientType:
+            toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
+    }
 
     mat4 screenSpace;
-    computeScreenSpaceMatrix(screenSpace, modelView);
+    computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
     glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
 }
 
@@ -418,49 +434,39 @@
 // Compose shader
 ///////////////////////////////////////////////////////////////////////////////
 
-SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
-        SkXfermode::Mode mode, SkShader* key):
-        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
-        NULL, first->blend() || second->blend()),
-        mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
-}
-
-SkiaComposeShader::~SkiaComposeShader() {
-    if (mCleanup) {
-        delete mFirst;
-        delete mSecond;
+void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
+        const Extensions& extensions, const SkShader& shader) {
+    SkShader::ComposeRec rec;
+    if (!shader.asACompose(&rec)) {
+        LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
     }
-}
-
-SkiaShader* SkiaComposeShader::copy() {
-    SkiaComposeShader* copy = new SkiaComposeShader();
-    copy->copyFrom(*this);
-    copy->mFirst = mFirst->copy();
-    copy->mSecond = mSecond->copy();
-    copy->mMode = mMode;
-    copy->cleanup();
-    return copy;
-}
-
-void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
-    mFirst->describe(description, extensions);
-    mSecond->describe(description, extensions);
-    if (mFirst->type() == kBitmap) {
+    SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
+    SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
+    if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
         description.isBitmapFirst = true;
     }
-    description.shadersMode = mMode;
+    if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
+        // TODO: Support other modes.
+        description.shadersMode = SkXfermode::kSrcOver_Mode;
+    }
 }
 
-void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
-        const Snapshot& snapshot, GLuint* textureUnit) {
+void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
+        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+    SkShader::ComposeRec rec;
+    if (!shader.asACompose(&rec)) {
+        LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
+    }
+
     // Apply this compose shader's local transform and pass it down to
     // the child shaders. They will in turn apply their local transform
     // to this matrix.
     mat4 transform;
-    computeScreenSpaceMatrix(transform, modelView);
+    computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
+            modelViewMatrix);
 
-    mFirst->setupProgram(program, transform, snapshot, textureUnit);
-    mSecond->setupProgram(program, transform, snapshot, textureUnit);
+    SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
+    SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 9f30257..034c3f6 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -28,249 +28,90 @@
 #include "ProgramCache.h"
 #include "TextureCache.h"
 #include "GradientCache.h"
-#include "Snapshot.h"
 
 namespace android {
 namespace uirenderer {
 
 class Caches;
-
-///////////////////////////////////////////////////////////////////////////////
-// Base shader
-///////////////////////////////////////////////////////////////////////////////
+class Layer;
 
 /**
- * Represents a Skia shader. A shader will modify the GL context and active
- * program to recreate the original effect.
+ * Type of Skia shader in use.
  */
+enum SkiaShaderType {
+    kNone_SkiaShaderType,
+    kBitmap_SkiaShaderType,
+    kGradient_SkiaShaderType,
+    kCompose_SkiaShaderType,
+    kLayer_SkiaShaderType
+};
+
 class SkiaShader {
 public:
-    /**
-     * Type of Skia shader in use.
-     */
-    enum Type {
-        kNone,
-        kBitmap,
-        kLinearGradient,
-        kCircularGradient,
-        kSweepGradient,
-        kCompose
-    };
+    static SkiaShaderType getType(const SkShader& shader);
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
 
-    ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
-            SkShader::TileMode tileY, const SkMatrix* matrix, bool blend);
-    virtual ~SkiaShader();
-
-    virtual SkiaShader* copy() = 0;
-    void copyFrom(const SkiaShader& shader);
-
-    virtual void describe(ProgramDescription& description, const Extensions& extensions);
-    virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-    inline SkShader* getSkShader() {
-        return mKey;
+class InvalidSkiaShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader) {
+        // This shader is unsupported. Skip it.
+    }
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
+        // This shader is unsupported. Skip it.
     }
 
-    inline bool blend() const {
-        return mBlend;
-    }
-
-    Type type() const {
-        return mType;
-    }
-
-    virtual void setCaches(Caches& caches) {
-        mCaches = &caches;
-    }
-
-    uint32_t getGenerationId() {
-        return mGenerationId;
-    }
-
-    void setMatrix(const SkMatrix* matrix) {
-        updateLocalMatrix(matrix);
-        mGenerationId++;
-    }
-
-    void updateLocalMatrix(const SkMatrix* matrix) {
-        if (matrix) {
-            mat4 localMatrix(*matrix);
-            mShaderMatrix.loadInverse(localMatrix);
-        } else {
-            mShaderMatrix.loadIdentity();
-        }
-    }
-
-    void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
-
-protected:
-    SkiaShader();
-
-    /**
-     * The appropriate texture unit must have been activated prior to invoking
-     * this method.
-     */
-    inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT);
-
-    Type mType;
-    SkShader* mKey;
-    SkShader::TileMode mTileX;
-    SkShader::TileMode mTileY;
-    bool mBlend;
-
-    Caches* mCaches;
-
-    mat4 mUnitMatrix;
-    mat4 mShaderMatrix;
-
-private:
-    uint32_t mGenerationId;
-}; // struct SkiaShader
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementations
-///////////////////////////////////////////////////////////////////////////////
-
+};
 /**
  * A shader that draws a layer.
  */
-struct SkiaLayerShader: public SkiaShader {
-    SkiaLayerShader(Layer* layer, const SkMatrix* matrix);
-    SkiaShader* copy();
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-private:
-    SkiaLayerShader() {
-    }
-
-    Layer* mLayer;
-}; // struct SkiaLayerShader
+class SkiaLayerShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaLayerShader
 
 /**
  * A shader that draws a bitmap.
  */
-struct SkiaBitmapShader: public SkiaShader {
-    ANDROID_API SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
-            SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
-    SkiaShader* copy();
+class SkiaBitmapShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
 
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
 
-private:
-    SkiaBitmapShader() : mBitmap(NULL), mTexture(NULL) {
-    }
-
-    SkBitmap* mBitmap;
-    Texture* mTexture;
-    GLenum mWrapS;
-    GLenum mWrapT;
-}; // struct SkiaBitmapShader
+}; // class SkiaBitmapShader
 
 /**
- * A shader that draws a linear gradient.
+ * A shader that draws one of three types of gradient, depending on shader param.
  */
-struct SkiaLinearGradientShader: public SkiaShader {
-    ANDROID_API SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions,
-            int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
-    ~SkiaLinearGradientShader();
-    SkiaShader* copy();
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-private:
-    SkiaLinearGradientShader() {
-    }
-
-    bool mIsSimple;
-    float* mBounds;
-    uint32_t* mColors;
-    float* mPositions;
-    int mCount;
-}; // struct SkiaLinearGradientShader
-
-/**
- * A shader that draws a sweep gradient.
- */
-struct SkiaSweepGradientShader: public SkiaShader {
-    ANDROID_API SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions,
-            int count, SkShader* key, SkMatrix* matrix, bool blend);
-    ~SkiaSweepGradientShader();
-    SkiaShader* copy();
-
-    virtual void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-protected:
-    SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions,
-            int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
-    SkiaSweepGradientShader() {
-    }
-
-    bool mIsSimple;
-    uint32_t* mColors;
-    float* mPositions;
-    int mCount;
-}; // struct SkiaSweepGradientShader
-
-/**
- * A shader that draws a circular gradient.
- */
-struct SkiaCircularGradientShader: public SkiaSweepGradientShader {
-    ANDROID_API SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors,
-            float* positions, int count, SkShader* key,SkShader::TileMode tileMode,
-            SkMatrix* matrix, bool blend);
-    SkiaShader* copy();
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-
-private:
-    SkiaCircularGradientShader() {
-    }
-}; // struct SkiaCircularGradientShader
+class SkiaGradientShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+};
 
 /**
  * A shader that draws two shaders, composited with an xfermode.
  */
-struct SkiaComposeShader: public SkiaShader {
-    ANDROID_API SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode,
-            SkShader* key);
-    ~SkiaComposeShader();
-    SkiaShader* copy();
-
-    void setCaches(Caches& caches) {
-        SkiaShader::setCaches(caches);
-        mFirst->setCaches(caches);
-        mSecond->setCaches(caches);
-    }
-
-    void describe(ProgramDescription& description, const Extensions& extensions);
-    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
-            GLuint* textureUnit);
-
-private:
-    SkiaComposeShader(): mCleanup(false) {
-    }
-
-    void cleanup() {
-        mCleanup = true;
-    }
-
-    SkiaShader* mFirst;
-    SkiaShader* mSecond;
-    SkXfermode::Mode mMode;
-
-    bool mCleanup;
-}; // struct SkiaComposeShader
+class SkiaComposeShader {
+public:
+    static void describe(Caches* caches, ProgramDescription& description,
+            const Extensions& extensions, const SkShader& shader);
+    static void setupProgram(Caches* caches, const mat4& modelViewMatrix,
+            GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
+}; // class SkiaComposeShader
 
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 34e2265..60b4b96 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -34,7 +34,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 TextureCache::TextureCache():
-        mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
         mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
     char property[PROPERTY_VALUE_MAX];
@@ -58,7 +58,7 @@
 }
 
 TextureCache::TextureCache(uint32_t maxByteSize):
-        mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     init();
 }
@@ -103,7 +103,7 @@
 // Callbacks
 ///////////////////////////////////////////////////////////////////////////////
 
-void TextureCache::operator()(const SkBitmap*&, Texture*& texture) {
+void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) {
     // This will be called already locked
     if (texture) {
         mSize -= texture->bitmapSize;
@@ -122,7 +122,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void TextureCache::resetMarkInUse() {
-    LruCache<const SkBitmap*, Texture*>::Iterator iter(mCache);
+    LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache);
     while (iter.next()) {
         iter.value()->isInUse = false;
     }
@@ -140,7 +140,7 @@
 // Returns a prepared Texture* that either is already in the cache or can fit
 // in the cache (and is thus added to the cache)
 Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
-    Texture* texture = mCache.get(bitmap);
+    Texture* texture = mCache.get(bitmap->pixelRef());
 
     if (!texture) {
         if (!canMakeTextureFromBitmap(bitmap)) {
@@ -170,7 +170,7 @@
             if (mDebugEnabled) {
                 ALOGD("Texture created, size = %d", size);
             }
-            mCache.put(bitmap, texture);
+            mCache.put(bitmap->pixelRef(), texture);
         }
     } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
         // Texture was in the cache but is dirty, re-upload
@@ -218,7 +218,7 @@
 }
 
 void TextureCache::remove(const SkBitmap* bitmap) {
-    mCache.remove(bitmap);
+    mCache.remove(bitmap->pixelRef());
 }
 
 void TextureCache::removeDeferred(const SkBitmap* bitmap) {
@@ -231,7 +231,7 @@
     size_t count = mGarbage.size();
     for (size_t i = 0; i < count; i++) {
         const SkBitmap* bitmap = mGarbage.itemAt(i);
-        mCache.remove(bitmap);
+        mCache.remove(bitmap->pixelRef());
         delete bitmap;
     }
     mGarbage.clear();
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 48a10c2..e5b5c1a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -49,7 +49,7 @@
  * Any texture added to the cache causing the cache to grow beyond the maximum
  * allowed size will also cause the oldest texture to be kicked out.
  */
-class TextureCache: public OnEntryRemoved<const SkBitmap*, Texture*> {
+class TextureCache: public OnEntryRemoved<const SkPixelRef*, Texture*> {
 public:
     TextureCache();
     TextureCache(uint32_t maxByteSize);
@@ -59,7 +59,7 @@
      * Used as a callback when an entry is removed from the cache.
      * Do not invoke directly.
      */
-    void operator()(const SkBitmap*& bitmap, Texture*& texture);
+    void operator()(const SkPixelRef*& pixelRef, Texture*& texture);
 
     /**
      * Resets all Textures to not be marked as in use
@@ -147,7 +147,7 @@
 
     void init();
 
-    LruCache<const SkBitmap*, Texture*> mCache;
+    LruCache<const SkPixelRef*, Texture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/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..b6b3428 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -360,7 +360,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 +395,7 @@
     makeCurrent();
 }
 
-bool CanvasContext::initialize(EGLNativeWindowType window) {
+bool CanvasContext::initialize(ANativeWindow* window) {
     if (mCanvas) return false;
     setSurface(window);
     mCanvas = new OpenGLRenderer();
@@ -401,11 +403,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 +458,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,6 +477,11 @@
     }
 }
 
+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!");
@@ -515,7 +530,9 @@
     info.prepareTextures = false;
 
     prepareTree(info);
-    draw(NULL);
+    if (info.out.canDrawThisFrame) {
+        draw(NULL);
+    }
 }
 
 void CanvasContext::invokeFunctor(Functor* functor) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4232f27..a54b33e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -48,9 +48,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();
@@ -73,11 +73,15 @@
     ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
             int64_t* map, size_t mapSize);
 
+    void notifyFramePending();
+
 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 +89,7 @@
 
     GlobalContext* mGlobalContext;
     RenderThread& mRenderThread;
+    sp<ANativeWindow> mNativeWindow;
     EGLSurface mEglSurface;
     bool mDirtyRegionsEnabled;
 
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 3b8786c..ee3e059 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -87,7 +87,13 @@
 void DrawFrameTask::run() {
     ATRACE_NAME("DrawFrame");
 
-    bool canUnblockUiThread = syncFrameState();
+    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 +104,9 @@
         unblockUiThread();
     }
 
-    context->draw(&dirty);
+    if (CC_LIKELY(canDrawThisFrame)) {
+        context->draw(&dirty);
+    }
 
     if (!canUnblockUiThread) {
         unblockUiThread();
@@ -111,12 +119,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..acbc02a 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 {
@@ -65,7 +66,7 @@
 
 private:
     void postAndWait();
-    bool syncFrameState();
+    bool syncFrameState(TreeInfo& info);
     void unblockUiThread();
 
     Mutex mLock;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ef8e45b..2e103d8 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -112,7 +112,7 @@
     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 +123,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 +135,7 @@
     postAndWait(task);
 }
 
-CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
     args->context->pauseSurface(args->window);
     return NULL;
 }
@@ -292,6 +292,17 @@
     postAndWait(task);
 }
 
+CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
+    args->context->notifyFramePending();
+    return NULL;
+}
+
+void RenderProxy::notifyFramePending() {
+    SETUP_TASK(notifyFramePending);
+    args->context = mContext;
+    mRenderThread.queueAtFront(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..8aeb264 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@
     ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
 
     ANDROID_API void fence();
+    ANDROID_API void notifyFramePending();
 
 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/DngCreator.java b/media/java/android/media/DngCreator.java
deleted file mode 100644
index 76c6d46..0000000
--- a/media/java/android/media/DngCreator.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-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.util.Size;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-
-/**
- * The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
- *
- * <p>
- * This class is designed to be used with the {@link android.graphics.ImageFormat#RAW_SENSOR}
- * buffers available from {@link android.hardware.camera2.CameraDevice}, or with Bayer-type raw
- * pixel data that is otherwise generated by an application.  The DNG metadata tags will be
- * generated from a {@link android.hardware.camera2.CaptureResult} object or set directly.
- * </p>
- *
- * <p>
- * The DNG file format is a cross-platform file format that is used to store pixel data from
- * camera sensors with minimal pre-processing applied.  DNG files allow for pixel data to be
- * defined in a user-defined colorspace, and have associated metadata that allow for this
- * pixel data to be converted to the standard CIE XYZ colorspace during post-processing.
- * </p>
- *
- * <p>
- * For more information on the DNG file format and associated metadata, please refer to the
- * <a href=
- * "https://wwwimages2.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf">
- * Adobe DNG 1.4.0.0 specification</a>.
- * </p>
- */
-public final class DngCreator implements AutoCloseable {
-
-    /**
-     * Create a new DNG object.
-     *
-     * <p>
-     * It is not necessary to call any set methods to write a well-formatted DNG file.
-     * </p>
-     * <p>
-     * DNG metadata tags will be generated from the corresponding parameters in the
-     * {@link android.hardware.camera2.CaptureResult} object.  This removes or overrides
-     * all previous tags set.
-     * </p>
-     *
-     * @param characteristics an object containing the static
-     *          {@link android.hardware.camera2.CameraCharacteristics}.
-     * @param metadata a metadata object to generate tags from.
-     */
-    public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
-        if (characteristics == null || metadata == null) {
-            throw new NullPointerException("Null argument to DngCreator constructor");
-        }
-        nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());
-    }
-
-    /**
-     * Set the orientation value to write.
-     *
-     * <p>
-     * This will be written as the TIFF "Orientation" tag {@code (0x0112)}.
-     * Calling this will override any prior settings for this tag.
-     * </p>
-     *
-     * @param orientation the orientation value to set, one of:
-     *                    <ul>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_NORMAL}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_HORIZONTAL}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_180}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_VERTICAL}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_TRANSPOSE}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_90}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_TRANSVERSE}</li>
-     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_270}</li>
-     *                    </ul>
-     * @return this {@link #DngCreator} object.
-     */
-    public DngCreator setOrientation(int orientation) {
-
-        if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
-                orientation > ExifInterface.ORIENTATION_ROTATE_270) {
-            throw new IllegalArgumentException("Orientation " + orientation +
-                    " is not a valid EXIF orientation value");
-        }
-        nativeSetOrientation(orientation);
-        return this;
-    }
-
-    /**
-     * Set the thumbnail image.
-     *
-     * <p>
-     * Pixel data will be converted to a Baseline TIFF RGB image, with 8 bits per color channel.
-     * The alpha channel will be discarded.
-     * </p>
-     *
-     * <p>
-     * The given bitmap should not be altered while this object is in use.
-     * </p>
-     *
-     * @param pixels a {@link android.graphics.Bitmap} of pixel data.
-     * @return this {@link #DngCreator} object.
-     */
-    public DngCreator setThumbnail(Bitmap pixels) {
-        if (pixels == null) {
-            throw new NullPointerException("Null argument to setThumbnail");
-        }
-
-        Bitmap.Config config = pixels.getConfig();
-
-        if (config != Bitmap.Config.ARGB_8888) {
-            pixels = pixels.copy(Bitmap.Config.ARGB_8888, false);
-            if (pixels == null) {
-                throw new IllegalArgumentException("Unsupported Bitmap format " + config);
-            }
-            nativeSetThumbnailBitmap(pixels);
-        }
-
-        return this;
-    }
-
-    /**
-     * Set the thumbnail image.
-     *
-     * <p>
-     * Pixel data is interpreted as a {@link android.graphics.ImageFormat#YUV_420_888} image.
-     * </p>
-     *
-     * <p>
-     * The given image should not be altered while this object is in use.
-     * </p>
-     *
-     * @param pixels an {@link android.media.Image} object with the format
-     *               {@link android.graphics.ImageFormat#YUV_420_888}.
-     * @return this {@link #DngCreator} object.
-     */
-    public DngCreator setThumbnail(Image pixels) {
-        if (pixels == null) {
-            throw new NullPointerException("Null argument to setThumbnail");
-        }
-
-        int format = pixels.getFormat();
-        if (format != ImageFormat.YUV_420_888) {
-            throw new IllegalArgumentException("Unsupported image format " + format);
-        }
-
-        Image.Plane[] planes = pixels.getPlanes();
-        nativeSetThumbnailImage(pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
-                planes[0].getRowStride(), planes[0].getPixelStride(), planes[1].getBuffer(),
-                planes[1].getRowStride(), planes[1].getPixelStride(), planes[1].getBuffer(),
-                planes[1].getRowStride(), planes[1].getPixelStride());
-
-        return this;
-    }
-
-
-    /**
-     * Set image location metadata.
-     *
-     * <p>
-     * The given location object must contain at least a valid time, latitude, and longitude
-     * (equivalent to the values returned by {@link android.location.Location#getTime()},
-     * {@link android.location.Location#getLatitude()}, and
-     * {@link android.location.Location#getLongitude()} methods).
-     * </p>
-     *
-     * @param location an {@link android.location.Location} object to set.
-     * @return this {@link #DngCreator} object.
-     *
-     * @throws java.lang.IllegalArgumentException if the given location object doesn't
-     *          contain enough information to set location metadata.
-     */
-    public DngCreator setLocation(Location location) {
-        /*TODO*/
-        return this;
-    }
-
-    /**
-     * Set the user description string to write.
-     *
-     * <p>
-     * This is equivalent to setting the TIFF "ImageDescription" tag {@code (0x010E)}.
-     * </p>
-     *
-     * @param description the user description string.
-     * @return this {@link #DngCreator} object.
-     */
-    public DngCreator setDescription(String description) {
-        /*TODO*/
-        return this;
-    }
-
-    /**
-     * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
-     * the currently configured metadata.
-     *
-     * <p>
-     * Raw pixel data must have 16 bits per pixel, and the input must contain at least
-     * {@code offset + 2 * width * height)} bytes.  The width and height of
-     * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
-     * and will typically be equal to the width and height of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
-     * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
-     * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}.  If insufficient
-     * metadata is available to write a well-formatted DNG file, an
-     * {@link java.lang.IllegalStateException} will be thrown.
-     * </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.io.InputStream} 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.
-     *
-     * @throws IOException if an error was encountered in the input or output stream.
-     * @throws java.lang.IllegalStateException if not enough metadata information has been
-     *          set to write a well-formatted DNG file.
-     * @throws java.lang.IllegalArgumentException if the size passed in does not match the
-     */
-    public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
-            throws IOException {
-        if (dngOutput == null || pixels == null) {
-            throw new NullPointerException("Null argument to writeImage");
-        }
-        nativeWriteInputStream(dngOutput, pixels, offset);
-    }
-
-    /**
-     * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
-     * the currently configured metadata.
-     *
-     * <p>
-     * Raw pixel data must have 16 bits per pixel, and the input must contain at least
-     * {@code offset + 2 * width * height)} bytes.  The width and height of
-     * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
-     * and will typically be equal to the width and height of
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
-     * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
-     * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}.  If insufficient
-     * metadata is available to write a well-formatted DNG file, an
-     * {@link java.lang.IllegalStateException} will be thrown.
-     * </p>
-     *
-     * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
-     * @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.
-     *
-     * @throws IOException if an error was encountered in the input or output stream.
-     * @throws java.lang.IllegalStateException if not enough metadata information has been
-     *          set to write a well-formatted DNG file.
-     */
-    public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
-            throws IOException {
-        if (dngOutput == null || pixels == null) {
-            throw new NullPointerException("Null argument to writeImage");
-        }
-        nativeWriteByteBuffer(dngOutput, pixels, offset);
-    }
-
-    /**
-     * Write the pixel data to a DNG file with the currently configured metadata.
-     *
-     * <p>
-     * For this method to succeed, the {@link android.media.Image} input must contain
-     * {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data, otherwise an
-     * {@link java.lang.IllegalArgumentException} will be thrown.
-     * </p>
-     *
-     * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
-     * @param pixels an {@link android.media.Image} to write.
-     *
-     * @throws java.io.IOException if an error was encountered in the output stream.
-     * @throws java.lang.IllegalArgumentException if an image with an unsupported format was used.
-     * @throws java.lang.IllegalStateException if not enough metadata information has been
-     *          set to write a well-formatted DNG file.
-     */
-    public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
-        if (dngOutput == null || pixels == null) {
-            throw new NullPointerException("Null argument to writeImage");
-        }
-
-        int format = pixels.getFormat();
-        if (format != ImageFormat.RAW_SENSOR) {
-            throw new IllegalArgumentException("Unsupported image format " + format);
-        }
-
-        Image.Plane[] planes = pixels.getPlanes();
-        nativeWriteImage(dngOutput, pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
-                planes[0].getRowStride(), planes[0].getPixelStride());
-    }
-
-    @Override
-    public void close() {
-        nativeDestroy();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * This field is used by native code, do not access or modify.
-     */
-    private long mNativeContext;
-
-    private static native void nativeClassInit();
-
-    private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
-                                                CameraMetadataNative nativeResult);
-
-    private synchronized native void nativeDestroy();
-
-    private synchronized native void nativeSetOrientation(int orientation);
-
-    private synchronized native void nativeSetThumbnailBitmap(Bitmap bitmap);
-
-    private synchronized native void nativeSetThumbnailImage(int width, int height,
-                                                             ByteBuffer yBuffer, int yRowStride,
-                                                             int yPixStride, ByteBuffer uBuffer,
-                                                             int uRowStride, int uPixStride,
-                                                             ByteBuffer vBuffer, int vRowStride,
-                                                             int vPixStride);
-
-    private synchronized native void nativeWriteImage(OutputStream out, int width, int height,
-                                                      ByteBuffer rawBuffer, int rowStride,
-                                                      int pixStride) throws IOException;
-
-    private synchronized native void nativeWriteByteBuffer(OutputStream out, ByteBuffer rawBuffer,
-                                                           long offset) throws IOException;
-
-    private synchronized native void nativeWriteInputStream(OutputStream out, InputStream rawStream,
-                                                            long offset) throws IOException;
-
-    static {
-        System.loadLibrary("media_jni");
-        nativeClassInit();
-    }
-}
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_DngCreator.cpp b/media/jni/android_media_DngCreator.cpp
deleted file mode 100644
index 860d896..0000000
--- a/media/jni/android_media_DngCreator.cpp
+++ /dev/null
@@ -1,772 +0,0 @@
-/*
- * 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_NDEBUG 0
-#define LOG_TAG "DngCreator_JNI"
-
-#include <system/camera_metadata.h>
-#include <camera/CameraMetadata.h>
-#include <img_utils/DngUtils.h>
-#include <img_utils/TagDefinitions.h>
-#include <img_utils/TiffIfd.h>
-#include <img_utils/TiffWriter.h>
-#include <img_utils/Output.h>
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-#include <utils/RefBase.h>
-#include <cutils/properties.h>
-
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
-
-#include <jni.h>
-#include <JNIHelp.h>
-
-using namespace android;
-using namespace img_utils;
-
-#define BAIL_IF_INVALID(expr, jnienv, tagId) \
-    if ((expr) != OK) { \
-        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
-                "Invalid metadata for tag %x", tagId); \
-        return; \
-    }
-
-#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
-    if (entry.count == 0) { \
-        jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
-                "Missing metadata fields for tag %x", tagId); \
-        return; \
-    }
-
-#define ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID     "mNativeContext"
-
-static struct {
-    jfieldID mNativeContext;
-} gDngCreatorClassInfo;
-
-static struct {
-    jmethodID mWriteMethod;
-} gOutputStreamClassInfo;
-
-enum {
-    BITS_PER_SAMPLE = 16,
-    BYTES_PER_SAMPLE = 2,
-    TIFF_IFD_0 = 0
-};
-
-// ----------------------------------------------------------------------------
-
-// This class is not intended to be used across JNI calls.
-class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
-public:
-    JniOutputStream(JNIEnv* env, jobject outStream);
-
-    virtual ~JniOutputStream();
-
-    status_t open();
-    status_t write(const uint8_t* buf, size_t offset, size_t count);
-    status_t close();
-private:
-    enum {
-        BYTE_ARRAY_LENGTH = 1024
-    };
-    jobject mOutputStream;
-    JNIEnv* mEnv;
-    jbyteArray mByteArray;
-};
-
-JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
-        mEnv(env) {
-    mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
-    if (mByteArray == NULL) {
-        jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
-    }
-}
-
-JniOutputStream::~JniOutputStream() {
-    mEnv->DeleteLocalRef(mByteArray);
-}
-
-status_t JniOutputStream::open() {
-    // Do nothing
-    return OK;
-}
-
-status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
-    while(count > 0) {
-        size_t len = BYTE_ARRAY_LENGTH;
-        len = (count > len) ? len : count;
-        mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
-
-        if (mEnv->ExceptionCheck()) {
-            return BAD_VALUE;
-        }
-
-        mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
-                0, len);
-
-        if (mEnv->ExceptionCheck()) {
-            return BAD_VALUE;
-        }
-
-        count -= len;
-        offset += len;
-    }
-    return OK;
-}
-
-status_t JniOutputStream::close() {
-    // Do nothing
-    return OK;
-}
-
-// ----------------------------------------------------------------------------
-
-extern "C" {
-
-static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
-    ALOGV("%s:", __FUNCTION__);
-    return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
-            gDngCreatorClassInfo.mNativeContext));
-}
-
-static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
-    ALOGV("%s:", __FUNCTION__);
-    TiffWriter* current = DngCreator_getCreator(env, thiz);
-    if (writer != NULL) {
-        writer->incStrong((void*) DngCreator_setCreator);
-    }
-    if (current) {
-        current->decStrong((void*) DngCreator_setCreator);
-    }
-    env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
-            reinterpret_cast<jlong>(writer.get()));
-}
-
-static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
-    ALOGV("%s:", __FUNCTION__);
-
-    gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
-            ANDROID_MEDIA_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);
-
-    jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
-    LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
-    gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
-    LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
-}
-
-static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
-        jobject resultsPtr) {
-    ALOGV("%s:", __FUNCTION__);
-    CameraMetadata characteristics;
-    CameraMetadata results;
-    if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
-         jniThrowException(env, "java/lang/AssertionError",
-                "No native metadata defined for camera characteristics.");
-         return;
-    }
-    if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
-        jniThrowException(env, "java/lang/AssertionError",
-                "No native metadata defined for capture results.");
-        return;
-    }
-
-    sp<TiffWriter> writer = new TiffWriter();
-
-    writer->addIfd(TIFF_IFD_0);
-
-    status_t err = OK;
-
-    const uint32_t samplesPerPixel = 1;
-    const uint32_t bitsPerSample = BITS_PER_SAMPLE;
-    const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
-    uint32_t imageWidth = 0;
-    uint32_t imageHeight = 0;
-
-    OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
-
-    // TODO: Greensplit.
-    // TODO: UniqueCameraModel
-    // TODO: Add remaining non-essential tags
-    {
-        // Set orientation
-        uint16_t orientation = 1; // Normal
-        BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
-                TAG_ORIENTATION);
-    }
-
-    {
-        // Set subfiletype
-        uint32_t subfileType = 0; // Main image
-        BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
-                TAG_NEWSUBFILETYPE);
-    }
-
-    {
-        // Set bits per sample
-        uint16_t bits = static_cast<uint16_t>(bitsPerSample);
-        BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
-                TAG_BITSPERSAMPLE);
-    }
-
-    {
-        // Set compression
-        uint16_t compression = 1; // None
-        BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
-                TAG_COMPRESSION);
-    }
-
-    {
-        // Set dimensions
-        camera_metadata_entry entry =
-                characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-        BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
-        uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
-        uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
-        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
-                TAG_IMAGEWIDTH);
-        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
-                TAG_IMAGELENGTH);
-        imageWidth = width;
-        imageHeight = height;
-    }
-
-    {
-        // Set photometric interpretation
-        uint16_t interpretation = 32803;
-        BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
-                TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
-    }
-
-    {
-        // Set blacklevel tags
-        camera_metadata_entry entry =
-                characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
-        BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
-        const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
-        BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
-                TAG_BLACKLEVEL);
-
-        uint16_t repeatDim[2] = {2, 2};
-        BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
-                TAG_BLACKLEVELREPEATDIM);
-    }
-
-    {
-        // Set samples per pixel
-        uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
-        BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
-                env, TAG_SAMPLESPERPIXEL);
-    }
-
-    {
-        // Set planar configuration
-        uint16_t config = 1; // Chunky
-        BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
-                env, TAG_PLANARCONFIGURATION);
-    }
-
-    {
-        // Set CFA pattern dimensions
-        uint16_t repeatDim[2] = {2, 2};
-        BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
-                env, TAG_CFAREPEATPATTERNDIM);
-    }
-
-    {
-        // Set CFA pattern
-        camera_metadata_entry entry =
-                        characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
-        BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
-        camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
-                static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
-                entry.data.u8[0]);
-        switch(cfa) {
-            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
-                uint8_t cfa[4] = {0, 1, 1, 2};
-                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
-                                                env, TAG_CFAPATTERN);
-                opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
-                break;
-            }
-            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
-                uint8_t cfa[4] = {1, 0, 2, 1};
-                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
-                                                env, TAG_CFAPATTERN);
-                opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
-                break;
-            }
-            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
-                uint8_t cfa[4] = {1, 2, 0, 1};
-                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
-                                                env, TAG_CFAPATTERN);
-                opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
-                break;
-            }
-            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
-                uint8_t cfa[4] = {2, 1, 1, 0};
-                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
-                                env, TAG_CFAPATTERN);
-                opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
-                break;
-            }
-            default: {
-                jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
-                            "Invalid metadata for tag %d", TAG_CFAPATTERN);
-                return;
-            }
-        }
-    }
-
-    {
-        // Set CFA plane color
-        uint8_t cfaPlaneColor[3] = {0, 1, 2};
-        BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
-                env, TAG_CFAPLANECOLOR);
-    }
-
-    {
-        // Set CFA layout
-        uint16_t cfaLayout = 1;
-        BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
-                env, TAG_CFALAYOUT);
-    }
-
-    {
-        // Set DNG version information
-        uint8_t version[4] = {1, 4, 0, 0};
-        BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
-                env, TAG_DNGVERSION);
-
-        uint8_t backwardVersion[4] = {1, 1, 0, 0};
-        BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
-                env, TAG_DNGBACKWARDVERSION);
-    }
-
-    {
-        // Set whitelevel
-        camera_metadata_entry entry =
-                characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
-        BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
-        uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
-        BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
-                TAG_WHITELEVEL);
-    }
-
-    {
-        // Set default scale
-        uint32_t defaultScale[4] = {1, 1, 1, 1};
-        BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
-                env, TAG_DEFAULTSCALE);
-    }
-
-    bool singleIlluminant = false;
-    {
-        // Set calibration illuminants
-        camera_metadata_entry entry1 =
-            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
-        BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
-        camera_metadata_entry entry2 =
-            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
-        if (entry2.count == 0) {
-            singleIlluminant = true;
-        }
-        uint16_t ref1 = entry1.data.u8[0];
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
-                TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
-
-        if (!singleIlluminant) {
-            uint16_t ref2 = entry2.data.u8[0];
-            BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
-                    TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
-        }
-    }
-
-    {
-        // Set color transforms
-        camera_metadata_entry entry1 =
-            characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
-        BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
-
-        int32_t colorTransform1[entry1.count * 2];
-
-        size_t ctr = 0;
-        for(size_t i = 0; i < entry1.count; ++i) {
-            colorTransform1[ctr++] = entry1.data.r[i].numerator;
-            colorTransform1[ctr++] = entry1.data.r[i].denominator;
-        }
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
-                env, TAG_COLORMATRIX1);
-
-        if (!singleIlluminant) {
-            camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
-            BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
-            int32_t colorTransform2[entry2.count * 2];
-
-            ctr = 0;
-            for(size_t i = 0; i < entry2.count; ++i) {
-                colorTransform2[ctr++] = entry2.data.r[i].numerator;
-                colorTransform2[ctr++] = entry2.data.r[i].denominator;
-            }
-
-            BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
-                    env, TAG_COLORMATRIX2);
-        }
-    }
-
-    {
-        // Set calibration transforms
-        camera_metadata_entry entry1 =
-            characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
-        BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
-
-        int32_t calibrationTransform1[entry1.count * 2];
-
-        size_t ctr = 0;
-        for(size_t i = 0; i < entry1.count; ++i) {
-            calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
-            calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
-        }
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
-                TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
-
-        if (!singleIlluminant) {
-            camera_metadata_entry entry2 =
-                characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
-            BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
-            int32_t calibrationTransform2[entry2.count * 2];
-
-            ctr = 0;
-            for(size_t i = 0; i < entry2.count; ++i) {
-                calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
-                calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
-            }
-
-            BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
-                    TIFF_IFD_0),  env, TAG_CAMERACALIBRATION2);
-        }
-    }
-
-    {
-        // Set forward transforms
-        camera_metadata_entry entry1 =
-            characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
-        BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
-
-        int32_t forwardTransform1[entry1.count * 2];
-
-        size_t ctr = 0;
-        for(size_t i = 0; i < entry1.count; ++i) {
-            forwardTransform1[ctr++] = entry1.data.r[i].numerator;
-            forwardTransform1[ctr++] = entry1.data.r[i].denominator;
-        }
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
-                TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
-
-        if (!singleIlluminant) {
-            camera_metadata_entry entry2 =
-                characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
-            BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
-            int32_t forwardTransform2[entry2.count * 2];
-
-            ctr = 0;
-            for(size_t i = 0; i < entry2.count; ++i) {
-                forwardTransform2[ctr++] = entry2.data.r[i].numerator;
-                forwardTransform2[ctr++] = entry2.data.r[i].denominator;
-            }
-
-            BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
-                    TIFF_IFD_0),  env, TAG_FORWARDMATRIX2);
-        }
-    }
-
-    {
-        // Set camera neutral
-        camera_metadata_entry entry =
-            results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
-        BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
-        uint32_t cameraNeutral[entry.count * 2];
-
-        size_t ctr = 0;
-        for(size_t i = 0; i < entry.count; ++i) {
-            cameraNeutral[ctr++] =
-                    static_cast<uint32_t>(entry.data.r[i].numerator);
-            cameraNeutral[ctr++] =
-                    static_cast<uint32_t>(entry.data.r[i].denominator);
-        }
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
-                TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
-    }
-
-    {
-        // Setup data strips
-        // TODO: Switch to tiled implementation.
-        uint32_t offset = 0;
-        BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
-                TAG_STRIPOFFSETS);
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
-                TAG_ROWSPERSTRIP);
-
-        uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
-                bitsPerByte;
-        BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
-                TAG_STRIPBYTECOUNTS);
-    }
-
-    {
-        // Setup default crop + crop origin tags
-        uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
-        uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
-        if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
-            uint32_t defaultCropOrigin[] = {margin, margin};
-            uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
-            BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
-                    TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
-            BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
-                    TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
-        }
-    }
-
-    {
-        // Setup unique camera model tag
-        char model[PROPERTY_VALUE_MAX];
-        property_get("ro.product.model", model, "");
-
-        char manufacturer[PROPERTY_VALUE_MAX];
-        property_get("ro.product.manufacturer", manufacturer, "");
-
-        char brand[PROPERTY_VALUE_MAX];
-        property_get("ro.product.brand", brand, "");
-
-        String8 cameraModel(model);
-        cameraModel += "-";
-        cameraModel += manufacturer;
-        cameraModel += "-";
-        cameraModel += brand;
-
-        BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
-                reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
-                TAG_UNIQUECAMERAMODEL);
-    }
-
-    {
-        // Setup opcode List 2
-        camera_metadata_entry entry1 =
-                characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
-        BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
-        uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
-        uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
-
-        camera_metadata_entry entry2 =
-                results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
-        BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
-        if (entry2.count == lsmWidth * lsmHeight * 4) {
-
-            OpcodeListBuilder builder;
-            status_t err = builder.addGainMapsForMetadata(lsmWidth,
-                                                          lsmHeight,
-                                                          0,
-                                                          0,
-                                                          imageHeight,
-                                                          imageWidth,
-                                                          opcodeCfaLayout,
-                                                          entry2.data.f);
-            if (err == OK) {
-                size_t listSize = builder.getSize();
-                uint8_t opcodeListBuf[listSize];
-                err = builder.buildOpList(opcodeListBuf);
-                if (err == OK) {
-                    BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
-                            TIFF_IFD_0), env, TAG_OPCODELIST2);
-                } else {
-                    ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
-                    jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
-                }
-            } else {
-                ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
-                jniThrowRuntimeException(env, "failed to add lens shading map.");
-            }
-        } else {
-            ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
-        }
-    }
-
-    DngCreator_setCreator(env, thiz, writer);
-}
-
-static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
-    ALOGV("%s:", __FUNCTION__);
-    DngCreator_setCreator(env, thiz, NULL);
-}
-
-static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
-    ALOGV("%s:", __FUNCTION__);
-    jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
-}
-
-static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
-    ALOGV("%s:", __FUNCTION__);
-    jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
-}
-
-static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
-        jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
-        jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
-    ALOGV("%s:", __FUNCTION__);
-    jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
-}
-
-static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
-        jint height, jobject inBuffer, jint rowStride, jint pixStride) {
-    ALOGV("%s:", __FUNCTION__);
-
-    sp<JniOutputStream> out = new JniOutputStream(env, outStream);
-    if(env->ExceptionCheck()) {
-        ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
-        return;
-    }
-
-    uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
-    if (pixelBytes == NULL) {
-        ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
-        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
-        return;
-    }
-
-    TiffWriter* writer = DngCreator_getCreator(env, thiz);
-    if (writer == NULL) {
-        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
-        jniThrowException(env, "java/lang/AssertionError",
-                "Write called with uninitialized DngCreator");
-        return;
-    }
-    // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
-    uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
-    uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
-    if (metadataWidth != width) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
-                        "Metadata width %d doesn't match image width %d", metadataWidth, width);
-        return;
-    }
-
-    if (metadataHeight != height) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
-                        "Metadata height %d doesn't match image height %d", metadataHeight, height);
-        return;
-    }
-
-    uint32_t stripOffset = writer->getTotalSize();
-
-    BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
-                    TAG_STRIPOFFSETS);
-
-    if (writer->write(out.get()) != OK) {
-        if (!env->ExceptionCheck()) {
-            jniThrowException(env, "java/io/IOException", "Failed to write metadata");
-        }
-        return;
-    }
-
-    size_t fullSize = rowStride * height;
-    jlong capacity = env->GetDirectBufferCapacity(inBuffer);
-    if (capacity < 0 || fullSize > capacity) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
-                "Invalid size %d for Image, size given in metadata is %d at current stride",
-                capacity, fullSize);
-        return;
-    }
-
-    if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
-        if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
-            if (!env->ExceptionCheck()) {
-                jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
-            }
-            return;
-        }
-    } else if (pixStride == BYTES_PER_SAMPLE) {
-        for (size_t i = 0; i < height; ++i) {
-            if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
-                        env->ExceptionCheck()) {
-                if (!env->ExceptionCheck()) {
-                    jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
-                }
-                return;
-            }
-        }
-    } else {
-        for (size_t i = 0; i < height; ++i) {
-            for (size_t j = 0; j < width; ++j) {
-                if (out->write(pixelBytes, i * rowStride + j * pixStride,
-                        BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
-                    if (env->ExceptionCheck()) {
-                        jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
-                    }
-                    return;
-                }
-            }
-        }
-    }
-
-}
-
-static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
-        jobject rawBuffer, jlong offset) {
-    ALOGV("%s:", __FUNCTION__);
-    jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
-}
-
-static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
-        jobject inStream, jlong offset) {
-    ALOGV("%s:", __FUNCTION__);
-    jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
-}
-
-} /*extern "C" */
-
-static JNINativeMethod gDngCreatorMethods[] = {
-    {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
-    {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
-            "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
-    {"nativeDestroy",           "()V",      (void*) DngCreator_destroy},
-    {"nativeSetOrientation",    "(I)V",     (void*) DngCreator_nativeSetOrientation},
-    {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
-            (void*) DngCreator_nativeSetThumbnailBitmap},
-    {"nativeSetThumbnailImage",
-            "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
-            (void*) DngCreator_nativeSetThumbnailImage},
-    {"nativeWriteImage",        "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
-            (void*) DngCreator_nativeWriteImage},
-    {"nativeWriteByteBuffer",    "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
-            (void*) DngCreator_nativeWriteByteBuffer},
-    {"nativeWriteInputStream",    "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
-            (void*) DngCreator_nativeWriteInputStream},
-};
-
-int register_android_media_DngCreator(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
-                   "android/media/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
-}
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..a3caba2 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;
@@ -46,6 +49,7 @@
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.List;
 
 /**
  * <pre>
@@ -56,6 +60,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 +948,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);
@@ -1046,7 +1054,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 +1082,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 +1108,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 +1166,31 @@
         }
     }
 
+    @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/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 2685447..d2bf30c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -112,7 +112,9 @@
     }
 
     public interface OnDismissAction {
-        /* returns true if the dismiss should be deferred */
+        /**
+         * @return true if the dismiss should be deferred
+         */
         boolean onDismiss();
     }
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6b62c25..e9cb197 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -52,6 +52,8 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+
 
     <!-- Physical hardware -->
     <uses-permission android:name="android.permission.MANAGE_USB" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2ec9935..d683162 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -61,6 +61,7 @@
             android:layout_height="match_parent"
             android:visibility="invisible"
             android:scrollbars="none"
+            android:overScrollMode="never"
             android:fillViewport="true">
             <LinearLayout
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c209434..e9dcea2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -246,7 +246,7 @@
     <dimen name="notification_side_padding">8dp</dimen>
 
     <!-- Z distance between notifications if they are in the stack -->
-    <dimen name="z_distance_between_notifications">2dp</dimen>
+    <dimen name="z_distance_between_notifications">1dp</dimen>
 
     <!-- The padding between the individual notification cards when dimmed. -->
     <dimen name="notification_padding_dimmed">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index c0f9bf2..191bac9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
 import android.provider.Settings.Global;
 
 import com.android.systemui.R;
@@ -52,10 +53,9 @@
     }
 
     private void setEnabled(boolean enabled) {
-        mSetting.setValue(enabled ? 1 : 0);
-        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        intent.putExtra("state", enabled);
-        mContext.sendBroadcast(intent);
+        final ConnectivityManager mgr =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mgr.setAirplaneMode(enabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index ac16164..e3dac4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -21,15 +21,25 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.RectF;
+import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
 import android.view.animation.PathInterpolator;
-
 import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -41,6 +51,36 @@
     private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
     private static final int ACTIVATE_ANIMATION_LENGTH = 220;
 
+    /**
+     * The amount of width, which is kept in the end when performing a disappear animation (also
+     * the amount from which the horizontal appearing begins)
+     */
+    private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
+
+    /**
+     * At which point from [0,1] does the horizontal collapse animation end (or start when
+     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
+     */
+    private static final float HORIZONTAL_ANIMATION_END = 0.2f;
+
+    /**
+     * At which point from [0,1] does the alpha animation end (or start when
+     * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
+     */
+    private static final float ALPHA_ANIMATION_END = 0.0f;
+
+    /**
+     * At which point from [0,1] does the horizontal collapse animation start (or start when
+     * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
+     */
+    private static final float HORIZONTAL_ANIMATION_START = 1.0f;
+
+    /**
+     * At which point from [0,1] does the vertical collapse animation start (or end when
+     * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
+     */
+    private static final float VERTICAL_ANIMATION_START = 1.0f;
+
     private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
             = new PathInterpolator(0.6f, 0, 0.5f, 1);
     private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
@@ -53,6 +93,7 @@
 
     private int mBgTint = 0;
     private int mDimmedBgTint = 0;
+    private final int mRoundedRectCornerRadius;
 
     /**
      * Flag to indicate that the notification has been touched once and the second touch will
@@ -66,22 +107,41 @@
 
     private OnActivatedListener mOnActivatedListener;
 
-    private Interpolator mLinearOutSlowInInterpolator;
-    private Interpolator mFastOutSlowInInterpolator;
+    private final Interpolator mLinearOutSlowInInterpolator;
+    private final Interpolator mFastOutSlowInInterpolator;
+    private final Interpolator mSlowOutFastInInterpolator;
+    private final Interpolator mSlowOutLinearInInterpolator;
+    private final Interpolator mLinearInterpolator;
+    private Interpolator mCurrentAppearInterpolator;
+    private Interpolator mCurrentAlphaInterpolator;
 
     private NotificationBackgroundView mBackgroundNormal;
     private NotificationBackgroundView mBackgroundDimmed;
     private ObjectAnimator mBackgroundAnimator;
+    private RectF mAppearAnimationRect = new RectF();
+    private PorterDuffColorFilter mAppearAnimationFilter;
+    private float mAnimationTranslationY;
+    private boolean mDrawingAppearAnimation;
+    private Paint mAppearPaint = new Paint();
+    private ValueAnimator mAppearAnimator;
+    private float mAppearAnimationFraction = -1.0f;
+    private float mAppearAnimationTranslation;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mFastOutSlowInInterpolator =
                 AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+        mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
         mLinearOutSlowInInterpolator =
                 AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
+        mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
+        mLinearInterpolator = new LinearInterpolator();
         setClipChildren(false);
         setClipToPadding(false);
+        mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
+        mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
     @Override
@@ -316,6 +376,202 @@
         mBackgroundDimmed.setClipTopAmount(clipTopAmount);
     }
 
+    @Override
+    public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+        enableAppearDrawing(true);
+        if (mDrawingAppearAnimation) {
+            startAppearAnimation(false /* isAppearing */, translationDirection,
+                    0, onFinishedRunnable);
+        }
+    }
+
+    @Override
+    public void performAddAnimation(long delay) {
+        enableAppearDrawing(true);
+        if (mDrawingAppearAnimation) {
+            startAppearAnimation(true /* isAppearing */, -1.0f, delay, null);
+        }
+    }
+
+    private void startAppearAnimation(boolean isAppearing,
+            float translationDirection, long delay, final Runnable onFinishedRunnable) {
+        if (mAppearAnimator != null) {
+            mAppearAnimator.cancel();
+        }
+        mAnimationTranslationY = translationDirection * mActualHeight;
+        if (mAppearAnimationFraction == -1.0f) {
+            // not initialized yet, we start anew
+            if (isAppearing) {
+                mAppearAnimationFraction = 0.0f;
+                mAppearAnimationTranslation = mAnimationTranslationY;
+            } else {
+                mAppearAnimationFraction = 1.0f;
+                mAppearAnimationTranslation = 0;
+            }
+        }
+
+        float targetValue;
+        if (isAppearing) {
+            mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
+            mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator;
+            targetValue = 1.0f;
+        } else {
+            mCurrentAppearInterpolator = mFastOutSlowInInterpolator;
+            mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
+            targetValue = 0.0f;
+        }
+        mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
+                targetValue);
+        mAppearAnimator.setInterpolator(mLinearInterpolator);
+        mAppearAnimator.setDuration(
+                (long) (StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR
+                        * Math.abs(mAppearAnimationFraction - targetValue)));
+        mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mAppearAnimationFraction = (float) animation.getAnimatedValue();
+                updateAppearAnimationAlpha();
+                updateAppearRect();
+                invalidate();
+            }
+        });
+        if (delay > 0) {
+            // we need to apply the initial state already to avoid drawn frames in the wrong state
+            updateAppearAnimationAlpha();
+            updateAppearRect();
+            mAppearAnimator.setStartDelay(delay);
+        }
+        mAppearAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onFinishedRunnable != null) {
+                    onFinishedRunnable.run();
+                }
+                if (!mWasCancelled) {
+                    mAppearAnimationFraction = -1;
+                    setOutlineRect(null);
+                    enableAppearDrawing(false);
+                }
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+        });
+        mAppearAnimator.start();
+    }
+
+    private void updateAppearRect() {
+        float inverseFraction = (1.0f - mAppearAnimationFraction);
+        float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
+        float translateYTotalAmount = translationFraction * mAnimationTranslationY;
+        mAppearAnimationTranslation = translateYTotalAmount;
+
+        // handle width animation
+        float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
+                / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
+        widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
+        widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
+        float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
+                widthFraction);
+        float right = getWidth() - left;
+
+        // handle top animation
+        float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
+                VERTICAL_ANIMATION_START;
+        heightFraction = Math.max(0.0f, heightFraction);
+        heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
+
+        float top;
+        float bottom;
+        if (mAnimationTranslationY > 0.0f) {
+            bottom = mActualHeight - heightFraction * mAnimationTranslationY * 0.1f
+                    - translateYTotalAmount;
+            top = bottom * heightFraction;
+        } else {
+            top = heightFraction * (mActualHeight + mAnimationTranslationY) * 0.1f -
+                    translateYTotalAmount;
+            bottom = mActualHeight * (1 - heightFraction) + top * heightFraction;
+        }
+        mAppearAnimationRect.set(left, top, right, bottom);
+        setOutlineRect(left, top + mAppearAnimationTranslation, right,
+                bottom + mAppearAnimationTranslation);
+    }
+
+    private void updateAppearAnimationAlpha() {
+        int backgroundColor = getBackgroundColor();
+        if (backgroundColor != -1) {
+            float contentAlphaProgress = mAppearAnimationFraction;
+            contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
+            contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
+            contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
+            int sourceColor = Color.argb((int) (255 * (1.0f - contentAlphaProgress)),
+                    Color.red(backgroundColor), Color.green(backgroundColor),
+                    Color.blue(backgroundColor));
+            mAppearAnimationFilter.setColor(sourceColor);
+            mAppearPaint.setColorFilter(mAppearAnimationFilter);
+        }
+    }
+
+    private int getBackgroundColor() {
+        // TODO: get real color
+        return 0xfffafafa;
+    }
+
+    /**
+     * When we draw the appear animation, we render the view in a bitmap and render this bitmap
+     * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
+     * such that the normal drawing of the views does not happen anymore.
+     *
+     * @param enable Should it be enabled.
+     */
+    private void enableAppearDrawing(boolean enable) {
+        if (enable != mDrawingAppearAnimation) {
+            if (enable) {
+                if (getWidth() == 0 || getActualHeight() == 0) {
+                    // TODO: This should not happen, but it can during expansion. Needs
+                    // investigation
+                    return;
+                }
+                Bitmap bitmap = Bitmap.createBitmap(getWidth(), getActualHeight(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas canvas = new Canvas(bitmap);
+                draw(canvas);
+                mAppearPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP,
+                        Shader.TileMode.CLAMP));
+            } else {
+                mAppearPaint.setShader(null);
+            }
+            mDrawingAppearAnimation = enable;
+            invalidate();
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (!mDrawingAppearAnimation) {
+            super.dispatchDraw(canvas);
+        } else {
+            drawAppearRect(canvas);
+        }
+    }
+
+    private void drawAppearRect(Canvas canvas) {
+        canvas.save();
+        canvas.translate(0, mAppearAnimationTranslation);
+        canvas.drawRoundRect(mAppearAnimationRect, mRoundedRectCornerRadius,
+                mRoundedRectCornerRadius, mAppearPaint);
+        canvas.restore();
+    }
+
     public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
         mOnActivatedListener = onActivatedListener;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 457d32e..f4db625 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -22,6 +22,7 @@
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -46,6 +47,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
@@ -79,13 +81,17 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Locale;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
+    private static final boolean USE_NOTIFICATION_LISTENER = false;
 
     protected static final int MSG_SHOW_RECENT_APPS = 1019;
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -158,6 +164,7 @@
 
     protected WindowManager mWindowManager;
     protected IWindowManager mWindowManagerService;
+
     protected abstract void refreshLayout(int layoutDirection);
 
     protected Display mDisplay;
@@ -208,33 +215,47 @@
 
     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
         @Override
-        public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+        public boolean onClickHandler(
+                final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
             if (DEBUG) {
                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
             }
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
-                try {
-                    // The intent we are sending is for the application, which
-                    // won't have permission to immediately start an activity after
-                    // the user switches to home.  We know it is safe to do at this
-                    // point, so make sure new activity switches are now allowed.
-                    ActivityManagerNative.getDefault().resumeAppSwitches();
-                    // Also, notifications can be launched from the lock screen,
-                    // so dismiss the lock screen when the activity starts.
-                    ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-                } catch (RemoteException e) {
-                }
-            }
+                startNotificationActivity(new OnDismissAction() {
+                    @Override
+                    public boolean onDismiss() {
+                        try {
+                            // The intent we are sending is for the application, which
+                            // won't have permission to immediately start an activity after
+                            // the user switches to home.  We know it is safe to do at this
+                            // point, so make sure new activity switches are now allowed.
+                            ActivityManagerNative.getDefault().resumeAppSwitches();
+                            // Also, notifications can be launched from the lock screen,
+                            // so dismiss the lock screen when the activity starts.
+                            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                        } catch (RemoteException e) {
+                        }
 
-            boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
+                        boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
 
-            if (isActivity && handled) {
-                // close the shade if it was open
-                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                visibilityChanged(false);
+                        // close the shade if it was open
+                        if (handled) {
+                            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                            visibilityChanged(false);
+                        }
+                        return handled; // Wait for activity start.
+                    }
+                });
+                return true;
+            } else {
+                return super.onClickHandler(view, pendingIntent, fillInIntent);
             }
-            return handled;
+        }
+
+        private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
+                Intent fillInIntent) {
+            return super.onClickHandler(view, pendingIntent, fillInIntent);
         }
     };
 
@@ -253,6 +274,49 @@
         }
     };
 
+    private final NotificationListenerService mNotificationListener =
+            new NotificationListenerService() {
+        @Override
+        public void onListenerConnected() {
+            if (DEBUG) Log.d(TAG, "onListenerConnected");
+            final StatusBarNotification[] notifications = getActiveNotifications();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    for (StatusBarNotification sbn : notifications) {
+                        addNotificationInternal(sbn);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onNotificationPosted(final StatusBarNotification sbn) {
+            if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mNotificationData.findByKey(sbn.getKey()) != null) {
+                        updateNotificationInternal(sbn);
+                    } else {
+                        addNotificationInternal(sbn);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onNotificationRemoved(final StatusBarNotification sbn) {
+            if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    removeNotificationInternal(sbn.getKey());
+                }
+            });
+        }
+    };
+
     private void updateCurrentProfilesCache() {
         synchronized (mCurrentProfiles) {
             mCurrentProfiles.clear();
@@ -299,14 +363,13 @@
 
         // Connect in to the status bar manager service
         StatusBarIconList iconList = new StatusBarIconList();
-        ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
         ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
         mCommandQueue = new CommandQueue(this, iconList);
 
         int[] switches = new int[8];
         ArrayList<IBinder> binders = new ArrayList<IBinder>();
         try {
-            mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
+            mBarService.registerStatusBar(mCommandQueue, iconList, notifications,
                     switches, binders);
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
@@ -332,17 +395,23 @@
             }
         }
 
-        // Set up the initial notification state
-        N = notificationKeys.size();
-        if (N == notifications.size()) {
-            for (int i=0; i<N; i++) {
-                addNotification(notificationKeys.get(i), notifications.get(i));
+        // Set up the initial notification state.
+        if (USE_NOTIFICATION_LISTENER) {
+            try {
+                mNotificationListener.registerAsSystemService(
+                        new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
+                        UserHandle.USER_ALL);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to register notification listener", e);
             }
         } else {
-            Log.wtf(TAG, "Notification list length mismatch: keys=" + N
-                    + " notifications=" + notifications.size());
+            N = notifications.size();
+            for (int i=0; i<N; i++) {
+                addNotification(notifications.get(i));
+            }
         }
 
+
         if (DEBUG) {
             Log.d(TAG, String.format(
                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
@@ -381,6 +450,14 @@
         }
     }
 
+    /**
+     * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
+     * @param action A dismiss action that is called if it's safe to start the activity.
+     */
+    protected void startNotificationActivity(OnDismissAction action) {
+        action.onDismiss();
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         final Locale locale = mContext.getResources().getConfiguration().locale;
@@ -946,47 +1023,55 @@
             mIsHeadsUp = forHun;
         }
 
-        public void onClick(View v) {
-            try {
-                // The intent we are sending is for the application, which
-                // won't have permission to immediately start an activity after
-                // the user switches to home.  We know it is safe to do at this
-                // point, so make sure new activity switches are now allowed.
-                ActivityManagerNative.getDefault().resumeAppSwitches();
-                // Also, notifications can be launched from the lock screen,
-                // so dismiss the lock screen when the activity starts.
-                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-            } catch (RemoteException e) {
-            }
+        public void onClick(final View v) {
+            startNotificationActivity(new OnDismissAction() {
+                public boolean onDismiss() {
+                    try {
+                        // The intent we are sending is for the application, which
+                        // won't have permission to immediately start an activity after
+                        // the user switches to home.  We know it is safe to do at this
+                        // point, so make sure new activity switches are now allowed.
+                        ActivityManagerNative.getDefault().resumeAppSwitches();
+                        // Also, notifications can be launched from the lock screen,
+                        // so dismiss the lock screen when the activity starts.
+                        ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                    } catch (RemoteException e) {
+                    }
 
-            if (mIntent != null) {
-                int[] pos = new int[2];
-                v.getLocationOnScreen(pos);
-                Intent overlay = new Intent();
-                overlay.setSourceBounds(
-                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
-                try {
-                    mIntent.send(mContext, 0, overlay);
-                } catch (PendingIntent.CanceledException e) {
-                    // the stack trace isn't very helpful here.  Just log the exception message.
-                    Log.w(TAG, "Sending contentIntent failed: " + e);
+                    boolean sent = false;
+                    if (mIntent != null) {
+                        int[] pos = new int[2];
+                        v.getLocationOnScreen(pos);
+                        Intent overlay = new Intent();
+                        overlay.setSourceBounds(new Rect(pos[0], pos[1],
+                                pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                        try {
+                            mIntent.send(mContext, 0, overlay);
+                            sent = true;
+                        } catch (PendingIntent.CanceledException e) {
+                            // the stack trace isn't very helpful here.
+                            // Just log the exception message.
+                            Log.w(TAG, "Sending contentIntent failed: " + e);
+                        }
+                    }
+
+                    try {
+                        if (mIsHeadsUp) {
+                            mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+                        }
+                        mBarService.onNotificationClick(mNotificationKey);
+                    } catch (RemoteException ex) {
+                        // system process is dead if we're here.
+                    }
+
+                    // close the shade if it was open
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                    visibilityChanged(false);
+
+                    boolean waitForActivityLaunch = sent && mIntent.isActivity();
+                    return waitForActivityLaunch;
                 }
-
-                KeyguardTouchDelegate.getInstance(mContext).dismiss();
-            }
-
-            try {
-                if (mIsHeadsUp) {
-                    mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
-                }
-                mBarService.onNotificationClick(mNotificationKey);
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-
-            // close the shade if it was open
-            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-            visibilityChanged(false);
+            });
         }
     }
 
@@ -1018,8 +1103,8 @@
      *
      * WARNING: this will call back into us.  Don't hold any locks.
      */
-    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
-        removeNotification(key);
+    void handleNotificationError(StatusBarNotification n, String message) {
+        removeNotification(n.getKey());
         try {
             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
                     n.getInitialPid(), message, n.getUserId());
@@ -1028,7 +1113,7 @@
         }
     }
 
-    protected StatusBarNotification removeNotificationViews(IBinder key) {
+    protected StatusBarNotification removeNotificationViews(String key) {
         NotificationData.Entry entry = mNotificationData.remove(key);
         if (entry == null) {
             Log.w(TAG, "removeNotification for unknown key: " + key);
@@ -1039,14 +1124,14 @@
         if (rowParent != null) rowParent.removeView(entry.row);
         updateRowStates();
         updateNotificationIcons();
+        updateSpeedBump();
 
         return entry.notification;
     }
 
-    protected NotificationData.Entry createNotificationViews(IBinder key,
-            StatusBarNotification notification) {
+    protected NotificationData.Entry createNotificationViews(StatusBarNotification notification) {
         if (DEBUG) {
-            Log.d(TAG, "createNotificationViews(key=" + key + ", notification=" + notification);
+            Log.d(TAG, "createNotificationViews(notification=" + notification);
         }
         // Construct the icon.
         final StatusBarIconView iconView = new StatusBarIconView(mContext,
@@ -1061,13 +1146,13 @@
                     notification.getNotification().number,
                     notification.getNotification().tickerText);
         if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
+            handleNotificationError(notification, "Couldn't create icon: " + ic);
             return null;
         }
         // Construct the expanded view.
-        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
+        NotificationData.Entry entry = new NotificationData.Entry(notification, iconView);
         if (!inflateViews(entry, mStackScroller)) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+            handleNotificationError(notification, "Couldn't expand RemoteViews for: "
                     + notification);
             return null;
         }
@@ -1083,12 +1168,26 @@
         if (DEBUG) {
             Log.d(TAG, "addNotificationViews: added at " + pos);
         }
-        updateNotificationIcons();
         updateRowStates();
+        updateNotificationIcons();
+        updateSpeedBump();
     }
 
-    private void addNotificationViews(IBinder key, StatusBarNotification notification) {
-        addNotificationViews(createNotificationViews(key, notification));
+    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));
     }
 
     /**
@@ -1104,7 +1203,6 @@
         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
         int n = mNotificationData.size();
         int visibleNotifications = 0;
-        int speedBumpIndex = -1;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
         for (int i = n-1; i >= 0; i--) {
             NotificationData.Entry entry = mNotificationData.get(i);
@@ -1125,17 +1223,14 @@
                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
                 }
             } else {
-                if (entry.row.getVisibility() == View.GONE) {
+                boolean wasGone = entry.row.getVisibility() == View.GONE;
+                entry.row.setVisibility(View.VISIBLE);
+                if (wasGone) {
                     // notify the scroller of a child addition
                     mStackScroller.generateAddAnimation(entry.row);
                 }
-                entry.row.setVisibility(View.VISIBLE);
                 visibleNotifications++;
             }
-            if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
-                    && entry.row.isBelowSpeedBump() ) {
-                speedBumpIndex = n - 1 - i;
-            }
         }
 
         if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
@@ -1143,8 +1238,6 @@
         } else {
             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
         }
-
-        mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
     }
 
     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1160,7 +1253,7 @@
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
     protected abstract void updateNotificationIcons();
-    protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
+    protected abstract void tick(StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
     protected abstract boolean shouldDisableNavbarGestures();
 
@@ -1168,12 +1261,37 @@
         return parent != null && parent.indexOfChild(entry.row) == 0;
     }
 
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
 
-        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
+    @Override
+    public void addNotification(StatusBarNotification notification) {
+        if (!USE_NOTIFICATION_LISTENER) {
+            addNotificationInternal(notification);
+        }
+    }
+
+    public abstract void addNotificationInternal(StatusBarNotification notification);
+
+    @Override
+    public void removeNotification(String key) {
+        if (!USE_NOTIFICATION_LISTENER) {
+            removeNotificationInternal(key);
+        }
+    }
+
+    protected abstract void removeNotificationInternal(String key);
+
+    public void updateNotification(StatusBarNotification notification) {
+        if (!USE_NOTIFICATION_LISTENER) {
+            updateNotificationInternal(notification);
+        }
+    }
+
+    public void updateNotificationInternal(StatusBarNotification notification) {
+        if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
+
+        final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
         if (oldEntry == null) {
-            Log.w(TAG, "updateNotification for unknown key: " + key);
+            Log.w(TAG, "updateNotification for unknown key: " + notification.getKey());
             return;
         }
 
@@ -1244,15 +1362,15 @@
         boolean orderUnchanged =
                    notification.getNotification().when == oldNotification.getNotification().when
                 && notification.getScore() == oldNotification.getScore();
-                // score now encompasses/supersedes isOngoing()
+        // score now encompasses/supersedes isOngoing()
 
         boolean updateTicker = notification.getNotification().tickerText != null
                 && !TextUtils.equals(notification.getNotification().tickerText,
-                        oldEntry.notification.getNotification().tickerText);
+                oldEntry.notification.getNotification().tickerText);
         boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
         if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
                 && (orderUnchanged || isTopAnyway)) {
-            if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
+            if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
             oldEntry.notification = notification;
             try {
                 updateNotificationViews(oldEntry, notification);
@@ -1276,25 +1394,27 @@
                         notification.getNotification().number,
                         notification.getNotification().tickerText);
                 if (!oldEntry.icon.set(ic)) {
-                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+                    handleNotificationError(notification, "Couldn't update icon: " + ic);
                     return;
                 }
                 updateRowStates();
+                updateSpeedBump();
             }
             catch (RuntimeException e) {
                 // It failed to add cleanly.  Log, and remove the view from the panel.
                 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(key);
-                addNotificationViews(key, notification);
+                removeNotificationViews(notification.getKey());
+                addNotificationViews(notification);
             }
         } else {
-            if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
+            if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
             if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
             if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
             if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
-            removeNotificationViews(key);
-            addNotificationViews(key, notification);  // will also replace the heads up
-            final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
+            removeNotificationViews(notification.getKey());
+            addNotificationViews(notification);  // will also replace the heads up
+            final NotificationData.Entry newEntry = mNotificationData.findByKey(
+                    notification.getKey());
             final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
             if (userChangedExpansion) {
                 boolean userExpanded = oldEntry.row.isUserExpanded();
@@ -1314,7 +1434,7 @@
         // Restart the ticker if it's still running
         if (updateTicker && isForCurrentUser) {
             haltTicker();
-            tick(key, notification, false);
+            tick(notification, false);
         }
 
         // Recalculate the position of the sliding windows and the titles.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index b4a347b..aaeadb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,7 +21,6 @@
 import android.os.Message;
 import android.service.notification.StatusBarNotification;
 
-import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
@@ -73,11 +72,6 @@
     private Callbacks mCallbacks;
     private Handler mHandler = new H();
 
-    private class NotificationQueueEntry {
-        IBinder key;
-        StatusBarNotification notification;
-    }
-
     /**
      * These methods are called back on the main thread.
      */
@@ -86,9 +80,9 @@
         public void updateIcon(String slot, int index, int viewIndex,
                 StatusBarIcon old, StatusBarIcon icon);
         public void removeIcon(String slot, int index, int viewIndex);
-        public void addNotification(IBinder key, StatusBarNotification notification);
-        public void updateNotification(IBinder key, StatusBarNotification notification);
-        public void removeNotification(IBinder key);
+        public void addNotification(StatusBarNotification notification);
+        public void updateNotification(StatusBarNotification notification);
+        public void removeNotification(String key);
         public void disable(int state);
         public void animateExpandNotificationsPanel();
         public void animateCollapsePanels(int flags);
@@ -106,7 +100,6 @@
         public void showSearchPanel();
         public void hideSearchPanel();
         public void setWindowState(int window, int state);
-
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -130,25 +123,21 @@
         }
     }
 
-    public void addNotification(IBinder key, StatusBarNotification notification) {
+    @Override
+    public void addNotification(StatusBarNotification notification) {
         synchronized (mList) {
-            NotificationQueueEntry ne = new NotificationQueueEntry();
-            ne.key = key;
-            ne.notification = notification;
-            mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, ne).sendToTarget();
+            mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, notification).sendToTarget();
         }
     }
 
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
+    @Override
+    public void updateNotification(StatusBarNotification notification) {
         synchronized (mList) {
-            NotificationQueueEntry ne = new NotificationQueueEntry();
-            ne.key = key;
-            ne.notification = notification;
-            mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, ne).sendToTarget();
+            mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, notification).sendToTarget();
         }
     }
 
-    public void removeNotification(IBinder key) {
+    public void removeNotification(String key) {
         synchronized (mList) {
             mHandler.obtainMessage(MSG_REMOVE_NOTIFICATION, 0, 0, key).sendToTarget();
         }
@@ -291,17 +280,15 @@
                     break;
                 }
                 case MSG_ADD_NOTIFICATION: {
-                    final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj;
-                    mCallbacks.addNotification(ne.key, ne.notification);
+                    mCallbacks.addNotification((StatusBarNotification) msg.obj);
                     break;
                 }
                 case MSG_UPDATE_NOTIFICATION: {
-                    final NotificationQueueEntry ne = (NotificationQueueEntry)msg.obj;
-                    mCallbacks.updateNotification(ne.key, ne.notification);
+                    mCallbacks.updateNotification((StatusBarNotification) msg.obj);
                     break;
                 }
                 case MSG_REMOVE_NOTIFICATION: {
-                    mCallbacks.removeNotification((IBinder)msg.obj);
+                    mCallbacks.removeNotification((String) msg.obj);
                     break;
                 }
                 case MSG_DISABLE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index a42c194..843db04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 import android.graphics.Outline;
+import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.widget.FrameLayout;
 
 /**
  * Like {@link ExpandableView}, but setting an outline for the height and clipping.
@@ -27,9 +27,12 @@
 public abstract class ExpandableOutlineView extends ExpandableView {
 
     private final Outline mOutline = new Outline();
+    private boolean mCustomOutline;
+    private float mDensity;
 
     public ExpandableOutlineView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mDensity = getResources().getDisplayMetrics().density;
     }
 
     @Override
@@ -50,11 +53,37 @@
         updateOutline();
     }
 
-    private void updateOutline() {
-        mOutline.setRect(0,
-                mClipTopAmount,
-                getWidth(),
-                Math.max(mActualHeight, mClipTopAmount));
+    protected void setOutlineRect(RectF rect) {
+        if (rect != null) {
+            setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
+        } else {
+            mCustomOutline = false;
+            updateOutline();
+        }
+    }
+
+    protected void setOutlineRect(float left, float top, float right, float bottom) {
+        mCustomOutline = true;
+
+        int rectLeft = (int) left;
+        int rectTop = (int) top;
+        int rectRight = (int) right;
+        int rectBottom = (int) bottom;
+
+        // Outlines need to be at least 1 dp
+        rectBottom = (int) Math.max(top + mDensity, rectBottom);
+        rectRight = (int) Math.max(left + mDensity, rectRight);
+        mOutline.setRect(rectLeft, rectTop, rectRight, rectBottom);
         setOutline(mOutline);
     }
+
+    private void updateOutline() {
+        if (!mCustomOutline) {
+            mOutline.setRect(0,
+                    mClipTopAmount,
+                    getWidth(),
+                    Math.max(mActualHeight, mClipTopAmount));
+            setOutline(mOutline);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index eaaac10..088f076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -205,6 +205,21 @@
     }
 
     /**
+     * Perform a remove animation on this view.
+     *
+     * @param translationDirection The direction value from [-1 ... 1] indicating in which the
+     *                             animation should be performed. A value of -1 means that The
+     *                             remove animation should be performed upwards,
+     *                             such that the  child appears to be going away to the top. 1
+     *                             Should mean the opposite.
+     * @param onFinishedRunnable A runnable which should be run when the animation is finished.
+     */
+    public abstract void performRemoveAnimation(float translationDirection,
+            Runnable onFinishedRunnable);
+
+    public abstract void performAddAnimation(long delay);
+
+    /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
     public interface OnHeightChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 8440b9f..0555879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -18,8 +18,6 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.os.Binder;
-import android.os.IBinder;
 import android.os.Process;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
@@ -33,13 +31,14 @@
 public class InterceptedNotifications {
     private static final String TAG = "InterceptedNotifications";
     private static final String EXTRA_INTERCEPT = "android.intercept";
+    private static final String SYNTHETIC_KEY = "InterceptedNotifications.SYNTHETIC_KEY";
 
     private final Context mContext;
     private final PhoneStatusBar mBar;
-    private final ArrayMap<IBinder, StatusBarNotification> mIntercepted
-            = new ArrayMap<IBinder, StatusBarNotification>();
+    private final ArrayMap<String, StatusBarNotification> mIntercepted
+            = new ArrayMap<String, StatusBarNotification>();
 
-    private Binder mSynKey;
+    private String mSynKey;
 
     public InterceptedNotifications(Context context, PhoneStatusBar bar) {
         mContext = context;
@@ -49,36 +48,35 @@
     public void releaseIntercepted() {
         final int n = mIntercepted.size();
         for (int i = 0; i < n; i++) {
-            final IBinder key = mIntercepted.keyAt(i);
             final StatusBarNotification sbn = mIntercepted.valueAt(i);
             sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
-            mBar.addNotification(key, sbn);
+            mBar.addNotificationInternal(sbn);
         }
         mIntercepted.clear();
         updateSyntheticNotification();
     }
 
-    public boolean tryIntercept(IBinder key, StatusBarNotification notification) {
+    public boolean tryIntercept(StatusBarNotification notification) {
         if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false;
         if (shouldDisplayIntercepted()) return false;
-        mIntercepted.put(key, notification);
+        mIntercepted.put(notification.getKey(), notification);
         updateSyntheticNotification();
         return true;
     }
 
-    public void remove(IBinder key) {
+    public void remove(String key) {
         if (mIntercepted.remove(key) != null) {
             updateSyntheticNotification();
         }
     }
 
     public boolean isSyntheticEntry(Entry ent) {
-        return mSynKey != null && ent.key.equals(mSynKey);
+        return ent.key.equals(SYNTHETIC_KEY);
     }
 
-    public void update(IBinder key, StatusBarNotification notification) {
-        if (mIntercepted.containsKey(key)) {
-            mIntercepted.put(key, notification);
+    public void update(StatusBarNotification notification) {
+        if (mIntercepted.containsKey(notification.getKey())) {
+            mIntercepted.put(notification.getKey(), notification);
         }
     }
 
@@ -90,7 +88,7 @@
     private void updateSyntheticNotification() {
         if (mIntercepted.isEmpty()) {
             if (mSynKey != null) {
-                mBar.removeNotification(mSynKey);
+                mBar.removeNotificationInternal(mSynKey);
                 mSynKey = null;
             }
             return;
@@ -108,10 +106,10 @@
                 TAG.hashCode(), TAG, Process.myUid(), Process.myPid(), 0, n,
                 mBar.getCurrentUserHandle());
         if (mSynKey == null) {
-            mSynKey = new Binder();
-            mBar.addNotification(mSynKey, sbn);
+            mSynKey = sbn.getKey();
+            mBar.addNotificationInternal(sbn);
         } else {
-           mBar.updateNotification(mSynKey, sbn);
+           mBar.updateNotificationInternal(sbn);
         }
         final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
         entry.row.setOnClickListener(mSynClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index 3c080fe..1c2ca91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -34,7 +34,6 @@
 
     public NotificationBackgroundView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        setWillNotDraw(false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index b1a5750..5696246 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import android.os.IBinder;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.widget.ImageView;
@@ -29,7 +28,7 @@
  */
 public class NotificationData {
     public static final class Entry {
-        public IBinder key;
+        public String key;
         public StatusBarNotification notification;
         public StatusBarIconView icon;
         public ExpandableNotificationRow row; // the outer expanded view
@@ -39,8 +38,8 @@
         public View expandedBig;
         private boolean interruption;
         public Entry() {}
-        public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
-            this.key = key;
+        public Entry(StatusBarNotification n, StatusBarIconView ic) {
+            this.key = n.getKey();
             this.notification = n;
             this.icon = ic;
         }
@@ -63,6 +62,7 @@
             interruption = true;
         }
     }
+
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
     private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
         // sort first by score, then by when
@@ -88,9 +88,9 @@
         return mEntries.get(i);
     }
 
-    public Entry findByKey(IBinder key) {
+    public Entry findByKey(String key) {
         for (Entry e : mEntries) {
-            if (e.key == key) {
+            if (e.key.equals(key)) {
                 return e;
             }
         }
@@ -100,7 +100,7 @@
     public int add(Entry entry) {
         int i;
         int N = mEntries.size();
-        for (i=0; i<N; i++) {
+        for (i = 0; i < N; i++) {
             if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
                 break;
             }
@@ -109,7 +109,7 @@
         return i;
     }
 
-    public Entry remove(IBinder key) {
+    public Entry remove(String key) {
         Entry e = findByKey(key);
         if (e != null) {
             mEntries.remove(e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index 8ae503a..a2f8991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -184,7 +184,7 @@
     }
 
     public void performVisibilityAnimation(boolean nowVisible) {
-        animateDivider(nowVisible);
+        animateDivider(nowVisible, null /* onFinishedRunnable */);
 
         // Animate explanation Text
         if (mIsExpanded) {
@@ -192,7 +192,14 @@
         }
     }
 
-    public void animateDivider(boolean nowVisible) {
+    /**
+     * Animate the divider to a new visibility.
+     *
+     * @param nowVisible should it now be visible
+     * @param onFinishedRunnable A runnable which should be run when the animation is
+     *        finished.
+     */
+    public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) {
         if (nowVisible != mDividerVisible) {
             // Animate dividers
             float endValue = nowVisible ? 1.0f : 0.0f;
@@ -204,7 +211,8 @@
                     .scaleX(endValue)
                     .scaleY(endValue)
                     .translationX(endTranslationXLeft)
-                    .setInterpolator(mFastOutSlowInInterpolator);
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .withEndAction(onFinishedRunnable);
             mLineRight.animate()
                     .alpha(endValue)
                     .withLayer()
@@ -216,6 +224,10 @@
             // Animate dots
             mDots.performVisibilityAnimation(nowVisible);
             mDividerVisible = nowVisible;
+        } else {
+            if (onFinishedRunnable != null) {
+                onFinishedRunnable.run();
+            }
         }
     }
 
@@ -250,6 +262,16 @@
         }
     }
 
+    @Override
+    public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
+        performVisibilityAnimation(false);
+    }
+
+    @Override
+    public void performAddAnimation(long delay) {
+        performVisibilityAnimation(true);
+    }
+
     private void resetExplanationText() {
         mExplanationText.setTranslationY(0);
         mExplanationText.setVisibility(INVISIBLE);
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 6336cf7..2fa2a00 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.*;
 
 /**
@@ -68,6 +69,12 @@
         }
     }
 
+    public void showWithDismissAction(OnDismissAction r) {
+        ensureView();
+        mKeyguardView.setOnDismissAction(r);
+        show();
+    }
+
     public void hide() {
         if (mKeyguardView != null) {
             mKeyguardView.cleanUp();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
new file mode 100644
index 0000000..6a83a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.res.Resources;
+import android.graphics.Path;
+import android.view.animation.PathInterpolator;
+
+import com.android.systemui.R;
+
+/**
+ * Utility class to calculate the clock position and top padding of notifications on Keyguard.
+ */
+public class KeyguardClockPositionAlgorithm {
+
+    private static final float SLOW_DOWN_FACTOR = 0.4f;
+
+    private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
+    private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
+
+    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
+    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
+
+    private int mClockNotificationsMarginMin;
+    private int mClockNotificationsMarginMax;
+    private float mClockYFractionMin;
+    private float mClockYFractionMax;
+    private int mMaxKeyguardNotifications;
+    private int mMaxPanelHeight;
+    private float mExpandedHeight;
+    private int mNotificationCount;
+    private int mHeight;
+    private int mKeyguardStatusHeight;
+
+    /**
+     * The number (fractional) of notifications the "more" card counts when calculating how many
+     * notifications are currently visible for the y positioning of the clock.
+     */
+    private float mMoreCardNotificationAmount;
+
+    private static final PathInterpolator sSlowDownInterpolator;
+
+    static {
+        Path path = new Path();
+        path.moveTo(0, 0);
+        path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
+        sSlowDownInterpolator = new PathInterpolator(path);
+    }
+
+    /**
+     * Refreshes the dimension values.
+     */
+    public void loadDimens(Resources res) {
+        mClockNotificationsMarginMin = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin_min);
+        mClockNotificationsMarginMax = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin_max);
+        mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
+        mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
+        mMoreCardNotificationAmount =
+                (float) res.getDimensionPixelSize(R.dimen.notification_summary_height) /
+                        res.getDimensionPixelSize(R.dimen.notification_min_height);
+    }
+
+    public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
+            int notificationCount, int height, int keyguardStatusHeight) {
+        mMaxKeyguardNotifications = maxKeyguardNotifications;
+        mMaxPanelHeight = maxPanelHeight;
+        mExpandedHeight = expandedHeight;
+        mNotificationCount = notificationCount;
+        mHeight = height;
+        mKeyguardStatusHeight = keyguardStatusHeight;
+    }
+
+    public void run(Result result) {
+        int y = getClockY() - mKeyguardStatusHeight/2;
+        float clockAdjustment = getClockYExpansionAdjustment();
+        float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
+        result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+        int clockNotificationsPadding = getClockNotificationsPadding()
+                + result.stackScrollerPaddingAdjustment;
+        int padding = y + clockNotificationsPadding;
+        y += clockAdjustment;
+        result.clockY = y;
+        result.stackScrollerPadding = mKeyguardStatusHeight + padding;
+        result.clockAlpha = getClockAlpha(result.stackScrollerPadding
+                - (y + mKeyguardStatusHeight));
+    }
+
+    private int getClockNotificationsPadding() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
+    }
+
+    private float getClockYFraction() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
+    }
+
+    private int getClockY() {
+        return (int) (getClockYFraction() * mHeight);
+    }
+
+    private float getClockYExpansionAdjustment() {
+        float rubberbandFactor = getClockYExpansionRubberbandFactor();
+        float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
+        float t = value / mMaxPanelHeight;
+        float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
+                * mMaxPanelHeight;
+        if (mNotificationCount == 0) {
+            return (-2*value + slowedDownValue)/3;
+        } else {
+            return slowedDownValue;
+        }
+    }
+
+    private float getClockYExpansionRubberbandFactor() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        t = (float) Math.pow(t, 0.3f);
+        return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
+    }
+
+    private float getTopPaddingAdjMultiplier() {
+        float t = getNotificationAmountT();
+        t = Math.min(t, 1.0f);
+        return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
+                + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
+    }
+
+    private float getClockAlpha(int clockNotificationPadding) {
+        float t = getNotificationAmountT();
+        t = (float) Math.pow(t, 0.3f);
+        float multiplier = 1 + 2 * (1 - t);
+        float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3;
+        return Math.max(0, Math.min(1, alpha));
+    }
+
+    /**
+     * @return a value from 0 to 1 depending on how many notification there are
+     */
+    private float getNotificationAmountT() {
+        return mNotificationCount
+                / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
+    }
+
+    public static class Result {
+
+        /**
+         * The y translation of the clock.
+         */
+        public int clockY;
+
+        /**
+         * The alpha value of the clock.
+         */
+        public float clockAlpha;
+
+        /**
+         * The top padding of the stack scroller, in pixels.
+         */
+        public int stackScrollerPadding;
+
+        /**
+         * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
+         * the padding, but not the overall panel size.
+         */
+        public int stackScrollerPaddingAdjustment;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 123a4f0..2f2a5df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -19,10 +19,11 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Path;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -31,6 +32,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
@@ -82,19 +84,14 @@
     private FlingAnimationUtils mFlingAnimationUtils;
     private int mStatusBarMinHeight;
 
-    private int mClockNotificationsMarginMin;
-    private int mClockNotificationsMarginMax;
-    private float mClockYFractionMin;
-    private float mClockYFractionMax;
     private Interpolator mFastOutSlowInInterpolator;
     private ObjectAnimator mClockAnimator;
     private int mClockAnimationTarget = -1;
-
-    /**
-     * The number (fractional) of notifications the "more" card counts when calculating how many
-     * notifications are currently visible for the y positioning of the clock.
-     */
-    private float mMoreCardNotificationAmount;
+    private int mTopPaddingAdjustment;
+    private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
+            new KeyguardClockPositionAlgorithm();
+    private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
+            new KeyguardClockPositionAlgorithm.Result();
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -139,20 +136,10 @@
         mNotificationTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notifications_top_padding);
         mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
-        mClockNotificationsMarginMin = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_min);
-        mClockNotificationsMarginMax = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_max);
-        mClockYFractionMin =
-                getResources().getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
-        mClockYFractionMax =
-                getResources().getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
-        mMoreCardNotificationAmount =
-                (float) getResources().getDimensionPixelSize(R.dimen.notification_summary_height) /
-                        getResources().getDimensionPixelSize(R.dimen.notification_min_height);
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
         mStatusBarMinHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
+        mClockPositionAlgorithm.loadDimens(getResources());
     }
 
     @Override
@@ -160,6 +147,7 @@
         super.onLayout(changed, left, top, right, bottom);
         if (!mQsExpanded) {
             positionClockAndNotifications();
+            mNotificationStackScroller.setStackHeight(getExpandedHeight());
         }
 
         // Calculate quick setting heights.
@@ -178,16 +166,24 @@
         boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
             mStackScrollerIntrinsicPadding = mHeader.getBottom() + mNotificationTopPadding;
+            mTopPaddingAdjustment = 0;
         } else {
-            int notificationCount = mNotificationStackScroller.getNotGoneChildCount();
-            int y = getClockY(notificationCount) - mKeyguardStatusView.getHeight()/2;
-            int padding = getClockNotificationsPadding(notificationCount);
+            mClockPositionAlgorithm.setup(
+                    mStatusBar.getMaxKeyguardNotifications(),
+                    getMaxPanelHeight(),
+                    getExpandedHeight(),
+                    mNotificationStackScroller.getNotGoneChildCount(),
+                    getHeight(),
+                    mKeyguardStatusView.getHeight());
+            mClockPositionAlgorithm.run(mClockPositionResult);
             if (animateClock || mClockAnimator != null) {
-                startClockAnimation(y);
+                startClockAnimation(mClockPositionResult.clockY);
             } else {
-                mKeyguardStatusView.setY(y);
+                mKeyguardStatusView.setY(mClockPositionResult.clockY);
             }
-            mStackScrollerIntrinsicPadding = y + mKeyguardStatusView.getHeight() + padding;
+            applyClockAlpha(mClockPositionResult.clockAlpha);
+            mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding;
+            mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
                 mAnimateNextTopPaddingChange || animateClock);
@@ -218,28 +214,19 @@
                         mClockAnimationTarget = -1;
                     }
                 });
-                StackStateAnimator.startInstantly(mClockAnimator);
+                mClockAnimator.start();
                 return true;
             }
         });
     }
 
-    private int getClockNotificationsPadding(int notificationCount) {
-        float t = notificationCount
-                / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
-        t = Math.min(t, 1.0f);
-        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
-    }
-
-    private float getClockYFraction(int notificationCount) {
-        float t = notificationCount
-                / (mStatusBar.getMaxKeyguardNotifications() + mMoreCardNotificationAmount);
-        t = Math.min(t, 1.0f);
-        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
-    }
-
-    private int getClockY(int notificationCount) {
-        return (int) (getClockYFraction(notificationCount) * getHeight());
+    private void applyClockAlpha(float alpha) {
+        if (alpha != 1.0f) {
+            mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null);
+        } else {
+            mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null);
+        }
+        mKeyguardStatusView.setAlpha(alpha);
     }
 
     public void animateToFullShade() {
@@ -366,10 +353,12 @@
     @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() {
@@ -491,6 +480,7 @@
         mHeader.setExpansion(height);
         setQsTranslation(height);
         setQsStackScrollerPadding(height);
+        mStatusBar.userActivity();
     }
 
     private void setQsTranslation(float height) {
@@ -626,7 +616,7 @@
         int emptyBottomMargin = mStackScrollerContainer.getHeight()
                 - mNotificationStackScroller.getHeight()
                 + mNotificationStackScroller.getEmptyBottomMargin();
-        int maxHeight = maxPanelHeight - emptyBottomMargin;
+        int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment;
         maxHeight = Math.max(maxHeight, mStatusBarMinHeight);
         return maxHeight;
     }
@@ -637,6 +627,9 @@
 
     @Override
     protected void onHeightUpdated(float expandedHeight) {
+        if (!mQsExpanded) {
+            positionClockAndNotifications();
+        }
         mNotificationStackScroller.setStackHeight(expandedHeight);
     }
 
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..c4e61d0 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,7 @@
 
     private Listener mListener;
     private int mLastOverscrollAmount;
+    private boolean mDispatchingTouchEvent;
 
     public ObservableScrollView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -41,6 +43,10 @@
         return getScrollY() == getMaxScrollY();
     }
 
+    public boolean isDispatchingTouchEvent() {
+        return mDispatchingTouchEvent;
+    }
+
     private int getMaxScrollY() {
         int scrollRange = 0;
         if (getChildCount() > 0) {
@@ -52,6 +58,14 @@
     }
 
     @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        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/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index b6a43a7..8631e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -402,7 +402,7 @@
 
     public void setExpandedHeightInternal(float h) {
         float fh = getMaxPanelHeight();
-        mExpandedHeight = h;
+        mExpandedHeight = Math.min(fh, h);
 
         if (DEBUG) {
             logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 54af2c5..0922abd 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;
@@ -1027,18 +1028,19 @@
         return new UserHandle(mCurrentUserId);
     }
 
-    public void addNotification(IBinder key, StatusBarNotification notification) {
+    @Override
+    public void addNotificationInternal(StatusBarNotification notification) {
         if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
-        Entry shadeEntry = createNotificationViews(key, notification);
+        Entry shadeEntry = createNotificationViews(notification);
         if (shadeEntry == null) {
             return;
         }
-        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(key, notification)) {
+        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
             return;
         }
         if (mUseHeadsUp && shouldInterrupt(notification)) {
             if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
-            Entry interruptionCandidate = new Entry(key, notification, null);
+            Entry interruptionCandidate = new Entry(notification, null);
             ViewGroup holder = mHeadsUpNotificationView.getHolder();
             if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
                 mInterruptingNotificationTime = System.currentTimeMillis();
@@ -1070,7 +1072,7 @@
 
             // show the ticker if there isn't already a heads up
             if (mInterruptingNotificationEntry == null) {
-                tick(null, notification, true);
+                tick(notification, true);
             }
         }
         addNotificationViews(shadeEntry);
@@ -1089,12 +1091,13 @@
     }
 
     @Override
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        super.updateNotification(key, notification);
-        mIntercepted.update(key, notification);
+    public void updateNotification(StatusBarNotification notification) {
+        super.updateNotification(notification);
+        mIntercepted.update(notification);
     }
 
-    public void removeNotification(IBinder key) {
+    @Override
+    public void removeNotificationInternal(String key) {
         StatusBarNotification old = removeNotificationViews(key);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
@@ -2019,7 +2022,7 @@
     public void setHardKeyboardStatus(boolean available, boolean enabled) {}
 
     @Override
-    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
+    protected void tick(StatusBarNotification n, boolean firstTime) {
         // no ticking in lights-out mode
         if (!areLightsOn()) return;
 
@@ -2344,6 +2347,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) {
@@ -2766,6 +2778,7 @@
         updateStackScrollerState();
         updatePublicMode();
         updateRowStates();
+        updateSpeedBump();
         checkBarModes();
         updateNotificationIcons();
         updateCarrierLabelVisibility(false);
@@ -2776,9 +2789,8 @@
     }
 
     public void userActivity() {
-        if (mState == StatusBarState.KEYGUARD) {
-            mKeyguardViewMediatorCallback.userActivity();
-        }
+        mHandler.removeCallbacks(mUserActivity);
+        mHandler.post(mUserActivity);
     }
 
     public boolean interceptMediaKey(KeyEvent event) {
@@ -2936,4 +2948,13 @@
     public void onScreenTurnedOn() {
         mStackScroller.setAnimationsEnabled(true);
     }
+
+    private final Runnable mUserActivity = new Runnable() {
+        @Override
+        public void run() {
+            if (mState == StatusBarState.KEYGUARD) {
+                mKeyguardViewMediatorCallback.userActivity();
+            }
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1040c15..3849d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -29,6 +29,8 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -108,6 +110,13 @@
         updateStates();
     }
 
+    public void dismissWithAction(OnDismissAction r) {
+        if (!mOccluded) {
+            mBouncer.showWithDismissAction(r);
+        }
+        updateStates();
+    }
+
     /**
      * Reset the state of the view.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 41914ed..5e2d06b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -28,6 +28,7 @@
     boolean animateScale;
     boolean animateHeight;
     boolean animateDimmed;
+    boolean hasDelays;
 
     public AnimationFilter animateAlpha() {
         animateAlpha = true;
@@ -39,6 +40,11 @@
         return this;
     }
 
+    public AnimationFilter hasDelays() {
+        hasDelays = true;
+        return this;
+    }
+
     public AnimationFilter animateZ() {
         animateZ = true;
         return this;
@@ -79,6 +85,7 @@
         animateScale |= filter.animateScale;
         animateHeight |= filter.animateHeight;
         animateDimmed |= filter.animateDimmed;
+        hasDelays |= filter.hasDelays;
     }
 
     private void reset() {
@@ -88,5 +95,6 @@
         animateScale = false;
         animateHeight = 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 90f3d17..079b184 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -103,6 +103,7 @@
     private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
     private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
     private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>();
+    private ArrayList<View> mChildrenChangingPositions = new ArrayList<View>();
     private ArrayList<AnimationEvent> mAnimationEvents
             = new ArrayList<AnimationEvent>();
     private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
@@ -969,9 +970,24 @@
     }
 
     /**
+     * @return The first child which has visibility unequal to GONE which is currently below the
+     *         given translationY or equal to it.
+     */
+    private View getFirstChildBelowTranlsationY(float translationY) {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
      * @return the last child which has visibility unequal to GONE
      */
-    private View getLastChildNotGone() {
+    public View getLastChildNotGone() {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             View child = getChildAt(i);
@@ -1094,23 +1110,41 @@
     @Override
     protected void onViewRemoved(View child) {
         super.onViewRemoved(child);
+        mStackScrollAlgorithm.notifyChildrenChanged(this);
+        if (mChildrenChangingPositions.contains(child)) {
+            // This is only a position change, don't do anything special
+            return;
+        }
         ((ExpandableView) child).setOnHeightChangedListener(null);
         mCurrentStackScrollState.removeViewStateForView(child);
-        mStackScrollAlgorithm.notifyChildrenChanged(this);
         updateScrollStateForRemovedChild(child);
-        generateRemoveAnimation(child);
+        boolean animationGenerated = generateRemoveAnimation(child);
+        if (animationGenerated && !mSwipedOutViews.contains(child)) {
+            // Add this view to an overlay in order to ensure that it will still be temporary
+            // drawn when removed
+            getOverlay().add(child);
+        }
     }
 
-    private void generateRemoveAnimation(View child) {
+    /**
+     * Generate a remove animation for a child view.
+     *
+     * @param child The view to generate the remove animation for.
+     * @return Whether an animation was generated.
+     */
+    private boolean generateRemoveAnimation(View child) {
         if (mIsExpanded && mAnimationsEnabled) {
             if (!mChildrenToAddAnimated.contains(child)) {
                 // Generate Animations
                 mChildrenToRemoveAnimated.add(child);
                 mNeedsAnimation = true;
+                return true;
             } else {
                 mChildrenToAddAnimated.remove(child);
+                return false;
             }
         }
+        return false;
     }
 
     /**
@@ -1155,9 +1189,7 @@
         super.onViewAdded(child);
         mStackScrollAlgorithm.notifyChildrenChanged(this);
         ((ExpandableView) child).setOnHeightChangedListener(this);
-        if (child.getVisibility() != View.GONE) {
-            generateAddAnimation(child);
-        }
+        generateAddAnimation(child);
     }
 
     public void setAnimationsEnabled(boolean animationsEnabled) {
@@ -1168,10 +1200,13 @@
         return mNeedsAnimation
                 && (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
     }
-
+    /**
+     * Generate an animation for an added child view.
+     *
+     * @param child The view to be added.
+     */
     public void generateAddAnimation(View child) {
-        if (mIsExpanded && mAnimationsEnabled) {
-
+        if (mIsExpanded && mAnimationsEnabled && !mChildrenChangingPositions.contains(child)) {
             // Generate Animations
             mChildrenToAddAnimated.add(child);
             mNeedsAnimation = true;
@@ -1186,9 +1221,10 @@
      */
     public void changeViewPosition(View child, int newIndex) {
         if (child != null && child.getParent() == this) {
+            mChildrenChangingPositions.add(child);
             removeView(child);
             addView(child, newIndex);
-            // TODO: handle events
+            mNeedsAnimation = true;
         }
     }
 
@@ -1197,16 +1233,18 @@
             generateChildHierarchyEvents();
             mNeedsAnimation = false;
         }
-        if (!mAnimationEvents.isEmpty()) {
+        if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
             mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState);
+            mAnimationEvents.clear();
         } else {
             applyCurrentState();
         }
     }
 
     private void generateChildHierarchyEvents() {
-        generateChildAdditionEvents();
         generateChildRemovalEvents();
+        generateChildAdditionEvents();
+        generatePositionChangeEvents();
         generateSnapBackEvents();
         generateDragEvents();
         generateTopPaddingEvent();
@@ -1237,12 +1275,24 @@
             int animationType = childWasSwipedOut
                     ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
                     : AnimationEvent.ANIMATION_TYPE_REMOVE;
-            mAnimationEvents.add(new AnimationEvent(child, animationType));
+            AnimationEvent event = new AnimationEvent(child, animationType);
+
+            // we need to know the view after this one
+            event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY());
+            mAnimationEvents.add(event);
         }
         mSwipedOutViews.clear();
         mChildrenToRemoveAnimated.clear();
     }
 
+    private void generatePositionChangeEvents() {
+        for (View child : mChildrenChangingPositions) {
+            mAnimationEvents.add(new AnimationEvent(child,
+                    AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
+        }
+        mChildrenChangingPositions.clear();
+    }
+
     private void generateChildAdditionEvents() {
         for (View child : mChildrenToAddAnimated) {
             mAnimationEvents.add(new AnimationEvent(child,
@@ -1467,7 +1517,6 @@
 
     public void onChildAnimationFinished() {
         requestChildrenUpdate();
-        mAnimationEvents.clear();
     }
 
     /**
@@ -1513,9 +1562,9 @@
     }
 
     private void updateSpeedBump(boolean visible) {
-        int newVisibility = visible ? VISIBLE : GONE;
-        int oldVisibility = mSpeedBumpView.getVisibility();
-        if (newVisibility != oldVisibility) {
+        boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE;
+        if (visible != notGoneBefore) {
+            int newVisibility = visible ? VISIBLE : GONE;
             mSpeedBumpView.setVisibility(newVisibility);
             if (visible) {
                 mSpeedBumpView.collapse();
@@ -1551,21 +1600,24 @@
                         .animateAlpha()
                         .animateHeight()
                         .animateY()
-                        .animateZ(),
+                        .animateZ()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_REMOVE
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
                         .animateY()
-                        .animateZ(),
+                        .animateZ()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
                         .animateY()
-                        .animateZ(),
+                        .animateZ()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_TOP_PADDING_CHANGED
                 new AnimationFilter()
@@ -1593,16 +1645,23 @@
                 new AnimationFilter()
                         .animateY()
                         .animateScale()
-                        .animateDimmed()
+                        .animateDimmed(),
+
+                // ANIMATION_TYPE_CHANGE_POSITION
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateY()
+                        .animateZ()
         };
 
         static int[] LENGTHS = new int[] {
 
                 // ANIMATION_TYPE_ADD
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+                StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
 
                 // ANIMATION_TYPE_REMOVE
-                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+                StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
 
                 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
                 StackStateAnimator.ANIMATION_DURATION_STANDARD,
@@ -1621,22 +1680,27 @@
 
                 // ANIMATION_TYPE_DIMMED
                 StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+
+                // ANIMATION_TYPE_CHANGE_POSITION
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
         };
 
-        static int ANIMATION_TYPE_ADD = 0;
-        static int ANIMATION_TYPE_REMOVE = 1;
-        static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
-        static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
-        static int ANIMATION_TYPE_START_DRAG = 4;
-        static int ANIMATION_TYPE_SNAP_BACK = 5;
-        static int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
-        static int ANIMATION_TYPE_DIMMED = 7;
+        static final int ANIMATION_TYPE_ADD = 0;
+        static final int ANIMATION_TYPE_REMOVE = 1;
+        static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
+        static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
+        static final int ANIMATION_TYPE_START_DRAG = 4;
+        static final int ANIMATION_TYPE_SNAP_BACK = 5;
+        static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
+        static final int ANIMATION_TYPE_DIMMED = 7;
+        static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
 
         final long eventStartTime;
         final View changingView;
         final int animationType;
         final AnimationFilter filter;
         final long length;
+        View viewAfterChangingView;
 
         AnimationEvent(View view, int type) {
             eventStartTime = AnimationUtils.currentAnimationTimeMillis();
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 d572ea5..bd2541a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -208,6 +208,8 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = (ExpandableView) hostView.getChildAt(i);
             if (v.getVisibility() != View.GONE) {
+                StackScrollState.ViewState viewState = resultState.getViewStateForView(v);
+                viewState.notGoneIndex = state.visibleChildren.size();
                 state.visibleChildren.add(v);
             }
         }
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 011411c..44e10be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -37,15 +37,14 @@
     private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
 
     private final ViewGroup mHostView;
+    private final int mRoundedRectCornerRadius;
     private Map<ExpandableView, ViewState> mStateMap;
     private final Rect mClipRect = new Rect();
-    private int mBackgroundRoundedRectCornerRadius;
-    private final Outline mChildOutline = new Outline();
 
     public StackScrollState(ViewGroup hostView) {
         mHostView = hostView;
         mStateMap = new HashMap<ExpandableView, ViewState>();
-        mBackgroundRoundedRectCornerRadius = hostView.getResources().getDimensionPixelSize(
+        mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
@@ -66,6 +65,7 @@
             viewState.height = child.getIntrinsicHeight();
             viewState.gone = child.getVisibility() == View.GONE;
             viewState.alpha = 1;
+            viewState.notGoneIndex = -1;
         }
     }
 
@@ -158,11 +158,15 @@
                 // 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);
+                        : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
 
                 updateChildClippingAndBackground(child, newHeight,
                         clipHeight,
@@ -190,7 +194,7 @@
         if (nextChild != null) {
             ViewState nextState = getViewStateForView(nextChild);
             boolean startIsAboveNext = nextState.yTranslation > speedBumpStart;
-            speedBump.animateDivider(startIsAboveNext);
+            speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */);
 
             // handle expanded case
             if (speedBump.isExpanded()) {
@@ -272,6 +276,11 @@
         boolean dimmed;
 
         /**
+         * The index of the view, only accounting for views not equal to GONE
+         */
+        int notGoneIndex;
+
+        /**
          * The location this view is currently rendered at.
          *
          * <p>See <code>LOCATION_</code> flags.</p>
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 a9dcdd6..f019e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -39,7 +39,11 @@
 public class StackStateAnimator {
 
     public static final int ANIMATION_DURATION_STANDARD = 360;
+    public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
+    private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
 
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
     private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
@@ -62,10 +66,9 @@
 
     private final Interpolator mFastOutSlowInInterpolator;
     public NotificationStackScrollLayout mHostLayout;
-    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
-            new ArrayList<>();
     private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
             new ArrayList<>();
+    private ArrayList<View> mNewAddChildren = new ArrayList<>();
     private Set<Animator> mAnimatorSet = new HashSet<Animator>();
     private Stack<AnimatorListenerAdapter> mAnimationListenerPool
             = new Stack<AnimatorListenerAdapter>();
@@ -96,57 +99,130 @@
         mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
         for (int i = 0; i < childCount; i++) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
             StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
-            if (viewState == null) {
+            if (viewState == null || child.getVisibility() == View.GONE) {
                 continue;
             }
 
-            startAnimations(child, viewState);
-
             child.setClipBounds(null);
+            startAnimations(child, viewState, finalState);
         }
         if (!isRunning()) {
             // no child has preformed any animation, lets finish
             onAnimationFinished();
         }
+        mNewEvents.clear();
+        mNewAddChildren.clear();
     }
 
     /**
      * Start an animation to the given viewState
      */
-    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) {
+    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
+            StackScrollState finalState) {
         int childVisibility = child.getVisibility();
         boolean wasVisible = childVisibility == View.VISIBLE;
         final float alpha = viewState.alpha;
         if (!wasVisible && alpha != 0 && !viewState.gone) {
             child.setVisibility(View.VISIBLE);
         }
+
+        boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
+        boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
+        boolean scaleChanging = child.getScaleX() != viewState.scale;
+        boolean alphaChanging = alpha != child.getAlpha();
+        boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean wasAdded = mNewAddChildren.contains(child);
+        boolean hasDelays = mAnimationFilter.hasDelays;
+        boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
+                alphaChanging || heightChanging;
+        long delay = 0;
+        if (hasDelays && isDelayRelevant || wasAdded) {
+            delay = calculateChildAnimationDelay(viewState, finalState);
+        }
+
         // start translationY animation
-        if (child.getTranslationY() != viewState.yTranslation) {
-            startYTranslationAnimation(child, viewState);
+        if (yTranslationChanging) {
+            startYTranslationAnimation(child, viewState, delay);
         }
+
         // start translationZ animation
-        if (child.getTranslationZ() != viewState.zTranslation) {
-            startZTranslationAnimation(child, viewState);
+        if (zTranslationChanging) {
+            startZTranslationAnimation(child, viewState, delay);
         }
+
         // start scale animation
-        if (child.getScaleX() != viewState.scale) {
+        if (scaleChanging) {
             startScaleAnimation(child, viewState);
         }
+
         // start alpha animation
-        if (alpha != child.getAlpha()) {
-            startAlphaAnimation(child, viewState);
+        if (alphaChanging) {
+            startAlphaAnimation(child, viewState, delay);
         }
+
         // start height animation
-        if (viewState.height != child.getActualHeight()) {
-            startHeightAnimation(child, viewState);
+        if (heightChanging) {
+            startHeightAnimation(child, viewState, delay);
         }
+
         // start dimmed animation
         child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
+
+        if (wasAdded) {
+            child.performAddAnimation(delay);
+        }
+    }
+
+    private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
+            StackScrollState finalState) {
+        long minDelay = 0;
+        for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
+            long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
+            switch (event.animationType) {
+                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
+                    int ownIndex = viewState.notGoneIndex;
+                    int changingIndex = finalState
+                            .getViewStateForView(event.changingView).notGoneIndex;
+                    int difference = Math.abs(ownIndex - changingIndex);
+                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
+                            difference - 1));
+                    long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
+                    minDelay = Math.max(delay, minDelay);
+                    break;
+                }
+                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
+                    delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
+                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
+                    int ownIndex = viewState.notGoneIndex;
+                    boolean noNextView = event.viewAfterChangingView == null;
+                    View viewAfterChangingView = noNextView
+                            ? mHostLayout.getLastChildNotGone()
+                            : event.viewAfterChangingView;
+
+                    int nextIndex = finalState
+                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
+                    if (ownIndex >= nextIndex) {
+                        // we only have the view afterwards
+                        ownIndex++;
+                    }
+                    int difference = Math.abs(ownIndex - nextIndex);
+                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
+                            difference - 1));
+                    long delay = difference * delayPerElement;
+                    minDelay = Math.max(delay, minDelay);
+                    break;
+                }
+                default:
+                    break;
+            }
+        }
+        return minDelay;
     }
 
     private void startHeightAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState) {
+            StackScrollState.ViewState viewState, long delay) {
         Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
         Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
         int newEndValue = viewState.height;
@@ -185,6 +261,9 @@
         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() {
@@ -195,14 +274,14 @@
                 child.setTag(TAG_END_HEIGHT, null);
             }
         });
-        startInstantly(animator);
+        startAnimator(animator);
         child.setTag(TAG_ANIMATOR_HEIGHT, animator);
         child.setTag(TAG_START_HEIGHT, child.getActualHeight());
         child.setTag(TAG_END_HEIGHT, newEndValue);
     }
 
     private void startAlphaAnimation(final ExpandableView child,
-            final StackScrollState.ViewState viewState) {
+            final StackScrollState.ViewState viewState, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
         Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
         final float newEndValue = viewState.alpha;
@@ -236,14 +315,13 @@
                 child.getAlpha(), newEndValue);
         animator.setInterpolator(mFastOutSlowInInterpolator);
         // Handle layer type
-        final int currentLayerType = child.getLayerType();
         child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         animator.addListener(new AnimatorListenerAdapter() {
             public boolean mWasCancelled;
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                child.setLayerType(currentLayerType, null);
+                child.setLayerType(View.LAYER_TYPE_NONE, null);
                 if (newEndValue == 0 && !mWasCancelled) {
                     child.setVisibility(View.INVISIBLE);
                 }
@@ -264,6 +342,9 @@
         });
         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() {
@@ -272,14 +353,14 @@
 
             }
         });
-        startInstantly(animator);
+        startAnimator(animator);
         child.setTag(TAG_ANIMATOR_ALPHA, animator);
         child.setTag(TAG_START_ALPHA, child.getAlpha());
         child.setTag(TAG_END_ALPHA, newEndValue);
     }
 
     private void startZTranslationAnimation(final ExpandableView child,
-            final StackScrollState.ViewState viewState) {
+            final StackScrollState.ViewState viewState, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
         float newEndValue = viewState.zTranslation;
@@ -311,6 +392,9 @@
         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() {
@@ -321,14 +405,14 @@
                 child.setTag(TAG_END_TRANSLATION_Z, null);
             }
         });
-        startInstantly(animator);
+        startAnimator(animator);
         child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
         child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
         child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
     }
 
     private void startYTranslationAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState) {
+            StackScrollState.ViewState viewState, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
         float newEndValue = viewState.yTranslation;
@@ -361,6 +445,9 @@
         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() {
@@ -371,7 +458,7 @@
                 child.setTag(TAG_END_TRANSLATION_Y, null);
             }
         });
-        startInstantly(animator);
+        startAnimator(animator);
         child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
         child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
         child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
@@ -425,18 +512,15 @@
                 child.setTag(TAG_END_SCALE, null);
             }
         });
-        startInstantly(animator);
+        startAnimator(animator);
         child.setTag(TAG_ANIMATOR_SCALE, animator);
         child.setTag(TAG_START_SCALE, child.getScaleX());
         child.setTag(TAG_END_SCALE, newEndValue);
     }
 
-    /**
-     * Start an animator instantly instead of waiting on the next synchronization frame
-     */
-    public static void startInstantly(ValueAnimator animator) {
+    private void startAnimator(ValueAnimator animator) {
+        mAnimatorSet.add(animator);
         animator.start();
-        animator.setCurrentPlayTime(0);
     }
 
     /**
@@ -468,7 +552,6 @@
 
             @Override
             public void onAnimationStart(Animator animation) {
-                mAnimatorSet.add(animation);
                 mWasCancelled = false;
             }
         };
@@ -497,8 +580,6 @@
     }
 
     private void onAnimationFinished() {
-        mHandledEvents.clear();
-        mNewEvents.clear();
         mHostLayout.onChildAnimationFinished();
     }
 
@@ -511,27 +592,60 @@
     private void processAnimationEvents(
             ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
             StackScrollState finalState) {
-        mNewEvents.clear();
         for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
-            View changingView = event.changingView;
-            if (!mHandledEvents.contains(event)) {
-                if (event.animationType == NotificationStackScrollLayout.AnimationEvent
-                        .ANIMATION_TYPE_ADD) {
+            final ExpandableView changingView = (ExpandableView) event.changingView;
+            if (event.animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
 
-                    // This item is added, initialize it's properties.
-                    StackScrollState.ViewState viewState = finalState
-                            .getViewStateForView(changingView);
-                    if (viewState == null) {
-                        // The position for this child was never generated, let's continue.
-                        continue;
-                    }
-                    changingView.setAlpha(0);
-                    changingView.setTranslationY(viewState.yTranslation);
-                    changingView.setTranslationZ(viewState.zTranslation);
+                // This item is added, initialize it's properties.
+                StackScrollState.ViewState viewState = finalState
+                        .getViewStateForView(changingView);
+                if (viewState == null) {
+                    // The position for this child was never generated, let's continue.
+                    continue;
                 }
-                mHandledEvents.add(event);
-                mNewEvents.add(event);
+                if (changingView.getVisibility() == View.GONE) {
+                    // The view was set to gone but the state never removed
+                    finalState.removeViewStateForView(changingView);
+                    continue;
+                }
+                changingView.setAlpha(viewState.alpha);
+                changingView.setTranslationY(viewState.yTranslation);
+                changingView.setTranslationZ(viewState.zTranslation);
+                changingView.setActualHeight(viewState.height, false);
+                mNewAddChildren.add(changingView);
+
+            } else if (event.animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
+                if (changingView.getVisibility() == View.GONE) {
+                    continue;
+                }
+
+                // Find the amount to translate up. This is needed in order to understand the
+                // direction of the remove animation (either downwards or upwards)
+                StackScrollState.ViewState viewState = finalState
+                        .getViewStateForView(event.viewAfterChangingView);
+                int actualHeight = changingView.getActualHeight();
+                // upwards by default
+                float translationDirection = -1.0f;
+                if (viewState != null) {
+                    // there was a view after this one, Approximate the distance the next child
+                    // travelled
+                    translationDirection = ((viewState.yTranslation
+                            - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 /
+                            actualHeight);
+                    translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
+
+                }
+                changingView.performRemoveAnimation(translationDirection, new Runnable() {
+                    @Override
+                    public void run() {
+                        // remove the temporary overlay
+                        mHostLayout.getOverlay().remove(changingView);
+                    }
+                });
             }
+            mNewEvents.add(event);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 9006c9a..25147b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -46,15 +46,23 @@
     }
 
     @Override
-    public void addNotification(IBinder key, StatusBarNotification notification) {
+    public void addNotification(StatusBarNotification notification) {
     }
 
     @Override
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification) {
     }
 
     @Override
-    public void removeNotification(IBinder key) {
+    public void updateNotification(StatusBarNotification notification) {
+    }
+
+    @Override
+    protected void removeNotificationInternal(String key) {
+    }
+
+    @Override
+    public void removeNotification(String key) {
     }
 
     @Override
@@ -113,7 +121,7 @@
     }
 
     @Override
-    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
+    protected void tick(StatusBarNotification n, boolean firstTime) {
     }
 
     @Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 99771934..e178773 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -478,6 +478,7 @@
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
     private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
+    private static final int MSG_DISPATCH_SHOW_RECENTS = 5;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -495,6 +496,9 @@
                 case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
                     dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
                     break;
+                case MSG_DISPATCH_SHOW_RECENTS:
+                    showRecentApps(false);
+                    break;
             }
         }
     }
@@ -2459,6 +2463,12 @@
         }
     }
 
+    @Override
+    public void showRecentApps() {
+        mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
+        mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_RECENTS);
+    }
+
     private void showRecentApps(boolean triggeredFromAltTab) {
         mPreloadedRecentApps = false; // preloading no longer needs to be canceled
         try {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0ad5ce2..7ecf248 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5750,10 +5750,11 @@
                 // updateNetworkSettings();
             }
             // notify battery stats service about this network
-//            try {
-                // TODO
-                //BatteryStatsService.getService().noteNetworkInterfaceType(iface, netType);
-//            } catch (RemoteException e) { }
+            try {
+                BatteryStatsService.getService().noteNetworkInterfaceType(
+                        newNetwork.linkProperties.getInterfaceName(),
+                        newNetwork.networkInfo.getType());
+            } catch (RemoteException e) { }
             notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
         } else {
             if (DBG && newNetwork.networkRequests.size() != 0) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index cf91782..137387e 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -240,9 +240,8 @@
         mPhoneStateListener = new PhoneStateListener(mDaemonHandler.getLooper()) {
             public void onDataConnectionRealTimeInfoChanged(
                     DataConnectionRealTimeInfo dcRtInfo) {
-                // Disabled for now, until we are getting good data.
-                //notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
-                //        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
+                notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
+                        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
             }
         };
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac30319..88bebcb 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
@@ -7081,12 +7087,12 @@
     private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
         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 +7326,9 @@
         if (tr != null) {
             tr.removeTaskActivitiesLocked(-1, false);
             cleanUpRemovedTaskLocked(tr, flags);
+            if (tr.isPersistable) {
+                notifyTaskPersisterLocked(tr, true);
+            }
             return true;
         }
         return false;
@@ -7559,14 +7568,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 +8641,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 +8667,7 @@
         synchronized (this) {
             mProcessStats.shutdownLocked();
         }
+        notifyTaskPersisterLocked(null, true);
 
         return timedout;
     }
@@ -9562,7 +9573,13 @@
                 if (goingCallback != null) goingCallback.run();
                 return;
             }
-            
+
+            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 +17196,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..b948c41 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,132 @@
         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) {
+            Slog.e(TAG, "restoreActivity error intent=" + intent);
+            return null;
+        }
+
+        final ActivityManagerService service = stackSupervisor.mService;
+        final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
+                null, userId);
+        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..d0ba118 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -30,6 +30,10 @@
 import static com.android.server.am.ActivityManagerService.DEBUG_VISBILITY;
 import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS;
 
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+
 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;
@@ -863,7 +867,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 +892,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
@@ -1034,40 +1044,6 @@
         }
     }
 
-    /**
-     * Determine if home should be visible below the passed record.
-     * @param record activity we are querying for.
-     * @return true if home is visible below the passed activity, false otherwise.
-     */
-    boolean isActivityOverHome(ActivityRecord record) {
-        // Start at record and go down, look for either home or a visible fullscreen activity.
-        final TaskRecord recordTask = record.task;
-        for (int taskNdx = mTaskHistory.indexOf(recordTask); taskNdx >= 0; --taskNdx) {
-            TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            final int startNdx =
-                    task == recordTask ? activities.indexOf(record) : activities.size() - 1;
-            for (int activityNdx = startNdx; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.isHomeActivity()) {
-                    return true;
-                }
-                if (!r.finishing && r.fullscreen) {
-                    // Passed activity is over a fullscreen activity.
-                    return false;
-                }
-            }
-            if (task.mOnTopOfHome) {
-                // Got to the bottom of a task on top of home without finding a visible fullscreen
-                // activity. Home is visible.
-                return true;
-            }
-        }
-        // Got to the bottom of this stack and still don't know. If this is over the home stack
-        // then record is over home. May not work if we ever get more than two layers.
-        return mStackSupervisor.isFrontStack(this);
-    }
-
     private void setVisibile(ActivityRecord r, boolean visible) {
         r.visible = visible;
         mWindowManager.setAppVisibility(r.appToken, visible);
@@ -1097,7 +1073,8 @@
         for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
             final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
             for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
-                final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities;
+                final TaskRecord task = tasks.get(taskNdx);
+                final ArrayList<ActivityRecord> activities = task.mActivities;
                 for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
                     final ActivityRecord r = activities.get(activityNdx);
 
@@ -1108,7 +1085,7 @@
                     // - Full Screen Activity OR
                     // - On top of Home and our stack is NOT home
                     if (!r.finishing && r.visible && (r.fullscreen ||
-                            (!isHomeStack() && r.frontOfTask && tasks.get(taskNdx).mOnTopOfHome))) {
+                            (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
                         return false;
                     }
                 }
@@ -1236,7 +1213,7 @@
                         // At this point, nothing else needs to be shown
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
                         behindFullscreen = true;
-                    } else if (!isHomeStack() && r.frontOfTask && task.mOnTopOfHome) {
+                    } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
                         behindFullscreen = true;
                     }
@@ -1390,6 +1367,7 @@
         final boolean userLeaving = mStackSupervisor.mUserLeaving;
         mStackSupervisor.mUserLeaving = false;
 
+        final TaskRecord prevTask = prev != null ? prev.task : null;
         if (next == null) {
             // There are no more activities!  Let's just start up the
             // Launcher...
@@ -1397,7 +1375,10 @@
             if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             // Only resume home if on home display
-            return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
+            final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
+                    HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+            return isOnHomeDisplay() &&
+                    mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
         }
 
         next.delayedResume = false;
@@ -1416,22 +1397,24 @@
         }
 
         final TaskRecord nextTask = next.task;
-        final TaskRecord prevTask = prev != null ? prev.task : null;
         if (prevTask != null && prevTask.stack == this &&
-                prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
+                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
             if (DEBUG_STACK)  mStackSupervisor.validateTopActivitiesLocked();
             if (prevTask == nextTask) {
                 prevTask.setFrontOfTask();
             } else if (prevTask != topTask()) {
-                // This task is going away but it was supposed to return to the home task.
+                // This task is going away but it was supposed to return to the home stack.
                 // Now the task above it has to return to the home task instead.
                 final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
-                mTaskHistory.get(taskNdx).mOnTopOfHome = true;
+                mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             } else {
                 if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
                         "resumeTopActivityLocked: Launching home next");
                 // Only resume home if on home display
-                return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
+                final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
+                        HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+                return isOnHomeDisplay() &&
+                        mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
             }
         }
 
@@ -1802,10 +1785,11 @@
             ActivityStack lastStack = mStackSupervisor.getLastStack();
             final boolean fromHome = lastStack.isHomeStack();
             if (!isHomeStack() && (fromHome || topTask() != task)) {
-                task.mOnTopOfHome = fromHome;
+                task.setTaskToReturnTo(fromHome ?
+                        lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
             }
         } else {
-            task.mOnTopOfHome = false;
+            task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
         }
 
         mTaskHistory.remove(task);
@@ -1821,6 +1805,7 @@
             ++stackNdx;
         }
         mTaskHistory.add(stackNdx, task);
+        updateTaskMovement(task, true);
     }
 
     final void startActivityLocked(ActivityRecord r, boolean newTask,
@@ -2349,8 +2334,8 @@
             ActivityRecord next = topRunningActivityLocked(null);
             if (next != r) {
                 final TaskRecord task = r.task;
-                if (r.frontOfTask && task == topTask() && task.mOnTopOfHome) {
-                    mStackSupervisor.moveHomeToTop();
+                if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+                    mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
                 }
             }
             ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
@@ -2834,8 +2819,9 @@
         if (task != null && task.removeActivity(r)) {
             if (DEBUG_STACK) Slog.i(TAG,
                     "removeActivityFromHistoryLocked: last activity removed from " + this);
-            if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
-                mStackSupervisor.moveHomeToTop();
+            if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
+                    task.isOverHomeStack()) {
+                mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
             }
             removeTask(task);
         }
@@ -3138,14 +3124,28 @@
         mWindowManager.prepareAppTransition(transit, false);
     }
 
-    void moveHomeTaskToTop() {
+    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 moveHomeStackTaskToTop(int homeStackTaskType) {
         final int top = mTaskHistory.size() - 1;
         for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.isHomeTask()) {
-                if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
+            if (task.taskType == homeStackTaskType) {
+                if (DEBUG_TASKS || DEBUG_STACK)
+                    Slog.d(TAG, "moveHomeStackTaskToTop: moving " + task);
                 mTaskHistory.remove(taskNdx);
                 mTaskHistory.add(top, task);
+                updateTaskMovement(task, true);
                 mWindowManager.moveTaskToTop(task.taskId);
                 return;
             }
@@ -3247,19 +3247,19 @@
 
         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);
-            if (task.mOnTopOfHome) {
+            if (task.isOverHomeStack()) {
                 break;
             }
             if (taskNdx == 1) {
                 // Set the last task before tr to go to home.
-                task.mOnTopOfHome = true;
+                task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             }
         }
 
@@ -3280,9 +3280,10 @@
         }
 
         final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
-        if (task == tr && tr.mOnTopOfHome || numTasks <= 1 && isOnHomeDisplay()) {
-            tr.mOnTopOfHome = false;
-            return mStackSupervisor.resumeHomeActivity(null);
+        if (task == tr && tr.isOverHomeStack() || numTasks <= 1 && isOnHomeDisplay()) {
+            final int taskToReturnTo = tr.getTaskToReturnTo();
+            tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+            return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null);
         }
 
         mStackSupervisor.resumeTopActivitiesLocked();
@@ -3723,10 +3724,14 @@
 
         final int taskNdx = mTaskHistory.indexOf(task);
         final int topTaskNdx = mTaskHistory.size() - 1;
-        if (task.mOnTopOfHome && taskNdx < topTaskNdx) {
-            mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
+        if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
+            final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
+            if (!nextTask.isOverHomeStack()) {
+                nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            }
         }
         mTaskHistory.remove(task);
+        updateTaskMovement(task, true);
 
         if (task.mActivities.isEmpty()) {
             final boolean isVoiceSession = task.voiceSession != null;
@@ -3758,7 +3763,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 +3779,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..c1a4643 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -31,6 +31,9 @@
 import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -318,18 +321,27 @@
         }
     }
 
-    void moveHomeToTop() {
+    void moveHomeStackTaskToTop(int homeStackTaskType) {
+        if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
+            mWindowManager.showRecentApps();
+            return;
+        }
         moveHomeStack(true);
-        mHomeStack.moveHomeTaskToTop();
+        mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
     }
 
-    boolean resumeHomeActivity(ActivityRecord prev) {
-        moveHomeToTop();
-        if (prev != null) {
-            prev.task.mOnTopOfHome = false;
+    boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) {
+        if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
+            mWindowManager.showRecentApps();
+            return false;
         }
+        moveHomeStackTaskToTop(homeStackTaskType);
+        if (prev != null) {
+            prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+        }
+
         ActivityRecord r = mHomeStack.topRunningActivityLocked(null);
-        if (r != null && r.isHomeActivity()) {
+        if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) {
             mService.setFocusedActivityLocked(r);
             return resumeTopActivitiesLocked(mHomeStack, prev, null);
         }
@@ -370,6 +382,12 @@
         return null;
     }
 
+    void setNextTaskId(int taskId) {
+        if (taskId > mCurTaskId) {
+            mCurTaskId = taskId;
+        }
+    }
+
     int getNextTaskId() {
         do {
             mCurTaskId++;
@@ -677,7 +695,7 @@
     }
 
     void startHomeActivity(Intent intent, ActivityInfo aInfo) {
-        moveHomeToTop();
+        moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
         startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
                 null, false, null, null);
     }
@@ -1609,7 +1627,7 @@
                                     (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
                                     == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
                                 // Caller wants to appear on home activity.
-                                intentActivity.task.mOnTopOfHome = true;
+                                intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
                             }
                             options = null;
                         }
@@ -1793,6 +1811,11 @@
                         newTaskInfo != null ? newTaskInfo : r.info,
                         newTaskIntent != null ? newTaskIntent : intent,
                         voiceSession, voiceInteractor, true), null, true);
+                if (sourceRecord == null) {
+                    // Launched from a service or notification or task that is finishing.
+                    r.task.setTaskToReturnTo(isFrontStack(mHomeStack) ?
+                            mHomeStack.topTask().taskType : RECENTS_ACTIVITY_TYPE);
+                }
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                         r.task);
             } else {
@@ -1805,7 +1828,7 @@
                         == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
                     // Caller wants to appear on home activity, so before starting
                     // their own activity we will bring home to the front.
-                    r.task.mOnTopOfHome = r.task.stack.isOnHomeDisplay();
+                    r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
                 }
             }
         } else if (sourceRecord != null) {
@@ -2156,7 +2179,7 @@
         if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
             // Caller wants the home activity moved with it.  To accomplish this,
             // we'll just indicate that this task returns to the home task.
-            task.mOnTopOfHome = true;
+            task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
         }
         task.stack.moveTaskToFrontLocked(task, null, options);
         if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
@@ -2250,6 +2273,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);
+            }
+            mWindowManager.addTask(taskId, stackId, false);
+        }
+        resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
+    }
+
     void moveTaskToStack(int taskId, int stackId, boolean toTop) {
         final TaskRecord task = anyTaskForIdLocked(taskId);
         if (task == null) {
@@ -2504,7 +2547,7 @@
             }
         } else {
             // Stack was moved to another display while user was swapped out.
-            resumeHomeActivity(null);
+            resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
         }
         return homeInFront;
     }
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..ba3f2fe
--- /dev/null
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -0,0 +1,351 @@
+/*
+ * 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;
+            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 (IOException e) {
+                Slog.e(TAG, "Unable to parse " + taskFile + ". Error " + e);
+            } catch (XmlPullParserException e) {
+                Slog.e(TAG, "Unable to parse " + taskFile + ". Error " + e);
+            } finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
+
+        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 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();
+                    int taskNdx;
+                    for (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..c07bc1e 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,6 +17,9 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
 
 import android.app.Activity;
@@ -27,15 +30,38 @@
 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_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 +88,64 @@
             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;
 
-    /** Launch the home activity when leaving this task. Will be false for tasks that are not on
-     * Display.DEFAULT_DISPLAY. */
-    boolean mOnTopOfHome = false;
+    /** Takes on same value as first root activity */
+    boolean isPersistable = false;
 
-    TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
+    /** 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;
+
+    /** Indication of what to run next when task exits. Use ActivityRecord types.
+     * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
+     * task stack. */
+    private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
+
+    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, 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;
+        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
+        userId = _userId;
+        lastDescription = _lastDescription;
+        mActivities = activities;
+        mLastTimeMoved = lastTimeMoved;
     }
 
     void touchActiveTime() {
@@ -144,6 +209,14 @@
         }
     }
 
+    void setTaskToReturnTo(int taskToReturnTo) {
+        mTaskToReturnTo = taskToReturnTo;
+    }
+
+    int getTaskToReturnTo() {
+        return mTaskToReturnTo;
+    }
+
     void disposeThumbnail() {
         super.disposeThumbnail();
         for (int i=mActivities.size()-1; i>=0; i--) {
@@ -237,12 +310,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 +328,9 @@
             // Was previously in list.
             numFullscreen--;
         }
+        if (r.isPersistable()) {
+            mService.notifyTaskPersisterLocked(this, false);
+        }
         return mActivities.size() == 0;
     }
 
@@ -270,7 +350,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 +441,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 +488,15 @@
     }
 
     boolean isHomeTask() {
-        return mTaskType == ActivityRecord.HOME_ACTIVITY_TYPE;
+        return taskType == HOME_ACTIVITY_TYPE;
     }
 
     boolean isApplicationTask() {
-        return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+        return taskType == APPLICATION_ACTIVITY_TYPE;
+    }
+
+    boolean isOverHomeStack() {
+        return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE;
     }
 
     public TaskAccessInfo getTaskAccessInfoLocked() {
@@ -493,7 +586,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,14 +621,152 @@
         }
     }
 
+    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_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;
+        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_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, 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);
+                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
         }
         if (affinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.println(affinity);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 584145f..b94ea62 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -175,7 +175,10 @@
 
     public void registerService(IInterface service, ComponentName component, int userid) {
         checkNotNull(service);
-        registerServiceImpl(service, component, userid);
+        ManagedServiceInfo info = registerServiceImpl(service, component, userid);
+        if (info != null) {
+            onServiceAdded(info);
+        }
     }
 
     /**
@@ -464,7 +467,7 @@
         }
     }
 
-    private void registerServiceImpl(final IInterface service,
+    private ManagedServiceInfo registerServiceImpl(final IInterface service,
             final ComponentName component, final int userid) {
         synchronized (mMutex) {
             try {
@@ -472,10 +475,12 @@
                         true /*isSystem*/, null, Build.VERSION_CODES.L);
                 service.asBinder().linkToDeath(info, 0);
                 mServices.add(info);
+                return info;
             } catch (RemoteException e) {
                 // already dead
             }
         }
+        return null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index 125158fd..db17f3a 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.util.Slog;
 
-import com.android.internal.R;
 import com.android.server.notification.NotificationManagerService.NotificationRecord;
 
 /**
@@ -39,7 +38,7 @@
         if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
     }
 
-    public RankingFuture process(NotificationRecord record) {
+    public RankingReconsideration process(NotificationRecord record) {
         if (record == null || record.getNotification() == null) {
             if (DBG) Slog.d(TAG, "skipping empty notification");
             return null;
@@ -54,10 +53,15 @@
             record.setRecentlyIntusive(true);
         }
 
-        return new RankingFuture(record, HANG_TIME_MS) {
+        return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
             @Override
             public void work() {
-                mRecord.setRecentlyIntusive(false);
+                // pass
+            }
+
+            @Override
+            public void applyChangesLocked(NotificationRecord record) {
+                record.setRecentlyIntusive(false);
             }
         };
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0b3e02a6..bbc3091 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -111,7 +111,6 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
 /** {@hide} */
@@ -453,11 +452,10 @@
 
 
 
-    public static final class NotificationRecord
-    {
+    public static final class NotificationRecord {
         final StatusBarNotification sbn;
         SingleNotificationStats stats;
-        IBinder statusBarKey;
+        boolean isCanceled;
 
         // These members are used by NotificationSignalExtractors
         // to communicate with the ranking module.
@@ -472,6 +470,7 @@
         public Notification getNotification() { return sbn.getNotification(); }
         public int getFlags() { return sbn.getNotification().flags; }
         public int getUserId() { return sbn.getUserId(); }
+        public String getKey() { return sbn.getKey(); }
 
         void dump(PrintWriter pw, String prefix, Context baseContext) {
             final Notification notification = sbn.getNotification();
@@ -555,13 +554,13 @@
             return mContactAffinity;
         }
 
-        public boolean isRecentlyIntrusive() {
-            return mRecentlyIntrusive;
-        }
-
         public void setRecentlyIntusive(boolean recentlyIntrusive) {
             mRecentlyIntrusive = recentlyIntrusive;
         }
+
+        public boolean isRecentlyIntrusive() {
+            return mRecentlyIntrusive;
+        }
     }
 
     private static final class ToastRecord
@@ -1263,7 +1262,7 @@
         @Override
         public void registerListener(final INotificationListener listener,
                 final ComponentName component, final int userid) {
-            checkCallerIsSystem();
+            enforceSystemOrSystemUI("INotificationManager.registerListener");
             mListeners.registerService(listener, component, userid);
         }
 
@@ -1634,8 +1633,8 @@
                 if (!mSignalExtractors.isEmpty()) {
                     for (NotificationSignalExtractor extractor : mSignalExtractors) {
                         try {
-                            RankingFuture future = extractor.process(r);
-                            scheduleRankingReconsideration(future);
+                            RankingReconsideration recon = extractor.process(r);
+                            scheduleRankingReconsideration(recon);
                         } catch (Throwable t) {
                             Slog.w(TAG, "NotificationSignalExtractor failed.", t);
                         }
@@ -1668,7 +1667,7 @@
                         "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
                 synchronized (mNotificationList) {
                     NotificationRecord old = null;
-                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
+                    int index = indexOfNotificationLocked(n.getKey());
                     if (index < 0) {
                         mNotificationList.add(r);
                         mUsageStats.registerPostedByApp(r);
@@ -1677,12 +1676,8 @@
                         mNotificationList.set(index, r);
                         mUsageStats.registerUpdatedByApp(r, old);
                         // Make sure we don't lose the foreground service state.
-                        if (old != null) {
-                            notification.flags |=
-                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
-                        }
-                    }
-                    if (old != null) {
+                        notification.flags |=
+                            old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                         mNotificationsByKey.remove(old.sbn.getKey());
                     }
                     mNotificationsByKey.put(n.getKey(), r);
@@ -1705,18 +1700,17 @@
                     }
 
                     if (notification.icon != 0) {
-                        if (old != null && old.statusBarKey != null) {
-                            r.statusBarKey = old.statusBarKey;
+                        if (old != null && !old.isCanceled) {
                             final long identity = Binder.clearCallingIdentity();
                             try {
-                                mStatusBar.updateNotification(r.statusBarKey, n);
+                                mStatusBar.updateNotification(n);
                             } finally {
                                 Binder.restoreCallingIdentity(identity);
                             }
                         } else {
                             final long identity = Binder.clearCallingIdentity();
                             try {
-                                r.statusBarKey = mStatusBar.addNotification(n);
+                                mStatusBar.addNotification(n);
                                 if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
                                         && canInterrupt) {
                                     mAttentionLight.pulse();
@@ -1733,10 +1727,10 @@
                         mListeners.notifyPostedLocked(r.sbn, cloneNotificationListLocked());
                     } else {
                         Slog.e(TAG, "Not posting notification with icon==0: " + notification);
-                        if (old != null && old.statusBarKey != null) {
+                        if (old != null && !old.isCanceled) {
                             final long identity = Binder.clearCallingIdentity();
                             try {
-                                mStatusBar.removeNotification(old.statusBarKey);
+                                mStatusBar.removeNotification(r.getKey());
                             } finally {
                                 Binder.restoreCallingIdentity(identity);
                             }
@@ -1986,37 +1980,38 @@
         }
     }
 
-    private void scheduleRankingReconsideration(RankingFuture future) {
-        if (future != null) {
-            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
-            long delay = future.getDelay(TimeUnit.MILLISECONDS);
+    private void scheduleRankingReconsideration(RankingReconsideration recon) {
+        if (recon != null) {
+            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, recon);
+            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
             mRankingHandler.sendMessageDelayed(m, delay);
         }
     }
 
     private void handleRankingReconsideration(Message message) {
-        if (!(message.obj instanceof RankingFuture)) return;
-
-        RankingFuture future = (RankingFuture) message.obj;
-        future.run();
-        try {
-            NotificationRecord record = future.get();
-            synchronized (mNotificationList) {
-                int before = mNotificationList.indexOf(record);
-                if (before != -1) {
-                    Collections.sort(mNotificationList, mRankingComparator);
-                    int after = mNotificationList.indexOf(record);
-
-                    if (before != after) {
-                        scheduleSendRankingUpdate();
-                    }
-                }
+        if (!(message.obj instanceof RankingReconsideration)) return;
+        RankingReconsideration recon = (RankingReconsideration) message.obj;
+        recon.run();
+        boolean orderChanged;
+        synchronized (mNotificationList) {
+            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
+            if (record == null) {
+                return;
             }
-        } catch (InterruptedException e) {
-            // we're running the future explicitly, so this should never happen
-        } catch (ExecutionException e) {
-            // we're running the future explicitly, so this should never happen
+            int before = findNotificationRecordIndexLocked(record);
+            recon.applyChangesLocked(record);
+            Collections.sort(mNotificationList, mRankingComparator);
+            int after = findNotificationRecordIndexLocked(record);
+            orderChanged = before != after;
         }
+        if (orderChanged) {
+            scheduleSendRankingUpdate();
+        }
+    }
+
+    // lock on mNotificationList
+    private int findNotificationRecordIndexLocked(NotificationRecord target) {
+        return Collections.binarySearch(mNotificationList, target, mRankingComparator);
     }
 
     private void scheduleSendRankingUpdate() {
@@ -2120,11 +2115,11 @@
         if (r.getNotification().icon != 0) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                mStatusBar.removeNotification(r.statusBarKey);
+                mStatusBar.removeNotification(r.getKey());
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-            r.statusBarKey = null;
+            r.isCanceled = true;
             mListeners.notifyRemovedLocked(r.sbn, cloneNotificationListLocked());
         }
 
@@ -2392,6 +2387,18 @@
         return -1;
     }
 
+    // lock on mNotificationList
+    int indexOfNotificationLocked(String key) {
+        NotificationRecord r = mNotificationsByKey.get(key);
+        if (r == null) {
+            return -1;
+        }
+        int index = Collections.binarySearch(mNotificationList, r, mRankingComparator);
+        // Guarantee to return -1 when not found.
+        return (index >= 0) ? index : -1;
+    }
+
+
     private void updateNotificationPulse() {
         synchronized (mNotificationList) {
             updateLightsLocked();
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index a41fdfe..71c819e 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
 /**
  * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
  *  on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
@@ -32,10 +34,10 @@
      * Called once per notification that is posted or updated.
      *
      * @return null if the work is done, or a future if there is more to do. The
-     * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered
-     * by that execution, the {@link NotificationManagerService} may send order update
-     * events to the {@link android.service.notification.NotificationListenerService}s.
+     * {@link RankingReconsideration} will be run on a worker thread, and if notifications
+     * are re-ordered by that execution, the {@link NotificationManagerService} may send order
+     * update events to the {@link android.service.notification.NotificationListenerService}s.
      */
-    public RankingFuture process(NotificationManagerService.NotificationRecord notification);
+    public RankingReconsideration process(NotificationRecord notification);
 
 }
diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java
deleted file mode 100644
index d711d02..0000000
--- a/services/core/java/com/android/server/notification/RankingFuture.java
+++ /dev/null
@@ -1,118 +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.server.notification;
-
-import java.util.concurrent.Delayed;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-public abstract class RankingFuture
-        implements ScheduledFuture<NotificationManagerService.NotificationRecord> {
-    private static final long IMMEDIATE = 0l;
-
-    private static final int START = 0;
-    private static final int RUNNING = 1;
-    private static final int DONE = 2;
-    private static final int CANCELLED = 3;
-
-    private int mState;
-    private long mDelay;
-    protected NotificationManagerService.NotificationRecord mRecord;
-
-    public RankingFuture(NotificationManagerService.NotificationRecord record) {
-        this(record, IMMEDIATE);
-    }
-
-    public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) {
-        mDelay = delay;
-        mRecord = record;
-        mState = START;
-    }
-
-    public void run() {
-        if (mState == START) {
-            mState = RUNNING;
-
-            work();
-
-            mState = DONE;
-            synchronized (this) {
-                notifyAll();
-            }
-        }
-    }
-
-    @Override
-    public long getDelay(TimeUnit unit) {
-        return unit.convert(mDelay, TimeUnit.MILLISECONDS);
-    }
-
-    @Override
-    public int compareTo(Delayed another) {
-        return Long.compare(getDelay(TimeUnit.MILLISECONDS),
-                another.getDelay(TimeUnit.MILLISECONDS));
-    }
-
-    @Override
-    public boolean cancel(boolean mayInterruptIfRunning) {
-        if (mState == START) {  // can't cancel if running or done
-            mState = CANCELLED;
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isCancelled() {
-        return mState == CANCELLED;
-    }
-
-    @Override
-    public boolean isDone() {
-        return mState == DONE;
-    }
-
-    @Override
-    public NotificationManagerService.NotificationRecord get()
-            throws InterruptedException, ExecutionException {
-        while (!isDone()) {
-            synchronized (this) {
-                this.wait();
-            }
-        }
-        return mRecord;
-    }
-
-    @Override
-    public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS);
-        long start = System.currentTimeMillis();
-        long now = System.currentTimeMillis();
-        while (!isDone() && (now - start) < timeoutMillis) {
-            try {
-                wait(timeoutMillis - (now - start));
-            } catch (InterruptedException e) {
-                now = System.currentTimeMillis();
-            }
-        }
-        return mRecord;
-    }
-
-    public abstract void work();
-}
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
new file mode 100644
index 0000000..cf5e210
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.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.server.notification;
+
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents future work required to extract signals from notifications for ranking.
+ *
+ * {@hide}
+ */
+public abstract class RankingReconsideration implements Runnable {
+    private static final long IMMEDIATE = 0l;
+
+    private static final int START = 0;
+    private static final int RUNNING = 1;
+    private static final int DONE = 2;
+    private static final int CANCELLED = 3;
+
+    private int mState;
+    private long mDelay;
+    protected String mKey;
+
+    public RankingReconsideration(String key) {
+        this(key, IMMEDIATE);
+    }
+
+    public RankingReconsideration(String key, long delay) {
+        mDelay = delay;
+        mKey = key;
+        mState = START;
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    public void run() {
+        if (mState == START) {
+            mState = RUNNING;
+
+            work();
+
+            mState = DONE;
+            synchronized (this) {
+                notifyAll();
+            }
+        }
+    }
+
+    public long getDelay(TimeUnit unit) {
+        return unit.convert(mDelay, TimeUnit.MILLISECONDS);
+    }
+
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        if (mState == START) {  // can't cancel if running or done
+            mState = CANCELLED;
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isCancelled() {
+        return mState == CANCELLED;
+    }
+
+    public boolean isDone() {
+        return mState == DONE;
+    }
+
+    /**
+     * Analyse the notification.  This will be called on a worker thread. To
+     * avoid concurrency issues, do not use held references to modify the
+     * {@link NotificationRecord}.
+     */
+    public abstract void work();
+
+    /**
+     * Apply any computed changes to the notification record.  This method will be
+     * called on the main service thread, synchronized on he mNotificationList.
+     * @param record The locked record to be updated.
+     */
+    public abstract void applyChangesLocked(NotificationRecord record);
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index b5c2730..157d749 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -59,7 +59,7 @@
     // maps raw person handle to resolved person object
     private LruCache<String, LookupResult> mPeopleCache;
 
-    private RankingFuture validatePeople(NotificationRecord record) {
+    private RankingReconsideration validatePeople(final NotificationRecord record) {
         float affinity = NONE;
         Bundle extras = record.getNotification().extras;
         if (extras == null) {
@@ -99,11 +99,11 @@
         }
 
         if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
-        return new RankingFuture(record) {
+        return new RankingReconsideration(record.getKey()) {
+            float mContactAffinity = NONE;
             @Override
             public void work() {
-                if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey());
-                float affinity = NONE;
+                if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey());
                 for (final String handle: pendingLookups) {
                     LookupResult lookupResult = null;
                     final Uri uri = Uri.parse(handle);
@@ -124,13 +124,16 @@
                         synchronized (mPeopleCache) {
                             mPeopleCache.put(handle, lookupResult);
                         }
-                        affinity = Math.max(affinity, lookupResult.getAffinity());
+                        mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
                     }
                 }
-                float affinityBound = mRecord.getContactAffinity();
-                affinity = Math.max(affinity, affinityBound);
-                mRecord.setContactAffinity(affinity);
-                if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+            }
+
+            @Override
+            public void applyChangesLocked(NotificationRecord operand) {
+                float affinityBound = operand.getContactAffinity();
+                operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
+                if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
             }
         };
     }
@@ -229,7 +232,7 @@
                 mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
     }
 
-    public RankingFuture process(NotificationManagerService.NotificationRecord record) {
+    public RankingReconsideration process(NotificationRecord record) {
         if (!mEnabled) {
             if (INFO) Slog.i(TAG, "disabled");
             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..b06b090 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6140,7 +6140,12 @@
         }
         final String nativeLibraryPath = (new File(libDir, apkName)).getPath();
         pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
-        pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+        // pkgSetting might be null during rescan following uninstall of updates
+        // to a bundled app, so accommodate that possibility.  The settings in
+        // that case will be established later from the parsed package.
+        if (pkgSetting != null) {
+            pkgSetting.nativeLibraryPathString = nativeLibraryPath;
+        }
     }
 
     // Deduces the required ABI of an upgraded system app.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 4f75189..8d905ba 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -23,7 +23,7 @@
 
 public interface StatusBarManagerInternal {
     void setNotificationDelegate(NotificationDelegate delegate);
-    IBinder addNotification(StatusBarNotification notification);
-    void updateNotification(IBinder key, StatusBarNotification notification);
-    void removeNotification(IBinder key);
+    void addNotification(StatusBarNotification notification);
+    void updateNotification(StatusBarNotification notification);
+    void removeNotification(String key);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8b8c73d..55b5e3b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -60,8 +60,8 @@
     private NotificationDelegate mNotificationDelegate;
     private volatile IStatusBar mBar;
     private StatusBarIconList mIcons = new StatusBarIconList();
-    private HashMap<IBinder,StatusBarNotification> mNotifications
-            = new HashMap<IBinder,StatusBarNotification>();
+    private HashMap<String,StatusBarNotification> mNotifications
+            = new HashMap<String,StatusBarNotification>();
 
     // for disabling the status bar
     private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
@@ -117,30 +117,29 @@
         }
 
         @Override
-        public IBinder addNotification(StatusBarNotification notification) {
+        public void addNotification(StatusBarNotification notification) {
             synchronized (mNotifications) {
-                IBinder key = new Binder();
-                mNotifications.put(key, notification);
+                mNotifications.put(notification.getKey(), notification);
                 if (mBar != null) {
                     try {
-                        mBar.addNotification(key, notification);
+                        mBar.addNotification(notification);
                     } catch (RemoteException ex) {
                     }
                 }
-                return key;
             }
         }
 
         @Override
-        public void updateNotification(IBinder key, StatusBarNotification notification) {
+        public void updateNotification(StatusBarNotification notification) {
             synchronized (mNotifications) {
+                String key = notification.getKey();
                 if (!mNotifications.containsKey(key)) {
                     throw new IllegalArgumentException("updateNotification key not found: " + key);
                 }
-                mNotifications.put(key, notification);
+                mNotifications.put(notification.getKey(), notification);
                 if (mBar != null) {
                     try {
-                        mBar.updateNotification(key, notification);
+                        mBar.updateNotification(notification);
                     } catch (RemoteException ex) {
                     }
                 }
@@ -148,7 +147,7 @@
         }
 
         @Override
-        public void removeNotification(IBinder key) {
+        public void removeNotification(String key) {
             synchronized (mNotifications) {
                 final StatusBarNotification n = mNotifications.remove(key);
                 if (n == null) {
@@ -512,8 +511,7 @@
     // ================================================================================
     @Override
     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
-            List<IBinder> notificationKeys, List<StatusBarNotification> notifications,
-            int switches[], List<IBinder> binders) {
+            List<StatusBarNotification> notifications, int switches[], List<IBinder> binders) {
         enforceStatusBarService();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -522,9 +520,8 @@
             iconList.copyFrom(mIcons);
         }
         synchronized (mNotifications) {
-            for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
-                notificationKeys.add(e.getKey());
-                notifications.add(e.getValue());
+            for (StatusBarNotification sbn : mNotifications.values()) {
+                notifications.add(sbn);
             }
         }
         synchronized (mLock) {
@@ -714,7 +711,7 @@
         synchronized (mNotifications) {
             int i=0;
             pw.println("Notification list:");
-            for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
+            for (Map.Entry<String,StatusBarNotification> e: mNotifications.entrySet()) {
                 pw.printf("  %2d: %s\n", i, e.getValue().toString());
                 i++;
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1a0dd82..6fbb39d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -10265,6 +10265,10 @@
         mPolicy.lockNow(options);
     }
 
+    public void showRecentApps() {
+        mPolicy.showRecentApps();
+    }
+
     @Override
     public boolean isSafeModeEnabled() {
         return mSafeMode;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4e4d127..5395f60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3351,6 +3351,44 @@
     }
 
     @Override
+    public UserHandle createUser(ComponentName who, String name) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+            long id = Binder.clearCallingIdentity();
+            try {
+                UserInfo userInfo = mUserManager.createUser(name, 0 /* flags */);
+                if (userInfo != null) {
+                    return userInfo.getUserHandle();
+                }
+                return null;
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    @Override
+    public boolean removeUser(ComponentName who, UserHandle userHandle) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+            long id = Binder.clearCallingIdentity();
+            try {
+                return mUserManager.removeUser(userHandle.getIdentifier());
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    @Override
     public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
 
diff --git a/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);
         }
 
 }