Merge "Do not stop window if preserving the window" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 646c56e..95c43b2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -101,7 +101,6 @@
     field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
     field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
     field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
-    field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
     field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -2666,6 +2665,7 @@
     field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
     field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
     field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+    field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
@@ -4907,6 +4907,7 @@
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
     field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -5058,6 +5059,7 @@
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setChronometerCountsDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
@@ -19172,7 +19174,6 @@
     method public short getLeapSecond();
     method public long getTimeInNs();
     method public double getTimeUncertaintyInNs();
-    method public byte getType();
     method public boolean hasBiasInNs();
     method public boolean hasBiasUncertaintyInNs();
     method public boolean hasDriftInNsPerSec();
@@ -19198,17 +19199,10 @@
     method public void setLeapSecond(short);
     method public void setTimeInNs(long);
     method public void setTimeUncertaintyInNs(double);
-    method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
-    field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
-    field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
   }
 
-  public static abstract class GnssClock.GnssClockType implements java.lang.annotation.Annotation {
-  }
-
   public final class GnssMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method public double getAccumulatedDeltaRangeInMeters();
@@ -19774,6 +19768,7 @@
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
     field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+    field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
   }
 
   public static class AudioFormat.Builder {
@@ -20027,6 +20022,7 @@
   public abstract interface AudioRouting {
     method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+    method public abstract android.media.AudioDeviceInfo getRoutedDevice();
     method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
   }
@@ -22997,6 +22993,7 @@
     field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
     field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
     field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
+    field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -26888,6 +26885,7 @@
     method public static void glProgramBinary(int, int, java.nio.Buffer, int);
     method public static void glProgramParameteri(int, int, int);
     method public static void glReadBuffer(int);
+    method public static void glReadPixels(int, int, int, int, int, int, int);
     method public static void glRenderbufferStorageMultisample(int, int, int, int, int);
     method public static void glResumeTransformFeedback();
     method public static void glSamplerParameterf(int, int, float);
@@ -29105,7 +29103,6 @@
     field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
     field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
     field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
-    field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
   }
 
   public final class PowerManager.WakeLock {
@@ -34575,10 +34572,11 @@
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
-    field public static final int IMPORTANCE_DEFAULT = 2; // 0x2
-    field public static final int IMPORTANCE_HIGH = 3; // 0x3
-    field public static final int IMPORTANCE_LOW = 1; // 0x1
-    field public static final int IMPORTANCE_MAX = 4; // 0x4
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
     field public static final int IMPORTANCE_NONE = 0; // 0x0
     field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
   }
@@ -36509,9 +36507,11 @@
 package android.telephony {
 
   public class CarrierConfigManager {
+    method public android.os.PersistableBundle getConfig(int);
     method public android.os.PersistableBundle getConfig();
-    method public android.os.PersistableBundle getConfigForSubId(int);
-    method public void notifyConfigChangedForSubId(int);
+    method public deprecated android.os.PersistableBundle getConfigForSubId(int);
+    method public void notifyConfigChanged(int);
+    method public deprecated void notifyConfigChangedForSubId(int);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final java.lang.String BOOL_ALLOW_EMERGENCY_VIDEO_CALLS = "bool_allow_emergency_video_calls";
     field public static final java.lang.String BOOL_ALLOW_VIDEO_PAUSE = "bool_allow_video_pause";
@@ -44081,6 +44081,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
 
@@ -47217,6 +47218,7 @@
     method public void setChar(int, java.lang.String, char);
     method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
     method public void setChronometer(int, long, java.lang.String, boolean);
+    method public void setChronometerCountsDown(int, boolean);
     method public void setContentDescription(int, java.lang.CharSequence);
     method public void setDisplayedChild(int, int);
     method public void setDouble(int, java.lang.String, double);
diff --git a/api/system-current.txt b/api/system-current.txt
index 52460bc..47c642f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -169,7 +169,6 @@
     field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
     field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
     field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
-    field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
     field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
@@ -2768,6 +2767,7 @@
     field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
     field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
     field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+    field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
@@ -5039,6 +5039,7 @@
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
     field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -5190,6 +5191,7 @@
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setChronometerCountsDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
@@ -20361,7 +20363,6 @@
     method public short getLeapSecond();
     method public long getTimeInNs();
     method public double getTimeUncertaintyInNs();
-    method public byte getType();
     method public boolean hasBiasInNs();
     method public boolean hasBiasUncertaintyInNs();
     method public boolean hasDriftInNsPerSec();
@@ -20387,17 +20388,10 @@
     method public void setLeapSecond(short);
     method public void setTimeInNs(long);
     method public void setTimeUncertaintyInNs(double);
-    method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
-    field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
-    field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
   }
 
-  public static abstract class GnssClock.GnssClockType implements java.lang.annotation.Annotation {
-  }
-
   public final class GnssMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method public double getAccumulatedDeltaRangeInMeters();
@@ -21270,6 +21264,7 @@
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
     field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+    field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
   }
 
   public static class AudioFormat.Builder {
@@ -21535,6 +21530,7 @@
   public abstract interface AudioRouting {
     method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+    method public abstract android.media.AudioDeviceInfo getRoutedDevice();
     method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
   }
@@ -24708,6 +24704,7 @@
     field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
     field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
     field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
+    field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -29188,6 +29185,7 @@
     method public static void glProgramBinary(int, int, java.nio.Buffer, int);
     method public static void glProgramParameteri(int, int, int);
     method public static void glReadBuffer(int);
+    method public static void glReadPixels(int, int, int, int, int, int, int);
     method public static void glRenderbufferStorageMultisample(int, int, int, int, int);
     method public static void glResumeTransformFeedback();
     method public static void glSamplerParameterf(int, int, float);
@@ -31408,7 +31406,6 @@
     field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
     field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
     field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
-    field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
     field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
     field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
     field public static final int USER_ACTIVITY_EVENT_TOUCH = 2; // 0x2
@@ -37118,10 +37115,11 @@
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
-    field public static final int IMPORTANCE_DEFAULT = 2; // 0x2
-    field public static final int IMPORTANCE_HIGH = 3; // 0x3
-    field public static final int IMPORTANCE_LOW = 1; // 0x1
-    field public static final int IMPORTANCE_MAX = 4; // 0x4
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
     field public static final int IMPORTANCE_NONE = 0; // 0x0
     field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
   }
@@ -39224,10 +39222,12 @@
 package android.telephony {
 
   public class CarrierConfigManager {
+    method public android.os.PersistableBundle getConfig(int);
     method public android.os.PersistableBundle getConfig();
-    method public android.os.PersistableBundle getConfigForSubId(int);
+    method public deprecated android.os.PersistableBundle getConfigForSubId(int);
     method public static android.os.PersistableBundle getDefaultConfig();
-    method public void notifyConfigChangedForSubId(int);
+    method public void notifyConfigChanged(int);
+    method public deprecated void notifyConfigChangedForSubId(int);
     method public void updateConfigForPhoneId(int, java.lang.String);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final java.lang.String BOOL_ALLOW_EMERGENCY_VIDEO_CALLS = "bool_allow_emergency_video_calls";
@@ -46865,6 +46865,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
 
@@ -50335,6 +50336,7 @@
     method public void setChar(int, java.lang.String, char);
     method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
     method public void setChronometer(int, long, java.lang.String, boolean);
+    method public void setChronometerCountsDown(int, boolean);
     method public void setContentDescription(int, java.lang.CharSequence);
     method public void setDisplayedChild(int, int);
     method public void setDouble(int, java.lang.String, double);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6f40052..7a9557c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -101,7 +101,6 @@
     field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
     field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
     field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL";
-    field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
     field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
@@ -2666,6 +2665,7 @@
     field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
     field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
     field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+    field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
@@ -4907,6 +4907,7 @@
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
     field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
@@ -5058,6 +5059,7 @@
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setChronometerCountsDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
@@ -19180,7 +19182,6 @@
     method public short getLeapSecond();
     method public long getTimeInNs();
     method public double getTimeUncertaintyInNs();
-    method public byte getType();
     method public boolean hasBiasInNs();
     method public boolean hasBiasUncertaintyInNs();
     method public boolean hasDriftInNsPerSec();
@@ -19206,17 +19207,10 @@
     method public void setLeapSecond(short);
     method public void setTimeInNs(long);
     method public void setTimeUncertaintyInNs(double);
-    method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
-    field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
-    field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
   }
 
-  public static abstract class GnssClock.GnssClockType implements java.lang.annotation.Annotation {
-  }
-
   public final class GnssMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method public double getAccumulatedDeltaRangeInMeters();
@@ -19783,6 +19777,7 @@
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
     field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
+    field public static final int SAMPLE_RATE_UNSPECIFIED = 0; // 0x0
   }
 
   public static class AudioFormat.Builder {
@@ -20036,6 +20031,7 @@
   public abstract interface AudioRouting {
     method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+    method public abstract android.media.AudioDeviceInfo getRoutedDevice();
     method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
   }
@@ -23006,6 +23002,7 @@
     field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
     field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
     field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
+    field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -26897,6 +26894,7 @@
     method public static void glProgramBinary(int, int, java.nio.Buffer, int);
     method public static void glProgramParameteri(int, int, int);
     method public static void glReadBuffer(int);
+    method public static void glReadPixels(int, int, int, int, int, int, int);
     method public static void glRenderbufferStorageMultisample(int, int, int, int, int);
     method public static void glResumeTransformFeedback();
     method public static void glSamplerParameterf(int, int, float);
@@ -29114,7 +29112,6 @@
     field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
     field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
     field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
-    field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
   }
 
   public final class PowerManager.WakeLock {
@@ -34590,10 +34587,11 @@
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
-    field public static final int IMPORTANCE_DEFAULT = 2; // 0x2
-    field public static final int IMPORTANCE_HIGH = 3; // 0x3
-    field public static final int IMPORTANCE_LOW = 1; // 0x1
-    field public static final int IMPORTANCE_MAX = 4; // 0x4
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
     field public static final int IMPORTANCE_NONE = 0; // 0x0
     field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
   }
@@ -36524,9 +36522,11 @@
 package android.telephony {
 
   public class CarrierConfigManager {
+    method public android.os.PersistableBundle getConfig(int);
     method public android.os.PersistableBundle getConfig();
-    method public android.os.PersistableBundle getConfigForSubId(int);
-    method public void notifyConfigChangedForSubId(int);
+    method public deprecated android.os.PersistableBundle getConfigForSubId(int);
+    method public void notifyConfigChanged(int);
+    method public deprecated void notifyConfigChangedForSubId(int);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final java.lang.String BOOL_ALLOW_EMERGENCY_VIDEO_CALLS = "bool_allow_emergency_video_calls";
     field public static final java.lang.String BOOL_ALLOW_VIDEO_PAUSE = "bool_allow_video_pause";
@@ -44098,6 +44098,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
 
@@ -47234,6 +47235,7 @@
     method public void setChar(int, java.lang.String, char);
     method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
     method public void setChronometer(int, long, java.lang.String, boolean);
+    method public void setChronometerCountsDown(int, boolean);
     method public void setContentDescription(int, java.lang.CharSequence);
     method public void setDisplayedChild(int, int);
     method public void setDouble(int, java.lang.String, double);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index 51bbb81..3ae9e12 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -20,6 +20,7 @@
     libutils \
     liblog \
     libbinder \
+    libnativeloader \
     libandroid_runtime \
     $(app_process_common_shared_libs) \
 
@@ -52,6 +53,7 @@
     libutils \
     liblog \
     libbinder \
+    libnativeloader \
     libandroid_runtime \
     $(app_process_common_shared_libs) \
 
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 2e02382..8bcbf51 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -21,6 +21,7 @@
 #include <cutils/properties.h>
 #include <cutils/trace.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <nativeloader/native_loader.h>
 #include <private/android_filesystem_config.h>  // for AID_SYSTEM
 
 namespace android {
@@ -304,6 +305,7 @@
     }
 
     if (zygote) {
+        PreloadPublicNativeLibraries();
         runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
     } else if (className) {
         runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 24449d4..4025553 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1490,7 +1490,7 @@
         System.err.println("    -i: specify the installer package name");
         System.err.println("    -s: install application on sdcard");
         System.err.println("    -f: install application on internal flash");
-        System.err.println("    -d: allow version code downgrade");
+        System.err.println("    -d: allow version code downgrade (debuggable packages only)");
         System.err.println("    -p: partial application install");
         System.err.println("    -g: grant all runtime permissions");
         System.err.println("    -S: size in bytes of entire session");
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 4bc6b97..fb5f5b9 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -334,7 +334,7 @@
     public static final int GLOBAL_ACTION_HOME = 2;
 
     /**
-     * Action to open the recent apps.
+     * Action to toggle showing the overview of recent apps
      */
     public static final int GLOBAL_ACTION_RECENTS = 3;
 
@@ -353,6 +353,11 @@
      */
     public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
 
+    /**
+     * Action to toggle docking the current app's window
+     */
+    public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
+
     private static final String LOG_TAG = "AccessibilityService";
 
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b1927d0..8b010f3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -716,8 +716,7 @@
         try {
             return ActivityManagerNative.getDefault().getFrontActivityScreenCompatMode();
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return 0;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -726,7 +725,7 @@
         try {
             ActivityManagerNative.getDefault().setFrontActivityScreenCompatMode(mode);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -735,8 +734,7 @@
         try {
             return ActivityManagerNative.getDefault().getPackageScreenCompatMode(packageName);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return 0;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -745,7 +743,7 @@
         try {
             ActivityManagerNative.getDefault().setPackageScreenCompatMode(packageName, mode);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -754,8 +752,7 @@
         try {
             return ActivityManagerNative.getDefault().getPackageAskScreenCompat(packageName);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -764,7 +761,7 @@
         try {
             ActivityManagerNative.getDefault().setPackageAskScreenCompat(packageName, ask);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1049,6 +1046,7 @@
                     return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename,
                             userId);
                 } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
                 }
             }
             return null;
@@ -1429,8 +1427,7 @@
             return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
                     flags, UserHandle.myUserId());
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1455,8 +1452,7 @@
             return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
                     flags, userId);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1591,8 +1587,7 @@
         try {
             appTasks = ActivityManagerNative.getDefault().getAppTasks(mContext.getPackageName());
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
         int numAppTasks = appTasks.size();
         for (int i = 0; i < numAppTasks; i++) {
@@ -1617,7 +1612,7 @@
             try {
                 mAppTaskThumbnailSize = ActivityManagerNative.getDefault().getAppTaskThumbnailSize();
             } catch (RemoteException e) {
-                throw new IllegalStateException("System dead?", e);
+                throw e.rethrowAsRuntimeException();
             }
         }
     }
@@ -1683,7 +1678,7 @@
             return ActivityManagerNative.getDefault().addAppTask(activity.getActivityToken(),
                     intent, description, thumbnail);
         } catch (RemoteException e) {
-            throw new IllegalStateException("System dead?", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1725,8 +1720,7 @@
         try {
             return ActivityManagerNative.getDefault().getTasks(maxNum, 0);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1742,8 +1736,7 @@
         try {
             return ActivityManagerNative.getDefault().removeTask(taskId);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1902,8 +1895,7 @@
         try {
             return ActivityManagerNative.getDefault().getTaskThumbnail(id);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1912,8 +1904,7 @@
         try {
             return ActivityManagerNative.getDefault().isInHomeStack(taskId);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1962,7 +1953,7 @@
         try {
             ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2148,8 +2139,7 @@
             return ActivityManagerNative.getDefault()
                     .getServices(maxNum, 0);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2164,8 +2154,7 @@
             return ActivityManagerNative.getDefault()
                     .getRunningServiceControlPanel(service);
         } catch (RemoteException e) {
-            // System dead, we will be dead too soon!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2269,6 +2258,7 @@
         try {
             ActivityManagerNative.getDefault().getMemoryInfo(outInfo);
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2387,7 +2377,7 @@
             return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
                     observer, UserHandle.myUserId());
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2421,8 +2411,7 @@
             return ActivityManagerNative.getDefault().getGrantedUriPermissions(packageName,
                     UserHandle.myUserId());
         } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't get granted URI permissions for :" + packageName, e);
-            return ParceledListSlice.emptyList();
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2440,7 +2429,7 @@
             ActivityManagerNative.getDefault().clearGrantedUriPermissions(packageName,
                     UserHandle.myUserId());
         } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't clear granted URI permissions for :" + packageName, e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2560,7 +2549,7 @@
         try {
             return ActivityManagerNative.getDefault().getProcessesInErrorState();
         } catch (RemoteException e) {
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2874,7 +2863,7 @@
         try {
             return ActivityManagerNative.getDefault().getRunningExternalApplications();
         } catch (RemoteException e) {
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2891,7 +2880,7 @@
             return ActivityManagerNative.getDefault().setProcessMemoryTrimLevel(process, userId,
                     level);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2909,7 +2898,7 @@
         try {
             return ActivityManagerNative.getDefault().getRunningAppProcesses();
         } catch (RemoteException e) {
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2928,7 +2917,7 @@
                     mContext.getOpPackageName());
             return RunningAppProcessInfo.procStateToImportance(procState);
         } catch (RemoteException e) {
-            return RunningAppProcessInfo.IMPORTANCE_GONE;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2947,6 +2936,7 @@
         try {
             ActivityManagerNative.getDefault().getMyMemoryState(outState);
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2965,7 +2955,7 @@
         try {
             return ActivityManagerNative.getDefault().getProcessMemoryInfo(pids);
         } catch (RemoteException e) {
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2999,6 +2989,7 @@
             ActivityManagerNative.getDefault().killBackgroundProcesses(packageName,
                     UserHandle.myUserId());
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3015,7 +3006,7 @@
             ActivityManagerNative.getDefault().killUid(UserHandle.getAppId(uid),
                     UserHandle.getUserId(uid), reason);
         } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't kill uid:" + uid, e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3042,6 +3033,7 @@
         try {
             ActivityManagerNative.getDefault().forceStopPackage(packageName, userId);
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3060,8 +3052,8 @@
         try {
             return ActivityManagerNative.getDefault().getDeviceConfigurationInfo();
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     /**
@@ -3150,8 +3142,8 @@
         try {
             return ActivityManagerNative.getDefault().isUserAMonkey();
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     /**
@@ -3226,10 +3218,8 @@
             return AppGlobals.getPackageManager()
                     .checkUidPermission(permission, uid);
         } catch (RemoteException e) {
-            // Should never happen, but if it does... deny!
-            Slog.e(TAG, "PackageManager is dead?!?", e);
+            throw e.rethrowAsRuntimeException();
         }
-        return PackageManager.PERMISSION_DENIED;
     }
 
     /** @hide */
@@ -3238,10 +3228,8 @@
             return AppGlobals.getPackageManager()
                     .checkUidPermission(permission, uid);
         } catch (RemoteException e) {
-            // Should never happen, but if it does... deny!
-            Slog.e(TAG, "PackageManager is dead?!?", e);
+            throw e.rethrowAsRuntimeException();
         }
-        return PackageManager.PERMISSION_DENIED;
     }
 
     /**
@@ -3277,7 +3265,7 @@
             return ActivityManagerNative.getDefault().handleIncomingUser(callingPid,
                     callingUid, userId, allowAll, requireFull, name, callerPackage);
         } catch (RemoteException e) {
-            throw new SecurityException("Failed calling activity manager", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3292,7 +3280,7 @@
             ui = ActivityManagerNative.getDefault().getCurrentUser();
             return ui != null ? ui.id : 0;
         } catch (RemoteException e) {
-            return 0;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3304,7 +3292,7 @@
         try {
             return ActivityManagerNative.getDefault().switchUser(userid);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3328,7 +3316,7 @@
         try {
             return ActivityManagerNative.getDefault().isUserRunning(userId, 0);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3338,7 +3326,7 @@
             return ActivityManagerNative.getDefault().isUserRunning(userId,
                     ActivityManager.FLAG_AND_LOCKED);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3348,7 +3336,7 @@
             return ActivityManagerNative.getDefault().isUserRunning(userId,
                     ActivityManager.FLAG_AND_UNLOCKED);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3433,6 +3421,7 @@
             ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize,
                     mContext.getPackageName());
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3451,6 +3440,7 @@
         try {
             ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null);
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3461,6 +3451,7 @@
         try {
             ActivityManagerNative.getDefault().startLockTaskMode(taskId);
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3471,6 +3462,7 @@
         try {
             ActivityManagerNative.getDefault().stopLockTaskMode();
         } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3497,7 +3489,7 @@
         try {
             return ActivityManagerNative.getDefault().getLockTaskModeState();
         } catch (RemoteException e) {
-            return ActivityManager.LOCK_TASK_MODE_NONE;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -3520,7 +3512,7 @@
             try {
                 mAppTaskImpl.finishAndRemoveTask();
             } catch (RemoteException e) {
-                Slog.e(TAG, "Invalid AppTask", e);
+                throw e.rethrowAsRuntimeException();
             }
         }
 
@@ -3533,8 +3525,7 @@
             try {
                 return mAppTaskImpl.getTaskInfo();
             } catch (RemoteException e) {
-                Slog.e(TAG, "Invalid AppTask", e);
-                return null;
+                throw e.rethrowAsRuntimeException();
             }
         }
 
@@ -3548,7 +3539,7 @@
             try {
                 mAppTaskImpl.moveToFront();
             } catch (RemoteException e) {
-                Slog.e(TAG, "Invalid AppTask", e);
+                throw e.rethrowAsRuntimeException();
             }
         }
 
@@ -3590,7 +3581,7 @@
             try {
                 mAppTaskImpl.setExcludeFromRecents(exclude);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Invalid AppTask", e);
+                throw e.rethrowAsRuntimeException();
             }
         }
     }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4fa654f..7310d67 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -29,6 +29,31 @@
  * @hide Only for use within the system server.
  */
 public abstract class ActivityManagerInternal {
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we had
+     * the surface saved.
+     */
+    public static final int APP_TRANSITION_SAVED_SURFACE = 0;
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
+     * the starting window.
+     */
+    public static final int APP_TRANSITION_STARTING_WINDOW = 1;
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
+     * app windows were drawn
+     */
+    public static final int APP_TRANSITION_WINDOWS_DRAWN = 2;
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+     * timeout.
+     */
+    public static final int APP_TRANSITION_TIMEOUT = 3;
+
     // Called by the power manager.
     public abstract void onWakefulnessChanged(int wakefulness);
 
@@ -48,6 +73,7 @@
      * with underlying activities.
      */
     public static abstract class SleepToken {
+
         /**
          * Releases the sleep token.
          */
@@ -56,6 +82,7 @@
 
     /**
      * Returns home activity for the specified user.
+     *
      * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
      */
     public abstract ComponentName getHomeActivityForUser(int userId);
@@ -72,4 +99,19 @@
     public abstract void onLocalVoiceInteractionStarted(IBinder callingActivity,
             IVoiceInteractionSession mSession,
             IVoiceInteractor mInteractor);
+
+    /**
+     * Callback for window manager to let activity manager know that the starting window has been
+     * drawn
+     */
+    public abstract void notifyStartingWindowDrawn();
+
+    /**
+     * Callback for window manager to let activity manager know that we are finally starting the
+     * app transition;
+     *
+     * @param reason The reason why the app transition started. Must be one of the APP_TRANSITION_*
+     *               values.
+     */
+    public abstract void notifyAppTransitionStarting(int reason);
 }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bb36a3e..a1f82de 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1540,6 +1540,14 @@
             return true;
         }
 
+        case GET_MEMORY_TRIM_LEVEL_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int level = getMemoryTrimLevel();
+            reply.writeNoException();
+            reply.writeInt(level);
+            return true;
+        }
+
         case ENTER_SAFE_MODE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             enterSafeMode();
@@ -4874,6 +4882,18 @@
         data.recycle();
         reply.recycle();
     }
+    public int getMemoryTrimLevel() throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(GET_MEMORY_TRIM_LEVEL_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int level = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return level;
+    }
     public void enterSafeMode() throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 49230a0..a38df61 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2241,8 +2241,8 @@
                         memInfo.getTotalSwappablePss(),
                         memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
-                        memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOut() :
-                        memInfo.getTotalSwappedOutPss(),
+                        memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
+                        memInfo.getTotalSwappedOut(),
                         nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
                         nativeFree+dalvikFree);
             } else {
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 7d0d1b4..b20c091 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -20,16 +20,14 @@
 import android.util.ArrayMap;
 import dalvik.system.PathClassLoader;
 
-class ApplicationLoaders
-{
-    public static ApplicationLoaders getDefault()
-    {
+class ApplicationLoaders {
+    public static ApplicationLoaders getDefault() {
         return gApplicationLoaders;
     }
 
-    public ClassLoader getClassLoader(String zip, boolean isBundled, String librarySearchPath,
-                                      String libraryPermittedPath, ClassLoader parent)
-    {
+    public ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
+                                      String librarySearchPath, String libraryPermittedPath,
+                                      ClassLoader parent) {
         /*
          * This is the parent we use if they pass "null" in.  In theory
          * this should be the "system" class loader; in practice we
@@ -55,11 +53,22 @@
                 }
     
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
+
                 PathClassLoader pathClassloader =
-                    new PathClassLoader(zip, isBundled, librarySearchPath,
-                                        libraryPermittedPath, parent);
+                    new PathClassLoader(zip, librarySearchPath, parent);
+
+                String errorMessage = createClassloaderNamespace(pathClassloader,
+                                                                 targetSdkVersion,
+                                                                 librarySearchPath,
+                                                                 libraryPermittedPath,
+                                                                 isBundled);
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
+                if (errorMessage != null) {
+                    throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
+                                                   pathClassloader + ": " + errorMessage);
+                }
+
                 mLoaders.put(zip, pathClassloader);
                 return pathClassloader;
             }
@@ -71,6 +80,12 @@
         }
     }
 
+    private static native String createClassloaderNamespace(ClassLoader classLoader,
+                                                            int targetSdkVersion,
+                                                            String librarySearchPath,
+                                                            String libraryPermittedPath,
+                                                            boolean isShared);
+
     private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
 
     private static final ApplicationLoaders gApplicationLoaders
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 91eabcc..6d716cc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -143,7 +143,7 @@
                 return pi;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(packageName);
@@ -154,7 +154,7 @@
         try {
             return mPM.currentToCanonicalPackageNames(names);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -163,7 +163,7 @@
         try {
             return mPM.canonicalToCurrentPackageNames(names);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -227,7 +227,7 @@
                 return gids;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(packageName);
@@ -252,7 +252,7 @@
                 return uid;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(packageName);
@@ -267,7 +267,7 @@
                 return pi;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(name);
@@ -282,7 +282,7 @@
                 return pi;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(group);
@@ -297,7 +297,7 @@
                 return pgi;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(name);
@@ -308,7 +308,7 @@
         try {
             return mPM.getAllPermissionGroups(flags);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -330,7 +330,7 @@
                 return maybeAdjustApplicationInfo(ai);
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(packageName);
@@ -370,7 +370,7 @@
                 return ai;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(className.toString());
@@ -385,7 +385,7 @@
                 return ai;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(className.toString());
@@ -400,7 +400,7 @@
                 return si;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(className.toString());
@@ -415,7 +415,7 @@
                 return pi;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(className.toString());
@@ -426,7 +426,7 @@
         try {
             return mPM.getSystemSharedLibraryNames();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -436,7 +436,7 @@
         try {
             return mPM.getServicesSystemSharedLibraryPackageName();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -445,7 +445,7 @@
         try {
             return mPM.getSystemAvailableFeatures();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -459,7 +459,7 @@
         try {
             return mPM.hasSystemFeature(name, version);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -468,7 +468,7 @@
         try {
             return mPM.checkPermission(permName, pkgName, mContext.getUserId());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -477,7 +477,7 @@
         try {
             return mPM.isPermissionRevokedByPolicy(permName, pkgName, mContext.getUserId());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -491,7 +491,7 @@
                 try {
                     mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName();
                 } catch (RemoteException e) {
-                    throw new RuntimeException("Package manager has died", e);
+                    throw e.rethrowAsRuntimeException();
                 }
             }
             return mPermissionsControllerPackageName;
@@ -503,7 +503,7 @@
         try {
             return mPM.addPermission(info);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -512,7 +512,7 @@
         try {
             return mPM.addPermissionAsync(info);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -521,7 +521,7 @@
         try {
             mPM.removePermission(name);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -531,7 +531,7 @@
         try {
             mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -541,7 +541,7 @@
         try {
             mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -550,7 +550,7 @@
         try {
             return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -561,7 +561,7 @@
             mPM.updatePermissionFlags(permissionName, packageName, flagMask,
                     flagValues, user.getIdentifier());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -571,7 +571,7 @@
             return mPM.shouldShowRequestPermissionRationale(permission,
                     mContext.getPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -580,7 +580,7 @@
         try {
             return mPM.checkSignatures(pkg1, pkg2);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -589,7 +589,7 @@
         try {
             return mPM.checkUidSignatures(uid1, uid2);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -598,7 +598,7 @@
         try {
             return mPM.getPackagesForUid(uid);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -607,7 +607,7 @@
         try {
             return mPM.getNameForUid(uid);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -620,7 +620,7 @@
                 return uid;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
         throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
     }
@@ -638,7 +638,7 @@
             ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
             return slice.getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -652,7 +652,7 @@
                     permissions, flags, userId);
             return slice.getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -664,7 +664,7 @@
             ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId);
             return slice.getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -680,7 +680,7 @@
             }
             return Collections.emptyList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -695,7 +695,7 @@
             }
             return null;
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -705,9 +705,8 @@
             return mPM.isEphemeralApplication(
                     mContext.getPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
-            Log.e(TAG, "System server is dead", e);
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     @Override
@@ -724,11 +723,12 @@
                     mContext.getPackageName(), mContext.getUserId());
             if (cookie != null) {
                 return cookie;
+            } else {
+                return EmptyArray.BYTE;
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "System server is dead", e);
+            throw e.rethrowAsRuntimeException();
         }
-        return EmptyArray.BYTE;
     }
 
     @Override
@@ -737,9 +737,8 @@
             return mPM.setEphemeralApplicationCookie(
                     mContext.getPackageName(), cookie, mContext.getUserId());
         } catch (RemoteException e) {
-            Log.e(TAG, "System server is dead", e);
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     @Override
@@ -756,7 +755,7 @@
                 flags,
                 userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -777,7 +776,7 @@
                 flags,
                 userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -809,7 +808,7 @@
                                                   specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
                                                   flags, mContext.getUserId());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -825,7 +824,7 @@
                 flags,
                 userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -843,7 +842,7 @@
                 flags,
                 mContext.getUserId());
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -856,7 +855,7 @@
                 flags,
                 userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -872,7 +871,7 @@
             return mPM.queryIntentContentProviders(intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -892,7 +891,7 @@
         try {
             return mPM.resolveContentProvider(name, flags, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -904,7 +903,7 @@
                     = mPM.queryContentProviders(processName, uid, flags);
             return slice != null ? slice.getList() : null;
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -919,7 +918,7 @@
                 return ii;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         throw new NameNotFoundException(className.toString());
@@ -931,7 +930,7 @@
         try {
             return mPM.queryInstrumentation(targetPackage, flags);
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1198,7 +1197,7 @@
                 return getResourcesForApplication(ai);
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
         throw new NameNotFoundException("Package " + appPackageName + " doesn't exist");
     }
@@ -1213,7 +1212,7 @@
             }
             return mCachedSafeMode != 0;
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1229,7 +1228,7 @@
                 mPM.addOnPermissionsChangeListener(delegate);
                 mPermissionListeners.put(listener, delegate);
             } catch (RemoteException e) {
-                throw new RuntimeException("Package manager has died", e);
+                throw e.rethrowAsRuntimeException();
             }
         }
     }
@@ -1243,7 +1242,7 @@
                     mPM.removeOnPermissionsChangeListener(delegate);
                     mPermissionListeners.remove(listener);
                 } catch (RemoteException e) {
-                    throw new RuntimeException("Package manager has died", e);
+                    throw e.rethrowAsRuntimeException();
                 }
             }
         }
@@ -1544,7 +1543,8 @@
         try {
             mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
                     verificationParams, null, userId);
-        } catch (RemoteException ignored) {
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1563,8 +1563,7 @@
             }
             return res;
         } catch (RemoteException e) {
-            // Should never happen!
-            throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1573,7 +1572,7 @@
         try {
             mPM.verifyPendingInstall(id, response);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1583,7 +1582,7 @@
         try {
             mPM.extendVerificationTimeout(id, verificationCodeAtTimeout, millisecondsToDelay);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1592,7 +1591,7 @@
         try {
             mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1601,8 +1600,7 @@
         try {
             return mPM.getIntentVerificationStatus(packageName, userId);
         } catch (RemoteException e) {
-            // Should never happen!
-            return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1611,8 +1609,7 @@
         try {
             return mPM.updateIntentVerificationStatus(packageName, status, userId);
         } catch (RemoteException e) {
-            // Should never happen!
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1621,8 +1618,7 @@
         try {
             return mPM.getIntentFilterVerifications(packageName);
         } catch (RemoteException e) {
-            // Should never happen!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1631,8 +1627,7 @@
         try {
             return mPM.getAllIntentFilters(packageName);
         } catch (RemoteException e) {
-            // Should never happen!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1641,8 +1636,7 @@
         try {
             return mPM.getDefaultBrowserPackageName(userId);
         } catch (RemoteException e) {
-            // Should never happen!
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1651,8 +1645,7 @@
         try {
             return mPM.setDefaultBrowserPackageName(packageName, userId);
         } catch (RemoteException e) {
-            // Should never happen!
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1662,7 +1655,7 @@
         try {
             mPM.setInstallerPackageName(targetPackage, installerPackageName);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1671,9 +1664,8 @@
         try {
             return mPM.getInstallerPackageName(packageName);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     @Override
@@ -1796,7 +1788,7 @@
                 return false;
             }
         } catch (RemoteException e) {
-            throw new RuntimeException("Package manager has died", e);
+            throw e.rethrowAsRuntimeException();
         }
 
         // Otherwise we can move to any private volume
@@ -1874,7 +1866,7 @@
         try {
             mPM.deletePackageAsUser(packageName, observer, userId, flags);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1884,7 +1876,7 @@
         try {
             mPM.clearApplicationUserData(packageName, observer, mContext.getUserId());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
     @Override
@@ -1893,7 +1885,7 @@
         try {
             mPM.deleteApplicationCacheFiles(packageName, observer);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1903,7 +1895,7 @@
         try {
             mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, observer);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1912,7 +1904,7 @@
         try {
             mPM.freeStorage(volumeUuid, freeStorageSize, pi);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1922,9 +1914,8 @@
         try {
             return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return packageNames;
     }
 
     @Override
@@ -1932,9 +1923,8 @@
         try {
             return mPM.isPackageSuspendedForUser(packageName, userId);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     @Override
@@ -1943,7 +1933,7 @@
         try {
             mPM.getPackageSizeInfo(packageName, userHandle, observer);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
     @Override
@@ -1951,7 +1941,7 @@
         try {
             mPM.addPackageToPreferred(packageName);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1960,7 +1950,7 @@
         try {
             mPM.removePackageFromPreferred(packageName);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1969,9 +1959,8 @@
         try {
             return mPM.getPreferredPackages(flags);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return new ArrayList<PackageInfo>();
     }
 
     @Override
@@ -1980,7 +1969,7 @@
         try {
             mPM.addPreferredActivity(filter, match, set, activity, mContext.getUserId());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -1990,7 +1979,7 @@
         try {
             mPM.addPreferredActivity(filter, match, set, activity, userId);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2000,7 +1989,7 @@
         try {
             mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2011,7 +2000,7 @@
         try {
             mPM.replacePreferredActivity(filter, match, set, activity, userId);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2020,7 +2009,7 @@
         try {
             mPM.clearPackagePreferredActivities(packageName);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2030,9 +2019,8 @@
         try {
             return mPM.getPreferredActivities(outFilters, outActivities, packageName);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return 0;
     }
 
     @Override
@@ -2040,9 +2028,8 @@
         try {
             return mPM.getHomeActivities(outActivities);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     @Override
@@ -2051,7 +2038,7 @@
         try {
             mPM.setComponentEnabledSetting(componentName, newState, flags, mContext.getUserId());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2060,9 +2047,8 @@
         try {
             return mPM.getComponentEnabledSetting(componentName, mContext.getUserId());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
     }
 
     @Override
@@ -2072,7 +2058,7 @@
             mPM.setApplicationEnabledSetting(packageName, newState, flags,
                     mContext.getUserId(), mContext.getOpPackageName());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2081,9 +2067,8 @@
         try {
             return mPM.getApplicationEnabledSetting(packageName, mContext.getUserId());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
     }
 
     @Override
@@ -2092,20 +2077,18 @@
         try {
             return mPM.setApplicationHiddenSettingAsUser(packageName, hidden,
                     user.getIdentifier());
-        } catch (RemoteException re) {
-            // Should never happen!
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     @Override
     public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
         try {
             return mPM.getApplicationHiddenSettingAsUser(packageName, user.getIdentifier());
-        } catch (RemoteException re) {
-            // Should never happen!
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     /** @hide */
@@ -2113,26 +2096,22 @@
     public KeySet getKeySetByAlias(String packageName, String alias) {
         Preconditions.checkNotNull(packageName);
         Preconditions.checkNotNull(alias);
-        KeySet ks;
         try {
-            ks = mPM.getKeySetByAlias(packageName, alias);
+            return mPM.getKeySetByAlias(packageName, alias);
         } catch (RemoteException e) {
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
-        return ks;
     }
 
     /** @hide */
     @Override
     public KeySet getSigningKeySet(String packageName) {
         Preconditions.checkNotNull(packageName);
-        KeySet ks;
         try {
-            ks = mPM.getSigningKeySet(packageName);
+            return mPM.getSigningKeySet(packageName);
         } catch (RemoteException e) {
-            return null;
+            throw e.rethrowAsRuntimeException();
         }
-        return ks;
     }
 
     /** @hide */
@@ -2143,7 +2122,7 @@
         try {
             return mPM.isPackageSignedByKeySet(packageName, ks);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2155,7 +2134,7 @@
         try {
             return mPM.isPackageSignedByKeySetExactly(packageName, ks);
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2167,9 +2146,8 @@
         try {
             return mPM.getVerifierDeviceIdentity();
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     /**
@@ -2180,7 +2158,7 @@
         try {
             return mPM.isUpgrade();
         } catch (RemoteException e) {
-            return false;
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2218,7 +2196,7 @@
             mPM.addCrossProfileIntentFilter(filter, mContext.getOpPackageName(),
                     sourceUserId, targetUserId, flags);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -2230,7 +2208,7 @@
         try {
             mPM.clearCrossProfileIntentFilters(sourceUserId, mContext.getOpPackageName());
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
         }
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e4d6835..2cb6151 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -308,6 +308,7 @@
     public void setActivityController(IActivityController watcher)
         throws RemoteException;
     public void setLenientBackgroundCheck(boolean enabled) throws RemoteException;
+    public int getMemoryTrimLevel() throws RemoteException;
 
     public void enterSafeMode() throws RemoteException;
 
@@ -980,4 +981,5 @@
     int NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 366;
     int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367;
     int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
+    int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 837ceb6..838b8cb 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -413,8 +413,9 @@
             // as this is early and necessary.
             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
 
-            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
-                    librarySearchPath, libraryPermittedPath, mBaseClassLoader);
+            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
+                    mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
+                    libraryPermittedPath, mBaseClassLoader);
 
             StrictMode.setThreadPolicy(oldPolicy);
             return mClassLoader;
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 3c7f48b..b6e0467 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -95,8 +95,7 @@
     
     private native long loadNativeCode(String path, String funcname, MessageQueue queue,
             String internalDataPath, String obbPath, String externalDataPath, int sdkVersion,
-            AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath,
-            String isolationPath);
+            AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath);
     private native String getDlError();
     private native void unloadNativeCode(long handle);
     private native void onStartNative(long handle);
@@ -177,8 +176,7 @@
                 getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),
                 getAbsolutePath(getExternalFilesDir(null)),
                 Build.VERSION.SDK_INT, getAssets(), nativeSavedState,
-                classLoader, classLoader.getLdLibraryPath(),
-                classLoader.getLibraryPermittedPath());
+                classLoader, classLoader.getLdLibraryPath());
 
         if (mNativeHandle == 0) {
             throw new UnsatisfiedLinkError(
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 402c112..d98c717 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -823,6 +823,12 @@
     public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
 
     /**
+     * {@link #extras} key: whether the chronometer set on the notification should count down
+     * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present.
+     */
+    public static final String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
+
+    /**
      * {@link #extras} key: whether {@link #when} should be shown,
      * as supplied to {@link Builder#setShowWhen(boolean)}.
      */
@@ -2158,8 +2164,12 @@
          *
          * Useful when showing an elapsed time (like an ongoing phone call).
          *
+         * The counter can also be set to count down to <code>when</code> when using
+         * {@link #setChronometerCountsDown(boolean)}.
+         *
          * @see android.widget.Chronometer
          * @see Notification#when
+         * @see #setChronometerCountsDown(boolean)
          */
         public Builder setUsesChronometer(boolean b) {
             mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
@@ -2167,6 +2177,19 @@
         }
 
         /**
+         * Sets the Chronometer to count down instead of counting up.
+         *
+         * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true.
+         * If it isn't set the chronometer will count up.
+         *
+         * @see #setUsesChronometer(boolean)
+         */
+        public Builder setChronometerCountsDown(boolean countsDown) {
+            mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN, countsDown);
+            return this;
+        }
+
+        /**
          * Set the small icon resource, which will be used to represent the notification in the
          * status bar.
          *
@@ -3097,6 +3120,8 @@
                     contentView.setLong(R.id.chronometer, "setBase",
                             mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
+                    boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN);
+                    contentView.setChronometerCountsDown(R.id.chronometer, countsDown);
                 } else {
                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
                     contentView.setLong(R.id.time, "setTime", mN.when);
@@ -3328,6 +3353,8 @@
                     savedBundle.getBoolean(EXTRA_SHOW_WHEN));
             publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
                     savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
+            publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN,
+                    savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN));
             publicExtras.putCharSequence(EXTRA_TITLE,
                     mContext.getString(R.string.notification_hidden_text));
             mN.extras = publicExtras;
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 4cbaf6c..039c9d7 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -102,12 +102,19 @@
     public static final int PRIORITY_SYNC_INITIALIZATION = 20;
 
     /**
-     * Value of {@link #getPriority} for the current foreground app (overrides the supplied
+     * Value of {@link #getPriority} for a foreground app (overrides the supplied
      * JobInfo priority if it is smaller).
      * @hide
      */
     public static final int PRIORITY_FOREGROUND_APP = 30;
 
+    /**
+     * Value of {@link #getPriority} for the current top app (overrides the supplied
+     * JobInfo priority if it is smaller).
+     * @hide
+     */
+    public static final int PRIORITY_TOP_APP = 40;
+
     private final int jobId;
     private final PersistableBundle extras;
     private final ComponentName service;
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index 940a530..95a8ccf 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -46,10 +46,10 @@
      * Job services must be protected with this permission:
      *
      * <pre class="prettyprint">
-     *     <service android:name="MyJobService"
-     *              android:permission="android.permission.BIND_JOB_SERVICE" >
+     *     &#60;service android:name="MyJobService"
+     *              android:permission="android.permission.BIND_JOB_SERVICE" &#62;
      *         ...
-     *     </service>
+     *     &#60;/service&#62;
      * </pre>
      *
      * <p>If a job service is declared in the manifest but not protected with this
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index e58744b..fb16150 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -98,13 +98,15 @@
     /**
      * Default trace file path and file
      */
-    private static final String DEFAULT_TRACE_PATH_PREFIX =
-        Environment.getLegacyExternalStorageDirectory().getPath() + "/";
     private static final String DEFAULT_TRACE_BODY = "dmtrace";
     private static final String DEFAULT_TRACE_EXTENSION = ".trace";
-    private static final String DEFAULT_TRACE_FILE_PATH =
-        DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY
-        + DEFAULT_TRACE_EXTENSION;
+    private static class NoPreloadHolder {
+        private static final String DEFAULT_TRACE_PATH_PREFIX =
+                Environment.getLegacyExternalStorageDirectory().getPath() + "/";
+        private static final String DEFAULT_TRACE_FILE_PATH =
+                DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY
+                + DEFAULT_TRACE_EXTENSION;
+    }
 
 
     /**
@@ -942,7 +944,7 @@
      * tracing.
      */
     public static void startMethodTracing() {
-        VMDebug.startMethodTracing(DEFAULT_TRACE_FILE_PATH, 0, 0, false, 0);
+        VMDebug.startMethodTracing(fixTraceName(null), 0, 0, false, 0);
     }
 
     /**
@@ -1032,9 +1034,9 @@
      */
     private static String fixTraceName(String traceName) {
         if (traceName == null)
-            traceName = DEFAULT_TRACE_FILE_PATH;
+            traceName = NoPreloadHolder.DEFAULT_TRACE_FILE_PATH;
         if (traceName.charAt(0) != '/')
-            traceName = DEFAULT_TRACE_PATH_PREFIX + traceName;
+            traceName = NoPreloadHolder.DEFAULT_TRACE_PATH_PREFIX + traceName;
         if (!traceName.endsWith(DEFAULT_TRACE_EXTENSION))
             traceName = traceName + DEFAULT_TRACE_EXTENSION;
 
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dcc28d6..369ec15 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -224,6 +224,8 @@
      * This is used by Gaming and VR applications to ensure the device provides
      * will provide consistent performance over a large amount of time.
      * </p>
+     *
+     * {@hide}
      */
     public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 0x00000100;
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index da7f85f..0edc43c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -36,7 +36,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
-import android.util.Log;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
@@ -700,8 +699,7 @@
         try {
             return mService.getUserInfo(getUserHandle()).name;
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user name", re);
-            return "";
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -771,8 +769,7 @@
         try {
             return mService.isRestricted();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not check if user is limited ", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -784,8 +781,7 @@
         try {
             return mService.canHaveRestrictedProfile(userId);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not check if user can have restricted profile", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -847,8 +843,8 @@
     public boolean isUserRunning(int userId) {
         try {
             return ActivityManagerNative.getDefault().isUserRunning(userId, 0);
-        } catch (RemoteException e) {
-            return false;
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -864,8 +860,8 @@
             // TODO: reconcile stopped vs stopping?
             return ActivityManagerNative.getDefault().isUserRunning(
                     user.getIdentifier(), ActivityManager.FLAG_OR_STOPPED);
-        } catch (RemoteException e) {
-            return false;
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -893,8 +889,8 @@
         try {
             return ActivityManagerNative.getDefault().isUserRunning(
                     user.getIdentifier(), ActivityManager.FLAG_AND_LOCKED);
-        } catch (RemoteException e) {
-            return false;
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -922,8 +918,8 @@
         try {
             return ActivityManagerNative.getDefault().isUserRunning(
                     user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED);
-        } catch (RemoteException e) {
-            return false;
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -968,8 +964,7 @@
         try {
             return mService.getUserInfo(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user info", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -990,8 +985,7 @@
         try {
             return mService.getUserRestrictions(userHandle.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user restrictions", re);
-            return Bundle.EMPTY;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1007,9 +1001,7 @@
         try {
             return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get base user restrictions for user " +
-                    userHandle.getIdentifier(), re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1065,7 +1057,7 @@
         try {
             mService.setUserRestriction(key, value, userHandle.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set user restriction", re);
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1092,8 +1084,7 @@
             return mService.hasUserRestriction(restrictionKey,
                     userHandle.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not check user restrictions", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1147,7 +1138,7 @@
                 mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
             }
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not create a user", re);
+            throw re.rethrowAsRuntimeException();
         }
         return user;
     }
@@ -1167,7 +1158,7 @@
                         Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
             }
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not create a user", re);
+            throw re.rethrowAsRuntimeException();
         }
         return guest;
     }
@@ -1188,8 +1179,7 @@
         try {
             return mService.createProfileForUser(name, flags, userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not create a user", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1211,10 +1201,9 @@
                         UserHandle.of(user.id));
             }
             return user;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Could not create a restricted profile", e);
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     /**
@@ -1282,8 +1271,7 @@
         try {
             return mService.getSeedAccountName();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get the seed account name", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1297,8 +1285,7 @@
         try {
             return mService.getSeedAccountType();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get the seed account type", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1314,8 +1301,7 @@
         try {
             return mService.getSeedAccountOptions();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get the seed account options", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1336,7 +1322,7 @@
             mService.setSeedAccountData(userId, accountName, accountType, accountOptions,
                     /* persist= */ true);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set the seed account data", re);
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1349,7 +1335,7 @@
         try {
             mService.clearSeedAccountData();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not clear the seed account data", re);
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1364,8 +1350,7 @@
         try {
             return mService.markGuestForDeletion(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not mark guest for deletion", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1384,8 +1369,8 @@
     public void setUserEnabled(@UserIdInt int userHandle) {
         try {
             mService.setUserEnabled(userHandle);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Could not enable the profile", e);
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1407,8 +1392,7 @@
         try {
             return mService.getUsers(false);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user list", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1430,8 +1414,7 @@
             }
             return result;
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get users list", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1447,8 +1430,7 @@
         try {
             return mService.getUserAccount(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user account", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1464,7 +1446,7 @@
         try {
             mService.setUserAccount(userHandle, accountName);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set user account", re);
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1479,8 +1461,7 @@
         try {
             return mService.getPrimaryUser();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get Primary user", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1517,8 +1498,7 @@
         try {
             return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not check if we can add more managed profiles", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1537,8 +1517,7 @@
         try {
             return mService.getProfiles(userHandle, false /* enabledOnly */);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user list", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1553,8 +1532,7 @@
         try {
             return mService.isSameProfileGroup(userId, otherUserId);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user list", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1572,8 +1550,7 @@
         try {
             return mService.getProfiles(userHandle, true /* enabledOnly */);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user list", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1589,8 +1566,7 @@
         try {
             users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user list", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
         for (UserInfo info : users) {
             UserHandle userHandle = new UserHandle(info.id);
@@ -1610,8 +1586,7 @@
         try {
             return mService.getCredentialOwnerProfile(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get credential owner", re);
-            return -1;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1625,8 +1600,7 @@
         try {
             return mService.getProfileParent(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get profile parent", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1640,8 +1614,8 @@
     public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) {
         try {
             mService.setQuietModeEnabled(userHandle, enableQuietMode);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Could not change the profile's quiet mode", e);
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1654,10 +1628,9 @@
     public boolean isQuietModeEnabled(UserHandle userHandle) {
         try {
             return mService.isQuietModeEnabled(userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            Log.w(TAG, "Could not query the profile's quiet mode", e);
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
         }
-        return false;
     }
 
     /**
@@ -1742,8 +1715,7 @@
         try {
             return mService.getUsers(excludeDying);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user list", re);
-            return null;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1757,8 +1729,7 @@
         try {
             return mService.removeUser(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not remove user ", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1774,7 +1745,7 @@
         try {
             mService.setUserName(userHandle, name);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set the user name ", re);
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1788,7 +1759,7 @@
         try {
             mService.setUserIcon(userHandle, icon);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set the user icon ", re);
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1813,7 +1784,7 @@
                 }
             }
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get the user icon ", re);
+            throw re.rethrowAsRuntimeException();
         }
         return null;
     }
@@ -1869,9 +1840,8 @@
         try {
             return mService.getUserSerialNumber(userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get serial number for user " + userHandle);
+            throw re.rethrowAsRuntimeException();
         }
-        return -1;
     }
 
     /**
@@ -1887,9 +1857,8 @@
         try {
             return mService.getUserHandle(userSerialNumber);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get userHandle for user " + userSerialNumber);
+            throw re.rethrowAsRuntimeException();
         }
-        return -1;
     }
 
     /**
@@ -1915,9 +1884,8 @@
         try {
             return mService.getApplicationRestrictions(packageName);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get application restrictions for package " + packageName);
+            throw re.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     /**
@@ -1927,9 +1895,8 @@
         try {
             return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
+            throw re.rethrowAsRuntimeException();
         }
-        return null;
     }
 
     /**
@@ -1940,7 +1907,7 @@
         try {
             mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1964,7 +1931,7 @@
         try {
             mService.setDefaultGuestRestrictions(restrictions);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set guest restrictions");
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -1976,9 +1943,8 @@
         try {
             return mService.getDefaultGuestRestrictions();
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set guest restrictions");
+            throw re.rethrowAsRuntimeException();
         }
-        return new Bundle();
     }
 
     /**
@@ -1991,8 +1957,7 @@
         try {
             return mService.getUserCreationTime(userHandle.getIdentifier());
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not get user creation time", re);
-            return 0;
+            throw re.rethrowAsRuntimeException();
         }
     }
 
@@ -2008,8 +1973,7 @@
         try {
             return mService.someUserHasSeedAccount(accountName, accountType);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not check seed accounts", re);
-            return false;
+            throw re.rethrowAsRuntimeException();
         }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eaff1ac..cb45deb 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -58,6 +58,7 @@
 import android.util.AndroidException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.LocaleList;
 import android.util.Log;
 
 import com.android.internal.util.ArrayUtils;
@@ -2071,6 +2072,8 @@
             if (outConfig.fontScale < 0) {
                 outConfig.fontScale = 1;
             }
+            outConfig.setLocales(LocaleList.forLanguageTags(
+                    Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle)));
         }
 
         /**
@@ -2079,6 +2082,9 @@
          */
         public static void clearConfiguration(Configuration inoutConfig) {
             inoutConfig.fontScale = 0;
+            if (!inoutConfig.userSetLocale) {
+                inoutConfig.setLocales(LocaleList.getEmptyLocaleList());
+            }
         }
 
         /**
@@ -2096,12 +2102,15 @@
         /** @hide */
         public static boolean putConfigurationForUser(ContentResolver cr, Configuration config,
                 int userHandle) {
-            return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle);
+            return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle) &&
+                    Settings.System.putStringForUser(
+                            cr, SYSTEM_LOCALES, config.getLocales().toLanguageTags(), userHandle);
         }
 
         /** @hide */
         public static boolean hasInterestingConfigurationChanges(int changes) {
-            return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
+            return (changes & ActivityInfo.CONFIG_FONT_SCALE) != 0 ||
+                    (changes & ActivityInfo.CONFIG_LOCALE) != 0;
         }
 
         /** @deprecated - Do not use */
@@ -2480,6 +2489,18 @@
         };
 
         /**
+         * The serialized system locale value.
+         *
+         * Do not use this value directory.
+         * To get system locale, use {@link android.util.LocaleList#getDefault} instead.
+         * To update system locale, use {@link com.android.internal.app.LocalePicker#updateLocales}
+         * instead.
+         * @hide
+         */
+        public static final String SYSTEM_LOCALES = "system_locales";
+
+
+        /**
          * Name of an application package to be debugged.
          *
          * @deprecated Use {@link Global#DEBUG_APP} instead
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7b461b1..53c92bf 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -929,26 +929,31 @@
         public static final int IMPORTANCE_NONE = 0;
 
         /**
-         * Low notification importance: only shows in the shade, below the fold.
+         * Min notification importance: only shows in the shade, below the fold.
          */
-        public static final int IMPORTANCE_LOW = 1;
+        public static final int IMPORTANCE_MIN = 1;
 
         /**
-         * Default notification importance: shows everywhere, but is not intrusive.
+         * Low notification importance: shows everywhere, but is not intrusive.
          */
-        public static final int IMPORTANCE_DEFAULT = 2;
+        public static final int IMPORTANCE_LOW = 2;
 
         /**
-         * Higher notification importance: shows everywhere, makes noise,
+         * Default notification importance: shows everywhere, allowed to makes noise,
          * but does not visually intrude.
          */
-        public static final int IMPORTANCE_HIGH = 3;
+        public static final int IMPORTANCE_DEFAULT = 3;
 
         /**
-         * Highest notification importance: shows everywhere, makes noise,
-         * and also visually intrudes.
+         * Higher notification importance: shows everywhere, allowed to makes noise and peek.
          */
-        public static final int IMPORTANCE_MAX = 4;
+        public static final int IMPORTANCE_HIGH = 4;
+
+        /**
+         * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
+         * use full screen intents.
+         */
+        public static final int IMPORTANCE_MAX = 5;
 
         private String mKey;
         private int mRank = -1;
@@ -1041,7 +1046,7 @@
                 CharSequence explanation) {
             mKey = key;
             mRank = rank;
-            mIsAmbient = importance < IMPORTANCE_DEFAULT;
+            mIsAmbient = importance < IMPORTANCE_LOW;
             mMatchesInterruptionFilter = matchesInterruptionFilter;
             mVisibilityOverride = visibilityOverride;
             mSuppressedVisualEffects = suppressedVisualEffects;
@@ -1058,6 +1063,8 @@
                     return "UNSPECIFIED";
                 case IMPORTANCE_NONE:
                     return "NONE";
+                case IMPORTANCE_MIN:
+                    return "MIN";
                 case IMPORTANCE_LOW:
                     return "LOW";
                 case IMPORTANCE_DEFAULT:
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 367c9fb..e79dfca 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -437,6 +437,7 @@
 
         if (handled) {
             adjustMetaAfterKeypress(content);
+            return true;
         }
 
         return super.onKeyDown(view, content, keyCode, event);
@@ -470,4 +471,3 @@
         return true;
     }
 }
-
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index a75e8a7..ad78b68 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -64,6 +64,12 @@
      */
     public static final int TYPE_ACCESSIBILITY_OVERLAY = 4;
 
+    /**
+     * Window type: A system window used to divide the screen in split-screen mode.
+     * This type of window is present only in split-screen mode.
+     */
+    public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
+
     private static final int UNDEFINED = -1;
 
     private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
@@ -551,6 +557,9 @@
             case TYPE_ACCESSIBILITY_OVERLAY: {
                 return "TYPE_ACCESSIBILITY_OVERLAY";
             }
+            case TYPE_SPLIT_SCREEN_DIVIDER: {
+                return "TYPE_SPLIT_SCREEN_DIVIDER";
+            }
             default:
                 return "<UNKNOWN>";
         }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 4c81d1a..54b3932 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1632,7 +1632,6 @@
 
         final PopupDecorView decorView = mDecorView;
         final View contentView = mContentView;
-        final OnDismissListener dismissListener = mOnDismissListener;
 
         final ViewGroup contentHolder;
         final ViewParent contentParent = contentView.getParent();
@@ -1676,16 +1675,19 @@
                     new TransitionListenerAdapter() {
                         @Override
                         public void onTransitionEnd(Transition transition) {
-                            dismissImmediate(decorView, contentHolder,
-                                    contentView, dismissListener);
+                            dismissImmediate(decorView, contentHolder, contentView);
                         }
                     });
         } else {
-            dismissImmediate(decorView, contentHolder, contentView, dismissListener);
+            dismissImmediate(decorView, contentHolder, contentView);
         }
 
         // Clears the anchor view.
         unregisterForViewTreeChanges();
+
+        if (mOnDismissListener != null) {
+            mOnDismissListener.onDismiss();
+        }
     }
 
     /**
@@ -1727,8 +1729,7 @@
      * Removes the popup from the window manager and tears down the supporting
      * view hierarchy, if necessary.
      */
-    private void dismissImmediate(View decorView, ViewGroup contentHolder,
-            View contentView, OnDismissListener listener) {
+    private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
         // If this method gets called and the decor view doesn't have a parent,
         // then it was either never added or was already removed. That should
         // never happen, but it's worth checking to avoid potential crashes.
@@ -1745,10 +1746,6 @@
         mDecorView = null;
         mBackgroundView = null;
         mIsTransitioningToDismiss = false;
-
-        if (mOnDismissListener != null) {
-            mOnDismissListener.onDismiss();
-        }
     }
 
     /**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1cccfae..063288e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2563,6 +2563,8 @@
      * @param format The Chronometer format string, or null to
      *               simply display the timer value.
      * @param started True if you want the clock to be started, false if not.
+     *
+     * @see #setChronometerCountsDown(int, boolean)
      */
     public void setChronometer(int viewId, long base, String format, boolean started) {
         setLong(viewId, "setBase", base);
@@ -2571,6 +2573,18 @@
     }
 
     /**
+     * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
+     * the chronometer with the given viewId.
+     *
+     * @param viewId The id of the {@link Chronometer} to change
+     * @param isCountDown True if you want the chronometer to count down to base instead of
+     *                    counting up.
+     */
+    public void setChronometerCountsDown(int viewId, boolean isCountDown) {
+        setBoolean(viewId, "setCountDown", isCountDown);
+    }
+
+    /**
      * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax},
      * {@link ProgressBar#setProgress ProgressBar.setProgress}, and
      * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate}
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 6a365e0..b1b019c 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -275,7 +275,7 @@
             config.setLocales(locales);
             config.userSetLocale = true;
 
-            am.updateConfiguration(config);
+            am.updatePersistentConfiguration(config);
             // Trigger the dirty bit for the Settings Provider.
             BackupManager.dataChanged("com.android.providers.settings");
         } catch (RemoteException e) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 08d4fba..64c5b8d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -60,6 +60,7 @@
     void showRecentApps(boolean triggeredFromAltTab);
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecentApps();
+    void toggleSplitScreen();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
     void showScreenPinningRequest();
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 77859b8..451078b 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -21,7 +21,7 @@
 import android.os.Handler;
 import android.os.Message;
 
- /**
+/**
  * An AlarmListener that sends the specified message to a Handler and keeps the system awake until
  * the message is processed.
  *
@@ -33,19 +33,17 @@
  * the message, but does not guarantee that the system will be awake until the target object has
  * processed it. This is because as soon as the onAlarmListener sends the message and returns, the
  * AlarmManager releases its wakelock and the system is free to go to sleep again.
- *
  */
 public class WakeupMessage implements AlarmManager.OnAlarmListener {
-    private static AlarmManager sAlarmManager;
+    private final AlarmManager mAlarmManager;
     private final Handler mHandler;
     private final String mCmdName;
     private final int mCmd, mArg1, mArg2;
+    private boolean mScheduled;
 
     public WakeupMessage(Context context, Handler handler,
             String cmdName, int cmd, int arg1, int arg2) {
-        if (sAlarmManager == null) {
-            sAlarmManager = context.getSystemService(AlarmManager.class);
-        }
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mHandler = handler;
         mCmdName = cmdName;
         mCmd = cmd;
@@ -61,19 +59,43 @@
         this(context, handler, cmdName, cmd, 0, 0);
     }
 
-    public void schedule(long when) {
-        sAlarmManager.setExact(
+    /**
+     * Schedule the message to be delivered at the time in milliseconds of the
+     * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup
+     * the device when it goes off. If schedule is called multiple times without the message being
+     * dispatched then the alarm is rescheduled to the new time.
+     */
+    public synchronized void schedule(long when) {
+        mAlarmManager.setExact(
                 AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler);
+        mScheduled = true;
     }
 
-    public void cancel() {
-        sAlarmManager.cancel(this);
+    /**
+     * Cancel all pending messages. This includes alarms that may have been fired, but have not been
+     * run on the handler yet.
+     */
+    public synchronized void cancel() {
+        if (mScheduled) {
+            mAlarmManager.cancel(this);
+            mScheduled = false;
+        }
     }
 
     @Override
     public void onAlarm() {
-        Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
-        mHandler.handleMessage(msg);
-        msg.recycle();
+        // Once this method is called the alarm has already been fired and removed from
+        // AlarmManager (it is still partially tracked, but only for statistics). The alarm can now
+        // be marked as unscheduled so that it can be rescheduled in the message handler.
+        final boolean stillScheduled;
+        synchronized (this) {
+            stillScheduled = mScheduled;
+            mScheduled = false;
+        }
+        if (stillScheduled) {
+            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+            mHandler.handleMessage(msg);
+            msg.recycle();
+        }
     }
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8b686b7..a8d684d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -7,7 +7,7 @@
 LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_CFLAGS += -Wno-non-virtual-dtor
 LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-LOCAL_CFLAGS += -DHWUI_NEW_OPS
+#LOCAL_CFLAGS += -DHWUI_NEW_OPS
 LOCAL_CPPFLAGS += -Wno-conversion-null
 
 ifeq ($(TARGET_ARCH), arm)
@@ -33,6 +33,7 @@
     com_android_internal_content_NativeLibraryHelper.cpp \
     com_google_android_gles_jni_EGLImpl.cpp \
     com_google_android_gles_jni_GLImpl.cpp.arm \
+    android_app_ApplicationLoaders.cpp \
     android_app_NativeActivity.cpp \
     android_auditing_SecurityLog.cpp \
     android_opengl_EGL14.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 017fb53..6ed07a7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@
 extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
 extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
 extern int register_android_app_backup_FullBackup(JNIEnv *env);
+extern int register_android_app_ApplicationLoaders(JNIEnv* env);
 extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_media_RemoteDisplay(JNIEnv *env);
@@ -1371,6 +1372,7 @@
     REG_JNI(register_android_backup_FileBackupHelperBase),
     REG_JNI(register_android_backup_BackupHelperDispatcher),
     REG_JNI(register_android_app_backup_FullBackup),
+    REG_JNI(register_android_app_ApplicationLoaders),
     REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_util_jar_StrictJarFile),
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
new file mode 100644
index 0000000..89f22eb
--- /dev/null
+++ b/core/jni/android_app_ApplicationLoaders.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "nativeloader/native_loader.h"
+
+#include "core_jni_helpers.h"
+
+
+static jstring createClassloaderNamespace_native(JNIEnv* env,
+                                              jobject clazz,
+                                              jobject classLoader,
+                                              jint targetSdkVersion,
+                                              jstring librarySearchPath,
+                                              jstring libraryPermittedPath,
+                                              jboolean isShared) {
+    return android::CreateClassLoaderNamespace(env, targetSdkVersion,
+                                               classLoader, isShared == JNI_TRUE,
+                                               librarySearchPath, libraryPermittedPath);
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "createClassloaderNamespace",
+      "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;",
+      reinterpret_cast<void*>(createClassloaderNamespace_native) },
+};
+
+static const char* const kApplicationLoadersPathName = "android/app/ApplicationLoaders";
+
+namespace android
+{
+
+int register_android_app_ApplicationLoaders(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, kApplicationLoadersPathName, g_methods, NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 88a56d2..6431b94 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -259,8 +259,7 @@
 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
         jobject messageQueue, jstring internalDataDir, jstring obbDir,
         jstring externalDataDir, jint sdkVersion, jobject jAssetMgr,
-        jbyteArray savedState, jobject classLoader, jstring libraryPath,
-        jstring isolationPath) {
+        jbyteArray savedState, jobject classLoader, jstring libraryPath) {
     if (kLogTrace) {
         ALOGD("loadNativeCode_native");
     }
@@ -269,8 +268,7 @@
     std::unique_ptr<NativeCode> code;
     bool needNativeBridge = false;
 
-    void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader,
-                                     false, libraryPath, isolationPath);
+    void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader, libraryPath);
     if (handle == NULL) {
         if (NativeBridgeIsSupported(pathStr)) {
             handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
@@ -656,7 +654,7 @@
 
 static const JNINativeMethod g_methods[] = {
     { "loadNativeCode",
-        "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[BLjava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)J",
+        "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[BLjava/lang/ClassLoader;Ljava/lang/String;)J",
         (void*)loadNativeCode_native },
     { "getDlError", "()Ljava/lang/String;", (void*) getDlError_native },
     { "unloadNativeCode", "(J)V", (void*)unloadNativeCode_native },
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index d0326f1..3e4e352 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -180,54 +180,15 @@
 // ----------------------------------------------------------------------------
 static jint
 android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-        jobject jaa, jint sampleRateInHertz, jint channelMask, jint channelIndexMask,
-        jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName)
+        jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
+        jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
+        jlong nativeRecordInJavaObj)
 {
     //ALOGV(">> Entering android_media_AudioRecord_setup");
-    //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
-    //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
-
-    if (jaa == 0) {
-        ALOGE("Error creating AudioRecord: invalid audio attributes");
-        return (jint) AUDIO_JAVA_ERROR;
-    }
-
-    // channel index mask takes priority over channel position masks.
-    if (channelIndexMask) {
-        // Java channel index masks need the representation bits set.
-        channelMask = audio_channel_mask_from_representation_and_bits(
-                AUDIO_CHANNEL_REPRESENTATION_INDEX,
-                channelIndexMask);
-    }
-    // Java channel position masks map directly to the native definition
-
-    if (!audio_is_input_channel(channelMask)) {
-        ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
-        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
-    }
-    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
-
-    // compare the format against the Java constants
-    audio_format_t format = audioFormatToNative(audioFormat);
-    if (format == AUDIO_FORMAT_INVALID) {
-        ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
-        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
-    }
-
-    size_t bytesPerSample = audio_bytes_per_sample(format);
-
-    if (buffSizeInBytes == 0) {
-         ALOGE("Error creating AudioRecord: frameCount is 0.");
-        return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
-    }
-    size_t frameSize = channelCount * bytesPerSample;
-    size_t frameCount = buffSizeInBytes / frameSize;
-
-    jclass clazz = env->GetObjectClass(thiz);
-    if (clazz == NULL) {
-        ALOGE("Can't find %s when setting up callback.", kClassPathName);
-        return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
-    }
+    //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
+    //     "nativeRecordInJavaObj=0x%llX",
+    //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj);
+    audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask);
 
     if (jSession == NULL) {
         ALOGE("Error creating AudioRecord: invalid session ID pointer");
@@ -243,55 +204,132 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
-
-    // create an uninitialized AudioRecord object
-    sp<AudioRecord> lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
-
     audio_attributes_t *paa = NULL;
-    // read the AudioAttributes values
-    paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
-    const jstring jtags =
-            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
-    const char* tags = env->GetStringUTFChars(jtags, NULL);
-    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
-    strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
-    env->ReleaseStringUTFChars(jtags, tags);
-    paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
-    paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
-    ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
+    sp<AudioRecord> lpRecorder = 0;
+    audiorecord_callback_cookie *lpCallbackData = NULL;
 
-    audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
-    if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
-        flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
+    jclass clazz = env->GetObjectClass(thiz);
+    if (clazz == NULL) {
+        ALOGE("Can't find %s when setting up callback.", kClassPathName);
+        return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
     }
-    // create the callback information:
-    // this data will be passed with every AudioRecord callback
-    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
-    lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
-    // we use a weak reference so the AudioRecord object can be garbage collected.
-    lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
-    lpCallbackData->busy = false;
 
-    const status_t status = lpRecorder->set(paa->source,
-        sampleRateInHertz,
-        format,        // word length, PCM
-        channelMask,
-        frameCount,
-        recorderCallback,// callback_t
-        lpCallbackData,// void* user
-        0,             // notificationFrames,
-        true,          // threadCanCallJava
-        sessionId,
-        AudioRecord::TRANSFER_DEFAULT,
-        flags,
-        -1, -1,        // default uid, pid
-        paa);
+    // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
+    if (nativeRecordInJavaObj == 0) {
+        if (jaa == 0) {
+            ALOGE("Error creating AudioRecord: invalid audio attributes");
+            return (jint) AUDIO_JAVA_ERROR;
+        }
 
-    if (status != NO_ERROR) {
-        ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
-                status);
-        goto native_init_failure;
+        if (jSampleRate == 0) {
+            ALOGE("Error creating AudioRecord: invalid sample rates");
+            return (jint) AUDIO_JAVA_ERROR;
+        }
+        jint elements[1];
+        env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
+        int sampleRateInHertz = elements[0];
+
+        // channel index mask takes priority over channel position masks.
+        if (channelIndexMask) {
+            // Java channel index masks need the representation bits set.
+            localChanMask = audio_channel_mask_from_representation_and_bits(
+                    AUDIO_CHANNEL_REPRESENTATION_INDEX,
+                    channelIndexMask);
+        }
+        // Java channel position masks map directly to the native definition
+
+        if (!audio_is_input_channel(localChanMask)) {
+            ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask);
+            return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
+        }
+        uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask);
+
+        // compare the format against the Java constants
+        audio_format_t format = audioFormatToNative(audioFormat);
+        if (format == AUDIO_FORMAT_INVALID) {
+            ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
+            return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
+        }
+
+        size_t bytesPerSample = audio_bytes_per_sample(format);
+
+        if (buffSizeInBytes == 0) {
+             ALOGE("Error creating AudioRecord: frameCount is 0.");
+            return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
+        }
+        size_t frameSize = channelCount * bytesPerSample;
+        size_t frameCount = buffSizeInBytes / frameSize;
+
+        ScopedUtfChars opPackageNameStr(env, opPackageName);
+
+        // create an uninitialized AudioRecord object
+        lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
+
+        // read the AudioAttributes values
+        paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+        const jstring jtags =
+                (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
+        const char* tags = env->GetStringUTFChars(jtags, NULL);
+        // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+        strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+        env->ReleaseStringUTFChars(jtags, tags);
+        paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
+        paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+        ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
+
+        audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
+        if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
+            flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
+        }
+        // create the callback information:
+        // this data will be passed with every AudioRecord callback
+        lpCallbackData = new audiorecord_callback_cookie;
+        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
+        // we use a weak reference so the AudioRecord object can be garbage collected.
+        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
+        lpCallbackData->busy = false;
+
+        const status_t status = lpRecorder->set(paa->source,
+            sampleRateInHertz,
+            format,        // word length, PCM
+            localChanMask,
+            frameCount,
+            recorderCallback,// callback_t
+            lpCallbackData,// void* user
+            0,             // notificationFrames,
+            true,          // threadCanCallJava
+            sessionId,
+            AudioRecord::TRANSFER_DEFAULT,
+            flags,
+            -1, -1,        // default uid, pid
+            paa);
+
+        if (status != NO_ERROR) {
+            ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
+                    status);
+            goto native_init_failure;
+        }
+    } else { // end if nativeRecordInJavaObj == 0)
+        lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
+        // TODO: We need to find out which members of the Java AudioRecord might need to be
+        // initialized from the Native AudioRecord
+        // these are directly returned from getters:
+        //  mSampleRate
+        //  mRecordSource
+        //  mAudioFormat
+        //  mChannelMask
+        //  mChannelCount
+        //  mState (?)
+        //  mRecordingState (?)
+        //  mPreferredDevice
+
+        // create the callback information:
+        // this data will be passed with every AudioRecord callback
+        lpCallbackData = new audiorecord_callback_cookie;
+        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
+        // we use a weak reference so the AudioRecord object can be garbage collected.
+        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
+        lpCallbackData->busy = false;
     }
 
     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -304,6 +342,11 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
+    {
+        const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
+        env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
+    }
+
     {   // scope for the lock
         Mutex::Autolock l(sLock);
         sAudioRecordCallBackCookies.add(lpCallbackData);
@@ -717,8 +760,8 @@
     // name,               signature,  funcPtr
     {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
     {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
-    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;IIIII[ILjava/lang/String;)I",
-                                       (void *)android_media_AudioRecord_setup},
+    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
+                                      (void *)android_media_AudioRecord_setup},
     {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
     {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
     {"native_read_in_byte_array",
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1ab9504..660cbdc 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -213,51 +213,17 @@
 
 // ----------------------------------------------------------------------------
 static jint
-android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-        jobject jaa,
-        jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
-        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
+android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
+        jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
+        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
+        jlong nativeAudioTrack) {
 
-    ALOGV("sampleRate=%d, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d",
-        sampleRateInHertz, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes);
+    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
+        "nativeAudioTrack=0x%llX",
+        jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
+        nativeAudioTrack);
 
-    if (jaa == 0) {
-        ALOGE("Error creating AudioTrack: invalid audio attributes");
-        return (jint) AUDIO_JAVA_ERROR;
-    }
-
-    // Invalid channel representations are caught by !audio_is_output_channel() below.
-    audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
-            channelPositionMask, channelIndexMask);
-    if (!audio_is_output_channel(nativeChannelMask)) {
-        ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
-        return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
-    }
-
-    uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
-
-    // check the format.
-    // This function was called from Java, so we compare the format against the Java constants
-    audio_format_t format = audioFormatToNative(audioFormat);
-    if (format == AUDIO_FORMAT_INVALID) {
-        ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
-        return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
-    }
-
-    // compute the frame count
-    size_t frameCount;
-    if (audio_has_proportional_frames(format)) {
-        const size_t bytesPerSample = audio_bytes_per_sample(format);
-        frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
-    } else {
-        frameCount = buffSizeInBytes;
-    }
-
-    jclass clazz = env->GetObjectClass(thiz);
-    if (clazz == NULL) {
-        ALOGE("Can't find %s when setting up callback.", kClassPathName);
-        return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
-    }
+    sp<AudioTrack> lpTrack = 0;
 
     if (jSession == NULL) {
         ALOGE("Error creating AudioTrack: invalid session ID pointer");
@@ -273,91 +239,168 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
-    // create the native AudioTrack object
-    sp<AudioTrack> lpTrack = new AudioTrack();
+    AudioTrackJniStorage* lpJniStorage = NULL;
 
     audio_attributes_t *paa = NULL;
-    // read the AudioAttributes values
-    paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
-    const jstring jtags =
-            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
-    const char* tags = env->GetStringUTFChars(jtags, NULL);
-    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
-    strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
-    env->ReleaseStringUTFChars(jtags, tags);
-    paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
-    paa->content_type =
-            (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
-    paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
 
-    ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
-            paa->usage, paa->content_type, paa->flags, paa->tags);
+    jclass clazz = env->GetObjectClass(thiz);
+    if (clazz == NULL) {
+        ALOGE("Can't find %s when setting up callback.", kClassPathName);
+        return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
+    }
 
-    // initialize the callback information:
-    // this data will be passed with every AudioTrack callback
-    AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
-    lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
-    // we use a weak reference so the AudioTrack object can be garbage collected.
-    lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
-    lpJniStorage->mCallbackData.busy = false;
+    // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
+    if (nativeAudioTrack == 0) {
+        if (jaa == 0) {
+            ALOGE("Error creating AudioTrack: invalid audio attributes");
+            return (jint) AUDIO_JAVA_ERROR;
+        }
 
-    // initialize the native AudioTrack object
-    status_t status = NO_ERROR;
-    switch (memoryMode) {
-    case MODE_STREAM:
+        if (jSampleRate == 0) {
+            ALOGE("Error creating AudioTrack: invalid sample rates");
+            return (jint) AUDIO_JAVA_ERROR;
+        }
 
-        status = lpTrack->set(
-                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
-                sampleRateInHertz,
-                format,// word length, PCM
-                nativeChannelMask,
-                frameCount,
-                AUDIO_OUTPUT_FLAG_NONE,
-                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
-                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
-                0,// shared mem
-                true,// thread can call Java
-                sessionId,// audio session ID
-                AudioTrack::TRANSFER_SYNC,
-                NULL,                         // default offloadInfo
-                -1, -1,                       // default uid, pid values
-                paa);
-        break;
+        int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
+        int sampleRateInHertz = sampleRates[0];
+        env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
 
-    case MODE_STATIC:
-        // AudioTrack is using shared memory
+        // Invalid channel representations are caught by !audio_is_output_channel() below.
+        audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
+                channelPositionMask, channelIndexMask);
+        if (!audio_is_output_channel(nativeChannelMask)) {
+            ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
+            return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
+        }
 
-        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
-            ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
+        uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
+
+        // check the format.
+        // This function was called from Java, so we compare the format against the Java constants
+        audio_format_t format = audioFormatToNative(audioFormat);
+        if (format == AUDIO_FORMAT_INVALID) {
+            ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
+            return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
+        }
+
+        // compute the frame count
+        size_t frameCount;
+        if (audio_is_linear_pcm(format)) {
+            const size_t bytesPerSample = audio_bytes_per_sample(format);
+            frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
+        } else {
+            frameCount = buffSizeInBytes;
+        }
+
+        // create the native AudioTrack object
+        lpTrack = new AudioTrack();
+
+        // read the AudioAttributes values
+        paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
+        const jstring jtags =
+                (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
+        const char* tags = env->GetStringUTFChars(jtags, NULL);
+        // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
+        strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+        env->ReleaseStringUTFChars(jtags, tags);
+        paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
+        paa->content_type =
+                (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
+        paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+
+        ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
+                paa->usage, paa->content_type, paa->flags, paa->tags);
+
+        // initialize the callback information:
+        // this data will be passed with every AudioTrack callback
+        lpJniStorage = new AudioTrackJniStorage();
+        lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
+        // we use a weak reference so the AudioTrack object can be garbage collected.
+        lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+        lpJniStorage->mCallbackData.busy = false;
+
+        // initialize the native AudioTrack object
+        status_t status = NO_ERROR;
+        switch (memoryMode) {
+        case MODE_STREAM:
+
+            status = lpTrack->set(
+                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
+                    sampleRateInHertz,
+                    format,// word length, PCM
+                    nativeChannelMask,
+                    frameCount,
+                    AUDIO_OUTPUT_FLAG_NONE,
+                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
+                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
+                    0,// shared mem
+                    true,// thread can call Java
+                    sessionId,// audio session ID
+                    AudioTrack::TRANSFER_SYNC,
+                    NULL,                         // default offloadInfo
+                    -1, -1,                       // default uid, pid values
+                    paa);
+            break;
+
+        case MODE_STATIC:
+            // AudioTrack is using shared memory
+
+            if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
+                ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
+                goto native_init_failure;
+            }
+
+            status = lpTrack->set(
+                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
+                    sampleRateInHertz,
+                    format,// word length, PCM
+                    nativeChannelMask,
+                    frameCount,
+                    AUDIO_OUTPUT_FLAG_NONE,
+                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
+                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
+                    lpJniStorage->mMemBase,// shared mem
+                    true,// thread can call Java
+                    sessionId,// audio session ID
+                    AudioTrack::TRANSFER_SHARED,
+                    NULL,                         // default offloadInfo
+                    -1, -1,                       // default uid, pid values
+                    paa);
+            break;
+
+        default:
+            ALOGE("Unknown mode %d", memoryMode);
             goto native_init_failure;
         }
 
-        status = lpTrack->set(
-                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
-                sampleRateInHertz,
-                format,// word length, PCM
-                nativeChannelMask,
-                frameCount,
-                AUDIO_OUTPUT_FLAG_NONE,
-                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
-                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
-                lpJniStorage->mMemBase,// shared mem
-                true,// thread can call Java
-                sessionId,// audio session ID
-                AudioTrack::TRANSFER_SHARED,
-                NULL,                         // default offloadInfo
-                -1, -1,                       // default uid, pid values
-                paa);
-        break;
+        if (status != NO_ERROR) {
+            ALOGE("Error %d initializing AudioTrack", status);
+            goto native_init_failure;
+        }
+    } else {  // end if (nativeAudioTrack == 0)
+        lpTrack = (AudioTrack*)nativeAudioTrack;
+        // TODO: We need to find out which members of the Java AudioTrack might
+        // need to be initialized from the Native AudioTrack
+        // these are directly returned from getters:
+        //  mSampleRate
+        //  mAudioFormat
+        //  mStreamType
+        //  mChannelConfiguration
+        //  mChannelCount
+        //  mState (?)
+        //  mPlayState (?)
+        // these may be used internally (Java AudioTrack.audioParamCheck():
+        //  mChannelMask
+        //  mChannelIndexMask
+        //  mDataLoadMode
 
-    default:
-        ALOGE("Unknown mode %d", memoryMode);
-        goto native_init_failure;
-    }
-
-    if (status != NO_ERROR) {
-        ALOGE("Error %d initializing AudioTrack", status);
-        goto native_init_failure;
+        // initialize the callback information:
+        // this data will be passed with every AudioTrack callback
+        lpJniStorage = new AudioTrackJniStorage();
+        lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
+        // we use a weak reference so the AudioTrack object can be garbage collected.
+        lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+        lpJniStorage->mCallbackData.busy = false;
     }
 
     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -370,6 +413,11 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
+    {
+        const jint elements[1] = { (jint) lpTrack->getSampleRate() };
+        env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
+    }
+
     {   // scope for the lock
         Mutex::Autolock l(sLock);
         sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
@@ -385,9 +433,11 @@
     // since we had audio attributes, the stream type was derived from them during the
     // creation of the native AudioTrack: push the same value to the Java object
     env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
-    // audio attributes were copied in AudioTrack creation
-    free(paa);
-    paa = NULL;
+    if (paa != NULL) {
+        // audio attributes were copied in AudioTrack creation
+        free(paa);
+        paa = NULL;
+    }
 
 
     return (jint) AUDIO_JAVA_SUCCESS;
@@ -409,7 +459,6 @@
     return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
 }
 
-
 // ----------------------------------------------------------------------------
 static void
 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
@@ -1114,7 +1163,7 @@
     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
     {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
     {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
-    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;IIIIII[I)I",
+    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
                                          (void *)android_media_AudioTrack_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index c9b5af7..2d69eaa 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -5155,6 +5155,21 @@
     }
 }
 
+/* void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint offset ) */
+static void
+android_glReadPixels__IIIIIII
+  (JNIEnv *_env, jobject _this, jint x, jint y, jint width, jint height, jint format, jint type, jint offset) {
+    glReadPixels(
+        (GLint)x,
+        (GLint)y,
+        (GLsizei)width,
+        (GLsizei)height,
+        (GLenum)format,
+        (GLenum)type,
+        reinterpret_cast<GLvoid *>(offset)
+    );
+}
+
 static const char *classPathName = "android/opengl/GLES30";
 
 static const JNINativeMethod methods[] = {
@@ -5320,6 +5335,7 @@
 {"glTexStorage3D", "(IIIIII)V", (void *) android_glTexStorage3D__IIIIII },
 {"glGetInternalformativ", "(IIII[II)V", (void *) android_glGetInternalformativ__IIII_3II },
 {"glGetInternalformativ", "(IIIILjava/nio/IntBuffer;)V", (void *) android_glGetInternalformativ__IIIILjava_nio_IntBuffer_2 },
+{"glReadPixels", "(IIIIIII)V", (void *) android_glReadPixels__IIIIIII },
 };
 
 int register_android_opengl_jni_GLES30(JNIEnv *_env)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 30d6f29..03d93a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -965,16 +965,6 @@
     <!-- INSTALL PERMISSIONS                                                    -->
     <!-- ====================================================================== -->
 
-`   <!-- =========================================== -->
-    <!-- Permissions for accessing contact metadata -->
-    <!-- =========================================== -->
-    <eat-comment />
-
-    <!-- @SystemApi Allows an application to read/write contact metadata.
-         <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.READ_WRITE_CONTACT_METADATA"
-        android:protectionLevel="signature|system" />
-
     <!-- ================================== -->
     <!-- Permissions for accessing messages -->
     <!-- ================================== -->
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index a3a3112..ae19150 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -25,7 +25,7 @@
     <bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
 
     <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
-    <string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
+    <string translatable="false" name="config_defaultPictureInPictureBounds">"1420 100 1820 325"</string>
 
     <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
          is located in center. -->
@@ -34,5 +34,4 @@
     <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
          when the PIP is shown with Recents. -->
     <string translatable="false" name="config_pictureInPictureBoundsInRecents">"1480 123 1760 303"</string>
-
 </resources>
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index f949e1a..3c6c32e 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -12,7 +12,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test
+    android-support-test \
+    mockito-target
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
new file mode 100644
index 0000000..da8bc1d
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit tests for {@link com.android.internal.util.WakeupMessage}.
+ */
+@SmallTest
+public class WakeupMessageTest {
+    private static final String TEST_CMD_NAME = "TEST cmd Name";
+    private static final int TEST_CMD = 18;
+    private static final int TEST_ARG1 = 33;
+    private static final int TEST_ARG2 = 182;
+
+    @Mock AlarmManager mAlarmManager;
+    WakeupMessage mMessage;
+    // Make a spy so that we can verify calls to it
+    @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler();
+
+    ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor =
+            ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+
+    /**
+     * A Handler that will capture the most recent message sent to it.
+     *
+     * This handler is setup on the main Looper
+     */
+    public static class MessageCapturingHandler extends Handler {
+        private Message mLastMessage;
+
+        public MessageCapturingHandler() {
+            super(Looper.getMainLooper(), /* Nothing is actually dispatched on this Looper */
+                    null, false);
+        }
+
+        @Override
+        public void handleMessage(Message m) {
+            // need to copy since it will be recycled after this method returns
+            mLastMessage = Message.obtain(m);
+        }
+
+        public Message getLastMessage() {
+            return mLastMessage;
+        }
+    }
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        Context context = mock(Context.class);
+        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+        // capture the listener for each AlarmManager.setExact call
+        doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class),
+                mListenerCaptor.capture(), any(Handler.class));
+
+        mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
+                TEST_ARG2);
+    }
+
+    /**
+     * Ensure the test is cleaned up and ready for the next test.
+     */
+    @After
+    public void cleanup() {
+        validateMockitoUsage();
+    }
+
+    private void scheduleAndVerifyAlarm(long when) {
+        mMessage.schedule(when);
+        verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when),
+                eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler));
+    }
+
+    private void verifyMessageDispatchedOnce() {
+        verify(mHandler, times(1)).handleMessage(any(Message.class));
+        assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
+        assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
+        assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
+    }
+
+    /**
+     * Schedule and deliver a single message
+     */
+    @Test
+    public void scheduleAndDeliverMessage() {
+        final long when = 1001;
+        scheduleAndVerifyAlarm(when);
+        verify(mHandler, never()).handleMessage(any(Message.class));
+        mListenerCaptor.getValue().onAlarm();
+        verifyMessageDispatchedOnce();
+    }
+
+    /**
+     * Check that the message is not delivered if cancel is called it after its alarm fires but
+     * before onAlarm is called.
+     *
+     * This ensures that if cancel is called on the handler thread, any previously-scheduled message
+     * is guaranteed not to be delivered.
+     */
+    @Test
+    public void scheduleAndCancelMessage() {
+        final long when = 1010;
+        scheduleAndVerifyAlarm(when);
+        mMessage.cancel();
+        mListenerCaptor.getValue().onAlarm();
+        verify(mHandler, never()).handleMessage(any(Message.class));
+    }
+
+    /**
+     * Verify nothing happens when cancel is called without a schedule
+     */
+    @Test
+    public void cancelWithoutSchedule() {
+        mMessage.cancel();
+    }
+
+    /**
+     * Verify that the message is silently rescheduled if schedule is called twice without the
+     * message being dispatched first.
+     */
+    @Test
+    public void scheduleTwiceWithoutMessageDispatched() {
+        final long when1 = 1011;
+        final long when2 = 1012;
+        scheduleAndVerifyAlarm(when1);
+        scheduleAndVerifyAlarm(when2);
+        mListenerCaptor.getValue().onAlarm();
+        verifyMessageDispatchedOnce();
+    }
+
+}
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index d05c66a..3b0e7e8 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -898,19 +898,19 @@
          * @param res the resources used to inflate density-dependent values
          */
         final void updateDensity(Resources res) {
-            if (mSourceRes != null) {
+            if (res != null) {
                 mSourceRes = res;
-            }
 
-            // The density may have changed since the last update (if any). Any
-            // dimension-type attributes will need their default values scaled.
-            final int targetDensity = Drawable.resolveDensity(res, mDensity);
-            final int sourceDensity = mDensity;
-            mDensity = targetDensity;
+                // The density may have changed since the last update (if any). Any
+                // dimension-type attributes will need their default values scaled.
+                final int targetDensity = Drawable.resolveDensity(res, mDensity);
+                final int sourceDensity = mDensity;
+                mDensity = targetDensity;
 
-            if (sourceDensity != targetDensity) {
-                mCheckedConstantSize = false;
-                mCheckedPadding = false;
+                if (sourceDensity != targetDensity) {
+                    mCheckedConstantSize = false;
+                    mCheckedPadding = false;
+                }
             }
         }
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index da7b7fb..8831baf 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
-HWUI_NEW_OPS := true
+HWUI_NEW_OPS := false
 
 # Enables fine-grained GLES error checking
 # If set to true, every GLES call is wrapped & error checked
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 35c8f6b..5f689b4 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -347,7 +347,7 @@
         if (valid) {
             dirty = android::Rect(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
         } else {
-            dirty = android::Rect(0, 0,
+            dirty = android::Rect(
                     mRenderTarget.offscreenBuffer->viewportWidth,
                     mRenderTarget.offscreenBuffer->viewportHeight);
         }
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index a542c26..682bd04 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -21,6 +21,15 @@
 namespace android {
 namespace uirenderer {
 
+static int computeClipSideFlags(const Rect& clip, const Rect& bounds) {
+    int clipSideFlags = 0;
+    if (clip.left > bounds.left) clipSideFlags |= OpClipSideFlags::Left;
+    if (clip.top > bounds.top) clipSideFlags |= OpClipSideFlags::Top;
+    if (clip.right < bounds.right) clipSideFlags |= OpClipSideFlags::Right;
+    if (clip.bottom < bounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+    return clipSideFlags;
+}
+
 ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
         const RecordedOp& recordedOp, bool expandForStroke) {
     // resolvedMatrix = parentMatrix * localMatrix
@@ -55,10 +64,7 @@
         clippedBounds.setEmpty();
     } else {
         // Not rejected! compute true clippedBounds and clipSideFlags
-        if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
-        if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
-        if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
-        if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+        clipSideFlags = computeClipSideFlags(clipRect, clippedBounds);
         clippedBounds.doIntersect(clipRect);
     }
 }
@@ -69,11 +75,13 @@
         , clippedBounds(clipState->rect)
         , clipSideFlags(OpClipSideFlags::Full) {}
 
-ResolvedRenderState::ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect)
+ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect)
         : transform(Matrix4::identity())
-        , clipState(viewportRect)
+        , clipState(clipRect)
         , clippedBounds(dstRect)
-        , clipSideFlags(OpClipSideFlags::None) {}
+        , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) {
+    clippedBounds.doIntersect(clipRect->rect);
+}
 
 } // namespace uirenderer
 } // namespace android
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 5a5845a..4365ef8 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -175,8 +175,8 @@
             , projectionPathMask(snapshot.projectionPathMask)
             , op(shadowOpPtr) {}
 
-    BakedOpState(const ClipRect* viewportRect, const Rect& dstRect, const RecordedOp& recordedOp)
-            : computedState(viewportRect, dstRect)
+    BakedOpState(const ClipRect* clipRect, const Rect& dstRect, const RecordedOp& recordedOp)
+            : computedState(clipRect, dstRect)
             , alpha(1.0f)
             , roundRectClipState(nullptr)
             , projectionPathMask(nullptr)
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 00560d7..a14bdc4 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -415,12 +415,7 @@
 
 void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
     mDisplayList->ref(tree);
-    const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty();
-    SkPaint* paint = tree->getPaint();
-    const SkRect bounds = tree->getBounds();
-    addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
-            0, 0, bitmap.width(), bitmap.height(),
-            bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint)));
+    addDrawOp(new (alloc()) DrawVectorDrawableOp(tree));
 }
 
 void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 98315d0..516e619 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -28,6 +28,7 @@
 #include "UvMapper.h"
 #include "utils/LinearAllocator.h"
 #include "utils/PaintUtils.h"
+#include "VectorDrawable.h"
 
 #include <algorithm>
 
@@ -1107,6 +1108,30 @@
     float* mRadius;
 };
 
+class DrawVectorDrawableOp : public DrawOp {
+public:
+    DrawVectorDrawableOp(VectorDrawableRoot* tree)
+            : DrawOp(nullptr), mTree(tree) {}
+
+    virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
+        const SkBitmap& bitmap = mTree->getBitmapUpdateIfDirty();
+        SkPaint* paint = mTree->getPaint();
+        const SkRect bounds = mTree->getBounds();
+        renderer.drawBitmap(&bitmap, Rect(0, 0, bitmap.width(), bitmap.height()),
+                bounds, paint);
+    }
+
+    virtual void output(int level, uint32_t logFlags) const override {
+        OP_LOG("Draw Vector Drawable %p", mTree);
+    }
+
+    virtual const char* name() override { return "DrawVectorDrawable"; }
+
+private:
+    VectorDrawableRoot* mTree;
+
+};
+
 class DrawOvalOp : public DrawStrokableOp {
 public:
     DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 4f51036..04de98a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -782,39 +782,46 @@
     boundsTransform.mapRect(dstRect);
     dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
 
-    // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
-    OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
+    if (dstRect.isEmpty()) {
+        // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored
+        currentLayer().activeUnclippedSaveLayers.push_back(nullptr);
+    } else {
+        // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
+        OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
 
-    /**
-     * First, defer an operation to copy out the content from the rendertarget into a layer.
-     */
-    auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
-    BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
-            &(currentLayer().viewportClip), dstRect, *copyToOp);
-    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
+        /**
+         * First, defer an operation to copy out the content from the rendertarget into a layer.
+         */
+        auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
+        BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
+                &(currentLayer().repaintClip), dstRect, *copyToOp);
+        currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
 
-    /**
-     * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
-     * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
-     */
-    currentLayer().deferLayerClear(dstRect);
+        /**
+         * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
+         * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
+         */
+        currentLayer().deferLayerClear(dstRect);
 
-    /**
-     * And stash an operation to copy that layer back under the rendertarget until
-     * a balanced EndUnclippedLayerOp is seen
-     */
-    auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
-    bakedState = BakedOpState::directConstruct(mAllocator,
-            &(currentLayer().viewportClip), dstRect, *copyFromOp);
-    currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
+        /**
+         * And stash an operation to copy that layer back under the rendertarget until
+         * a balanced EndUnclippedLayerOp is seen
+         */
+        auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
+        bakedState = BakedOpState::directConstruct(mAllocator,
+                &(currentLayer().repaintClip), dstRect, *copyFromOp);
+        currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
+    }
 }
 
 void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
     LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
 
     BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
-    currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
     currentLayer().activeUnclippedSaveLayers.pop_back();
+    if (copyFromLayerOp) {
+        currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
+    }
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index c5d7191..bc39621 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -199,10 +199,10 @@
         : width(width)
         , height(height)
         , repaintRect(repaintRect)
+        , repaintClip(repaintRect)
         , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
         , beginLayerOp(beginLayerOp)
-        , renderNode(renderNode)
-        , viewportClip(Rect(width, height)) {}
+        , renderNode(renderNode) {}
 
 // iterate back toward target to see if anything drawn since should overlap the new op
 // if no target, merging ops still iterate to find similar batch to insert after
@@ -260,7 +260,7 @@
                 Matrix4::identity(), nullptr, paint,
                 verts, vertCount);
         BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
-                &viewportClip, bounds, *op);
+                &repaintClip, bounds, *op);
         deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
     }
 }
diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h
index 99968e1..4a7ca2d 100644
--- a/libs/hwui/LayerBuilder.h
+++ b/libs/hwui/LayerBuilder.h
@@ -109,10 +109,10 @@
     const uint32_t width;
     const uint32_t height;
     const Rect repaintRect;
+    const ClipRect repaintClip;
     OffscreenBuffer* offscreenBuffer;
     const BeginLayerOp* beginLayerOp;
     const RenderNode* renderNode;
-    const ClipRect viewportClip;
 
     // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
     std::vector<BakedOpState*> activeUnclippedSaveLayers;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index f49dd3f..f86898f 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -651,6 +651,62 @@
             << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
 }
 
+TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
+    class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
+    public:
+        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+        }
+        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(1, mIndex++);
+            ASSERT_NE(nullptr, op.paint);
+            EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
+                    << "Expect dirty rect as clip";
+            ASSERT_NE(nullptr, state.computedState.clipState);
+            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
+            EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
+        }
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(2, mIndex++);
+        }
+        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(3, mIndex++);
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        // save smaller than clip, so we get unclipped behavior
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
+        canvas.drawRect(0, 0, 200, 200, SkPaint());
+        canvas.restore();
+    });
+
+    // draw with partial screen dirty, and assert we see that rect later
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+    SaveLayerUnclippedClearClipTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
+}
+
+TEST(FrameBuilder, saveLayerUnclipped_reject) {
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        // unclipped savelayer + rect both in area that won't intersect with dirty
+        canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
+        canvas.drawRect(100, 100, 200, 200, SkPaint());
+        canvas.restore();
+    });
+
+    // draw with partial screen dirty that doesn't intersect with savelayer
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightGeometry, nullptr);
+    FailRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+}
+
 /* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
  * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
  * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 5ba3f2c..3c8a78d 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -16,42 +16,16 @@
 
 package android.location;
 
-import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * A class containing a GPS clock timestamp.
  * It represents a measurement of the GPS receiver's clock.
  */
 public final class GnssClock implements Parcelable {
-
     // The following enumerations must be in sync with the values declared in gps.h
 
-    /** The type of the GPS Clock. */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CLOCK_TYPE_UNKNOWN, CLOCK_TYPE_LOCAL_HW_TIME, CLOCK_TYPE_GPS_TIME})
-    public @interface GnssClockType {}
-
-    /**
-     * The type of the time stored is not available or it is unknown.
-     */
-    public static final byte CLOCK_TYPE_UNKNOWN = 0;
-
-    /**
-     * The source of the time value reported by this class is the 'Local Hardware Clock'.
-     */
-    public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1;
-
-    /**
-     * The source of the time value reported by this class is the 'GPS time' derived from
-     * satellites (epoch = Jan 6, 1980).
-     */
-    public static final byte CLOCK_TYPE_GPS_TIME = 2;
-
     private static final short HAS_NO_FLAGS = 0;
     private static final short HAS_LEAP_SECOND = (1<<0);
     private static final short HAS_TIME_UNCERTAINTY = (1<<1);
@@ -65,7 +39,6 @@
 
     private short mFlags;
     private short mLeapSecond;
-    private byte mType;
     private long mTimeInNs;
     private double mTimeUncertaintyInNs;
     private long mFullBiasInNs;
@@ -85,7 +58,6 @@
     public void set(GnssClock clock) {
         mFlags = clock.mFlags;
         mLeapSecond = clock.mLeapSecond;
-        mType = clock.mType;
         mTimeInNs = clock.mTimeInNs;
         mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
         mFullBiasInNs = clock.mFullBiasInNs;
@@ -104,38 +76,6 @@
     }
 
     /**
-     * Gets the type of time reported by {@link #getTimeInNs()}.
-     */
-    @GnssClockType
-    public byte getType() {
-        return mType;
-    }
-
-    /**
-     * Sets the type of time reported.
-     */
-    public void setType(@GnssClockType byte value) {
-        mType = value;
-    }
-
-    /**
-     * Gets a string representation of the 'type'.
-     * For internal and logging use only.
-     */
-    private String getTypeString() {
-        switch (mType) {
-            case CLOCK_TYPE_UNKNOWN:
-                return "Unknown";
-            case CLOCK_TYPE_GPS_TIME:
-                return "GpsTime";
-            case CLOCK_TYPE_LOCAL_HW_TIME:
-                return "LocalHwClock";
-            default:
-                return "<Invalid:" + mType + ">";
-        }
-    }
-
-    /**
      * Returns true if {@link #getLeapSecond()} is available, false otherwise.
      */
     public boolean hasLeapSecond() {
@@ -170,10 +110,7 @@
     }
 
     /**
-     * Gets the GPS receiver internal clock value in nanoseconds.
-     * This can be either the 'local hardware clock' value ({@link #CLOCK_TYPE_LOCAL_HW_TIME}), or the
-     * current GPS time derived inside GPS receiver ({@link #CLOCK_TYPE_GPS_TIME}).
-     * {@link #getType()} defines the time reported.
+     * Gets the GNSS receiver internal clock value in nanoseconds.
      *
      * For 'local hardware clock' this value is expected to be monotonically increasing during the
      * reporting session. The real GPS time can be derived by compensating
@@ -241,15 +178,14 @@
      * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
      * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
      *
-     * This value is available if {@link #CLOCK_TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
-     * the clock for GPS time.
-     * {@link #getBiasUncertaintyInNs()} should be used for quality check.
+     * This value is available if the receiver has estimated GPS time. If the computed time is for a
+     * non-GPS constellation, the time offset of that constellation to GPS has to be applied to fill
+     * this value. The value contains the 'bias uncertainty' {@link #getBiasUncertaintyInNs()} in
+     * it, and it should be used for quality check. The value is only available if
+     * {@link #hasFullBiasInNs()} is true.
      *
      * The sign of the value is defined by the following equation:
-     *      true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
-     *
-     * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
-     * The value is onl available if {@link #hasFullBiasInNs()} is true.
+     *      local estimate of GPS time = time_ns + (full_bias_ns + bias_ns)
      */
     public long getFullBiasInNs() {
         return mFullBiasInNs;
@@ -423,7 +359,6 @@
 
             gpsClock.mFlags = (short) parcel.readInt();
             gpsClock.mLeapSecond = (short) parcel.readInt();
-            gpsClock.mType = parcel.readByte();
             gpsClock.mTimeInNs = parcel.readLong();
             gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
             gpsClock.mFullBiasInNs = parcel.readLong();
@@ -446,7 +381,6 @@
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mFlags);
         parcel.writeInt(mLeapSecond);
-        parcel.writeByte(mType);
         parcel.writeLong(mTimeInNs);
         parcel.writeDouble(mTimeUncertaintyInNs);
         parcel.writeLong(mFullBiasInNs);
@@ -468,8 +402,6 @@
         final String formatWithUncertainty = "   %-15s = %-25s   %-26s = %s\n";
         StringBuilder builder = new StringBuilder("GnssClock:\n");
 
-        builder.append(String.format(format, "Type", getTypeString()));
-
         builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
 
         builder.append(String.format(
@@ -498,17 +430,12 @@
                 "DriftUncertaintyInNsPerSec",
                 hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
 
-        builder.append(String.format(format, "HardwareClockDiscontinuityCount",
-                getType() == CLOCK_TYPE_LOCAL_HW_TIME
-                        ? mHardwareClockDiscontinuityCount : null));
-
         return builder.toString();
     }
 
     private void initialize() {
         mFlags = HAS_NO_FLAGS;
         resetLeapSecond();
-        setType(CLOCK_TYPE_UNKNOWN);
         setTimeInNs(Long.MIN_VALUE);
         resetTimeUncertaintyInNs();
         resetFullBiasInNs();
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index a619ab2..abdc13c 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -272,8 +272,9 @@
 
     /**
      * Gets the time offset at which the measurement was taken in nanoseconds.
-     * The reference receiver's time is specified by {@link GnssClock#getTimeInNs()} and should be
-     * interpreted in the same way as indicated by {@link GnssClock#getType()}.
+     *
+     * The reference receiver's time from which this is offset is specified by
+     * {@link GnssClock#getTimeInNs()}.
      *
      * The sign of this value is given by the following equation:
      *      measurement time = time_ns + time_offset_ns
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 22f4f04..a0dfaa0f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -335,6 +335,24 @@
             CHANNEL_OUT_LOW_FREQUENCY);
     // CHANNEL_OUT_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_OUT_ALL
 
+    /** Minimum value for sample rate,
+     *  assuming AudioTrack and AudioRecord share the same limitations.
+     * @hide
+     */
+    // never unhide
+    public static final int SAMPLE_RATE_HZ_MIN = 4000;
+    /** Maximum value for sample rate,
+     *  assuming AudioTrack and AudioRecord share the same limitations.
+     * @hide
+     */
+    // never unhide
+    public static final int SAMPLE_RATE_HZ_MAX = 192000;
+    /** Sample rate will be a route-dependent value.
+     * For AudioTrack, it is usually the sink sample rate,
+     * and for AudioRecord it is usually the source sample rate.
+     */
+    public static final int SAMPLE_RATE_UNSPECIFIED = 0;
+
     /**
      * @hide
      * Return the input channel mask corresponding to an output channel mask.
@@ -561,7 +579,7 @@
     }
 
     /**
-     * Constructor used by the JNI
+     * Constructor used by the JNI.  Parameters are not checked for validity.
      */
     // Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
     // constructor
@@ -610,12 +628,9 @@
     /**
      * Return the sample rate.
      * @return one of the values that can be set in {@link Builder#setSampleRate(int)} or
-     * 0 if not set.
+     * {@link #SAMPLE_RATE_UNSPECIFIED} if not set.
      */
     public int getSampleRate() {
-        if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) == 0) {
-            return 0;
-        }
         return mSampleRate;
     }
 
@@ -687,7 +702,7 @@
      */
     public static class Builder {
         private int mEncoding = ENCODING_INVALID;
-        private int mSampleRate = 0;
+        private int mSampleRate = SAMPLE_RATE_UNSPECIFIED;
         private int mChannelMask = CHANNEL_INVALID;
         private int mChannelIndexMask = 0;
         private int mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_NONE;
@@ -718,6 +733,8 @@
         public AudioFormat build() {
             AudioFormat af = new AudioFormat(1980/*ignored*/);
             af.mEncoding = mEncoding;
+            // not calling setSampleRate is equivalent to calling
+            // setSampleRate(SAMPLE_RATE_UNSPECIFIED)
             af.mSampleRate = mSampleRate;
             af.mChannelMask = mChannelMask;
             af.mChannelIndexMask = mChannelIndexMask;
@@ -795,7 +812,7 @@
          *    are specified but do not have the same channel count.
          */
         public @NonNull Builder setChannelMask(int channelMask) {
-            if (channelMask == 0) {
+            if (channelMask == CHANNEL_INVALID) {
                 throw new IllegalArgumentException("Invalid zero channel mask");
             } else if (/* channelMask != 0 && */ mChannelIndexMask != 0 &&
                     Integer.bitCount(channelMask) != Integer.bitCount(mChannelIndexMask)) {
@@ -867,7 +884,11 @@
          * @throws java.lang.IllegalArgumentException
          */
         public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
-            if ((sampleRate <= 0) || (sampleRate > 192000)) {
+            // TODO Consider whether to keep the MIN and MAX range checks here.
+            // It is not necessary and poses the problem of defining the limits independently from
+            // native implementation or platform capabilities.
+            if (((sampleRate < SAMPLE_RATE_HZ_MIN) || (sampleRate > SAMPLE_RATE_HZ_MAX)) &&
+                    sampleRate != SAMPLE_RATE_UNSPECIFIED) {
                 throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
             }
             mSampleRate = sampleRate;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index dc534be..38e3b15 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3321,6 +3321,7 @@
      * Used as a key for {@link #getProperty} to request the native or optimal output sample rate
      * for this device's primary output stream, in decimal Hz.
      */
+    // FIXME Deprecate
     public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
             "android.media.property.OUTPUT_SAMPLE_RATE";
 
@@ -3328,6 +3329,7 @@
      * Used as a key for {@link #getProperty} to request the native or optimal output buffer size
      * for this device's primary output stream, in decimal PCM frames.
      */
+    // FIXME Deprecate
     public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
             "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index e342385..b8f0717 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -57,10 +57,6 @@
     // Constants
     //--------------------
 
-    /** Minimum value for sample rate */
-    private static final int SAMPLE_RATE_HZ_MIN = 4000;
-    /** Maximum value for sample rate */
-    private static final int SAMPLE_RATE_HZ_MAX = 192000;
 
     /**
      *  indicates AudioRecord state is not successfully initialized.
@@ -168,8 +164,9 @@
     //--------------------
     /**
      * The audio data sampling rate in Hz.
+     * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
      */
-    private int mSampleRate;
+    private int mSampleRate; // initialized by all constructors via audioParamCheck()
     /**
      * The number of input audio channels (1 is mono, 2 is stereo)
      */
@@ -251,6 +248,9 @@
      * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
      *   rate that is guaranteed to work on all devices, but other rates such as 22050,
      *   16000, and 11025 may work on some devices.
+     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+     *   which is usually the sample rate of the source.
+     *   {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
      * @param channelConfig describes the configuration of the audio channels.
      *   See {@link AudioFormat#CHANNEL_IN_MONO} and
      *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
@@ -337,16 +337,9 @@
             mAudioAttributes = attributes;
         }
 
-        int rate = 0;
-        if ((format.getPropertySetMask()
-                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
-        {
-            rate = format.getSampleRate();
-        } else {
-            rate = AudioSystem.getPrimaryOutputSamplingRate();
-            if (rate <= 0) {
-                rate = 44100;
-            }
+        int rate = format.getSampleRate();
+        if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
+            rate = 0;
         }
 
         int encoding = AudioFormat.ENCODING_DEFAULT;
@@ -373,19 +366,21 @@
 
         audioBuffSizeCheck(bufferSizeInBytes);
 
+        int[] sampleRate = new int[] {mSampleRate};
         int[] session = new int[1];
         session[0] = sessionId;
         //TODO: update native initialization when information about hardware init failure
         //      due to capture device already open is available.
         int initResult = native_setup( new WeakReference<AudioRecord>(this),
-                mAudioAttributes, mSampleRate, mChannelMask, mChannelIndexMask,
+                mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
                 mAudioFormat, mNativeBufferSizeInBytes,
-                session, ActivityThread.currentOpPackageName());
+                session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing native AudioRecord object.");
             return; // with mState == STATE_UNINITIALIZED
         }
 
+        mSampleRate = sampleRate[0];
         mSessionId = session[0];
 
         mState = STATE_INITIALIZED;
@@ -395,12 +390,32 @@
      * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
      * the AudioRecordRoutingProxy subclass.
      * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
-     * (associated with an OpenSL ES recorder).
+     * (associated with an OpenSL ES recorder). Note: the caller must ensure a correct
+     * value here as no error checking is or can be done.
      */
     /*package*/ AudioRecord(long nativeRecordInJavaObj) {
-        mNativeRecorderInJavaObj = nativeRecordInJavaObj;
+        int[] session = { 0 };
+        int[] rates = { 0 };
+        //TODO: update native initialization when information about hardware init failure
+        //      due to capture device already open is available.
+        // Note that for this native_setup, we are providing an already created/initialized
+        // *Native* AudioRecord, so the attributes parameters to native_setup() are ignored.
+        int initResult = native_setup(new WeakReference<AudioRecord>(this),
+                null /*mAudioAttributes*/,
+                rates /*mSampleRates*/,
+                0 /*mChannelMask*/,
+                0 /*mChannelIndexMask*/,
+                0 /*mAudioFormat*/,
+                0 /*mNativeBufferSizeInBytes*/,
+                session,
+                ActivityThread.currentOpPackageName(),
+                nativeRecordInJavaObj);
+        if (initResult != SUCCESS) {
+            loge("Error code "+initResult+" when initializing native AudioRecord object.");
+            return; // with mState == STATE_UNINITIALIZED
+        }
 
-        // other initialization here...
+        mSessionId = session[0];
 
         mState = STATE_INITIALIZED;
     }
@@ -623,6 +638,7 @@
 
         return mask;
     }
+
     // postconditions:
     //    mRecordSource is valid
     //    mAudioFormat is valid
@@ -642,7 +658,9 @@
 
         //--------------
         // sample rate
-        if ((sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX)) {
+        if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
+                sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
+                sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
             throw new IllegalArgumentException(sampleRateInHz
                     + "Hz is not a supported sample rate.");
         }
@@ -714,7 +732,11 @@
     // Getters
     //--------------------
     /**
-     * Returns the configured audio data sample rate in Hz
+     * Returns the configured audio sink sample rate in Hz.
+     * The sink sample rate never changes after construction.
+     * If the constructor had a specific sample rate, then the sink sample rate is that value.
+     * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
+     * then the sink sample rate is a route-dependent default value based on the source [sic].
      */
     public int getSampleRate() {
         return mSampleRate;
@@ -861,6 +883,7 @@
      * See {@link #AudioRecord(int, int, int, int, int)} for more information on valid
      * configuration values.
      * @param sampleRateInHz the sample rate expressed in Hertz.
+     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
      * @param channelConfig describes the configuration of the audio channels.
      *   See {@link AudioFormat#CHANNEL_IN_MONO} and
      *   {@link AudioFormat#CHANNEL_IN_STEREO}
@@ -1708,8 +1731,9 @@
 
     private native final int native_setup(Object audiorecord_this,
             Object /*AudioAttributes*/ attributes,
-            int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
-            int buffSizeInBytes, int[] sessionId, String opPackageName);
+            int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+            int buffSizeInBytes, int[] sessionId, String opPackageName,
+            long nativeRecordInJavaObj);
 
     // TODO remove: implementation calls directly into implementation of native_release()
     private native final void native_finalize();
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
index 2161cf3..41f92d4 100644
--- a/media/java/android/media/AudioRouting.java
+++ b/media/java/android/media/AudioRouting.java
@@ -41,6 +41,14 @@
     public AudioDeviceInfo getPreferredDevice();
 
     /**
+     * Returns an {@link AudioDeviceInfo} identifying the current routing of this
+     * AudioTrack/AudioRecord.
+     * Note: The query is only valid if the AudioTrack/AudioRecord is currently playing.
+     * If it is not, <code>getRoutedDevice()</code> will return null.
+     */
+    public AudioDeviceInfo getRoutedDevice();
+
+    /**
      * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
      * changes on this AudioTrack/AudioRecord.
      * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 6fc2f87..f02e8375 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -91,11 +91,6 @@
      */
     private static final float GAIN_MAX = 1.0f;
 
-    /** Minimum value for sample rate */
-    private static final int SAMPLE_RATE_HZ_MIN = 4000;
-    /** Maximum value for sample rate */
-    private static final int SAMPLE_RATE_HZ_MAX = 192000;
-
     /** Maximum value for AudioTrack channel count
      * @hide public for MediaCode only, do not un-hide or change to a numeric literal
      */
@@ -254,6 +249,7 @@
     private final Looper mInitializationLooper;
     /**
      * The audio data source sampling rate in Hz.
+     * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
      */
     private int mSampleRate; // initialized by all constructors via audioParamCheck()
     /**
@@ -340,6 +336,9 @@
      *   {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
      *   {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
      * @param sampleRateInHz the initial source sample rate expressed in Hz.
+     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+     *   which is usually the sample rate of the sink.
+     *   {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
      * @param channelConfig describes the configuration of the audio channels.
      *   See {@link AudioFormat#CHANNEL_OUT_MONO} and
      *   {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -389,6 +388,8 @@
      *   {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
      *   {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
      * @param sampleRateInHz the initial source sample rate expressed in Hz.
+     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
+     *   which is usually the sample rate of the sink.
      * @param channelConfig describes the configuration of the audio channels.
      *   See {@link AudioFormat#CHANNEL_OUT_MONO} and
      *   {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -461,16 +462,11 @@
             looper = Looper.getMainLooper();
         }
 
-        int rate = 0;
-        if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
-        {
-            rate = format.getSampleRate();
-        } else {
-            rate = AudioSystem.getPrimaryOutputSamplingRate();
-            if (rate <= 0) {
-                rate = 44100;
-            }
+        int rate = format.getSampleRate();
+        if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
+            rate = 0;
         }
+
         int channelIndexMask = 0;
         if ((format.getPropertySetMask()
                 & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
@@ -503,17 +499,19 @@
             throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
         }
 
+        int[] sampleRate = new int[] {mSampleRate};
         int[] session = new int[1];
         session[0] = sessionId;
         // native initialization
         int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
-                mSampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
-                mNativeBufferSizeInBytes, mDataLoadMode, session);
+                sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
+                mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing AudioTrack.");
             return; // with mState == STATE_UNINITIALIZED
         }
 
+        mSampleRate = sampleRate[0];
         mSessionId = session[0];
 
         if (mDataLoadMode == MODE_STATIC) {
@@ -530,8 +528,6 @@
      * (associated with an OpenSL ES player).
      */
     /*package*/ AudioTrack(long nativeTrackInJavaObj) {
-        mNativeTrackInJavaObj = nativeTrackInJavaObj;
-
         // "final"s
         mAttributes = null;
         mAppOps = null;
@@ -544,6 +540,26 @@
         mInitializationLooper = looper;
 
         // other initialization...
+        // Note that for this native_setup, we are providing an already created/initialized
+        // *Native* AudioTrack, so the attributes parameters to native_setup() are ignored.
+        int[] session = { 0 };
+        int[] rates = { 0 };
+        int initResult = native_setup(new WeakReference<AudioTrack>(this),
+                null /*mAttributes - NA*/,
+                rates /*sampleRate - NA*/,
+                0 /*mChannelMask - NA*/,
+                0 /*mChannelIndexMask - NA*/,
+                0 /*mAudioFormat - NA*/,
+                0 /*mNativeBufferSizeInBytes - NA*/,
+                0 /*mDataLoadMode - NA*/,
+                session,
+                nativeTrackInJavaObj);
+        if (initResult != SUCCESS) {
+            loge("Error code "+initResult+" when initializing AudioTrack.");
+            return; // with mState == STATE_UNINITIALIZED
+        }
+
+        mSessionId = session[0];
 
         mState = STATE_INITIALIZED;
     }
@@ -712,7 +728,7 @@
             if (mFormat == null) {
                 mFormat = new AudioFormat.Builder()
                         .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
-                        .setSampleRate(AudioSystem.getPrimaryOutputSamplingRate())
+                        //.setSampleRate(AudioFormat.SAMPLE_RATE_UNSPECIFIED)
                         .setEncoding(AudioFormat.ENCODING_DEFAULT)
                         .build();
             }
@@ -762,7 +778,9 @@
                                  int audioFormat, int mode) {
         //--------------
         // sample rate, note these values are subject to change
-        if (sampleRateInHz < SAMPLE_RATE_HZ_MIN || sampleRateInHz > SAMPLE_RATE_HZ_MAX) {
+        if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
+                sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
+                sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
             throw new IllegalArgumentException(sampleRateInHz
                     + "Hz is not a supported sample rate.");
         }
@@ -948,7 +966,13 @@
     }
 
     /**
-     * Returns the configured audio data sample rate in Hz
+     * Returns the configured audio source sample rate in Hz.
+     * The initial source sample rate depends on the constructor parameters,
+     * but the source sample rate may change if {@link #setPlaybackRate(int)} is called.
+     * If the constructor had a specific sample rate, then the initial sink sample rate is that
+     * value.
+     * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
+     * then the initial sink sample rate is a route-dependent default value based on the source [sic].
      */
     public int getSampleRate() {
         return mSampleRate;
@@ -1218,6 +1242,7 @@
      * to a higher value than the initial source sample rate, be sure to configure the buffer size
      * based on the highest planned sample rate.
      * @param sampleRateInHz the source sample rate expressed in Hz.
+     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
      * @param channelConfig describes the configuration of the audio channels.
      *   See {@link AudioFormat#CHANNEL_OUT_MONO} and
      *   {@link AudioFormat#CHANNEL_OUT_STEREO}
@@ -1255,7 +1280,9 @@
         }
 
         // sample rate, note these values are subject to change
-        if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) {
+        // Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
+        if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
+                (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
             loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
             return ERROR_BAD_VALUE;
         }
@@ -2763,8 +2790,8 @@
     //     AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
     private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
             Object /*AudioAttributes*/ attributes,
-            int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
-            int buffSizeInBytes, int mode, int[] sessionId);
+            int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
+            int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack);
 
     private native final void native_finalize();
 
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 4ffac6d..55fb82b 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -277,6 +277,7 @@
                 mRouteFlags = ROUTE_FLAG_RENDER;
             }
             if (mFormat == null) {
+                // FIXME Can we eliminate this?  Will AudioMix work with an unspecified sample rate?
                 int rate = AudioSystem.getPrimaryOutputSamplingRate();
                 if (rate <= 0) {
                     rate = 44100;
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 13db00e..622900f 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -592,10 +592,11 @@
         }
 
         /**
-         * Request that the player prepare its playback. Once the preparation is done, the session
-         * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
-         * {@link #play} can be called to start playback. If the preparation is not needed,
-         * {@link #play} can be directly called without this method.
+         * Request that the player prepare its playback. In other words, other sessions can continue
+         * to play during the preparation of this session. This method can be used to speed up the
+         * start of the playback. Once the preparation is done, the session will change its playback
+         * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to
+         * start playback.
          */
         public void prepare() {
             try {
@@ -606,10 +607,12 @@
         }
 
         /**
-         * Request that the player prepare playback for a specific media id. Once the preparation is
-         * done, the session will change its playback state to {@link PlaybackState#STATE_PAUSED}.
-         * Afterwards, {@link #play} can be called to start playback. If the preparation is not
-         * needed, {@link #playFromMediaId} can be directly called without this method.
+         * Request that the player prepare playback for a specific media id. In other words, other
+         * sessions can continue to play during the preparation of this session. This method can be
+         * used to speed up the start of the playback. Once the preparation is done, the session
+         * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+         * {@link #play} can be called to start playback. If the preparation is not needed,
+         * {@link #playFromMediaId} can be directly called without this method.
          *
          * @param mediaId The id of the requested media.
          * @param extras Optional extras that can include extra information about the media item
@@ -628,12 +631,13 @@
         }
 
         /**
-         * Request that the player prepare playback for a specific search query.
-         * An empty or null query should be treated as a request to prepare any
-         * music. Once the preparation is done, the session will change its playback state to
-         * {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to start
-         * playback. If the preparation is not needed, {@link #playFromSearch} can be directly
-         * called without this method.
+         * Request that the player prepare playback for a specific search query. An empty or null
+         * query should be treated as a request to prepare any music. In other words, other sessions
+         * can continue to play during the preparation of this session. This method can be used to
+         * speed up the start of the playback. Once the preparation is done, the session will
+         * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+         * {@link #play} can be called to start playback. If the preparation is not needed,
+         * {@link #playFromSearch} can be directly called without this method.
          *
          * @param query The search query.
          * @param extras Optional extras that can include extra information
@@ -653,11 +657,12 @@
         }
 
         /**
-         * Request that the player prepare playback for a specific {@link Uri}.
-         * Once the preparation is done, the session will change its playback state to
-         * {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to start
-         * playback. If the preparation is not needed, {@link #playFromUri} can be directly
-         * called without this method.
+         * Request that the player prepare playback for a specific {@link Uri}. In other words,
+         * other sessions can continue to play during the preparation of this session. This method
+         * can be used to speed up the start of the playback. Once the preparation is done, the
+         * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+         * {@link #play} can be called to start playback. If the preparation is not needed,
+         * {@link #playFromUri} can be directly called without this method.
          *
          * @param uri The URI of the requested media.
          * @param extras Optional extras that can include extra information about the media item
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 9073077..0bd1713 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -830,40 +830,45 @@
         }
 
         /**
-         * Override to handle requests to prepare playback. The state of playback should be updated
-         * to {@link PlaybackState#STATE_PAUSED} after the preparation is done. Override
-         * {@link #onPlay} to handle requests for starting playback of prepared content.
+         * Override to handle requests to prepare playback. During the preparation, a session should
+         * not hold audio focus in order to allow other sessions play seamlessly. The state of
+         * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is
+         * done.
          */
         public void onPrepare() {
         }
 
         /**
          * Override to handle requests to prepare for playing a specific mediaId that was provided
-         * by your app's {@link MediaBrowserService}. The state of playback should be updated
-         * to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback of
-         * the prepared content should start in the implementation of {@link #onPlay}. Override
-         * {@link #onPlayFromMediaId} to handle requests for starting playback without preparation.
+         * by your app's {@link MediaBrowserService}. During the preparation, a session should not
+         * hold audio focus in order to allow other sessions play seamlessly. The state of playback
+         * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * The playback of the prepared content should start in the implementation of
+         * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting
+         * playback without preparation.
          */
         public void onPrepareFromMediaId(String mediaId, Bundle extras) {
         }
 
         /**
-         * Override to handle requests to prepare playback from a search query. An
-         * empty query indicates that the app may prepare any music. The
-         * implementation should attempt to make a smart choice about what to
-         * play. The state of playback should be updated to {@link PlaybackState#STATE_PAUSED}
-         * after the preparation is done. The playback of the prepared content should start
-         * in the implementation of {@link #onPlay}. Override {@link #onPlayFromSearch}
-         * to handle requests for starting playback without preparation.
+         * Override to handle requests to prepare playback from a search query. An empty query
+         * indicates that the app may prepare any music. The implementation should attempt to make a
+         * smart choice about what to play. During the preparation, a session should not hold audio
+         * focus in order to allow other sessions play seamlessly. The state of playback should be
+         * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback
+         * of the prepared content should start in the implementation of {@link #onPlay}. Override
+         * {@link #onPlayFromSearch} to handle requests for starting playback without preparation.
          */
         public void onPrepareFromSearch(String query, Bundle extras) {
         }
 
         /**
          * Override to handle requests to prepare a specific media item represented by a URI.
-         * The state of playback should be updated to {@link PlaybackState#STATE_PAUSED}
-         * after the preparation is done. The playback of the prepared content should start in
-         * the implementation of {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
+         * During the preparation, a session should not hold audio focus in order to allow
+         * other sessions play seamlessly. The state of playback should be updated to
+         * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * The playback of the prepared content should start in the implementation of
+         * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
          * for starting playback without preparation.
          */
         public void onPrepareFromUri(Uri uri, Bundle extras) {
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index b8a1374..ec65ffe 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -105,7 +105,8 @@
     public static final int TYPE_DISPLAY_PORT = 1008;
 
     /**
-     * The ID of the TV input to provide to the setup activity and settings activity.
+     * Used as a String extra field in setup intents created by {@link #createSetupIntent()} to
+     * supply the ID of a specific TV input to set up.
      */
     public static final String EXTRA_INPUT_ID = "android.media.tv.extra.INPUT_ID";
 
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 7089c00..2703b1a 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -266,6 +266,14 @@
     public static final String META_DATA_CONTENT_RATING_SYSTEMS =
             "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
 
+    /**
+     * Activity action to set up channel sources i.e.&nbsp;TV inputs of type
+     * {@link TvInputInfo#TYPE_TUNER}. When invoked, the system will display an appropriate UI for
+     * the user to initiate the individual setup flow provided by
+     * {@link android.R.attr#setupActivity} of each TV input service.
+     */
+    public static final String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
+
     private final ITvInputManager mService;
 
     private final Object mLock = new Object();
diff --git a/opengl/java/android/opengl/GLES30.java b/opengl/java/android/opengl/GLES30.java
index 342ffa4..9c3b505 100644
--- a/opengl/java/android/opengl/GLES30.java
+++ b/opengl/java/android/opengl/GLES30.java
@@ -1791,4 +1791,16 @@
         java.nio.IntBuffer params
     );
 
+    // C function void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint offset )
+
+    public static native void glReadPixels(
+        int x,
+        int y,
+        int width,
+        int height,
+        int format,
+        int type,
+        int offset
+    );
+
 }
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 1a4e3eb..e1650e1 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
 # The design lib requires that the client package use appcompat themes.
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
 # Supplies material design components, e.g. Snackbar.
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-design
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 07498a0..8a76540 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -19,7 +19,7 @@
     <bool name="config_defaultAdvancedDevices">false</bool>
 
     <!-- Intentionally unset. Vendors should set this in an overlay. -->
-    <string name="trusted_quick_viewer_package"></string>
+    <string name="trusted_quick_viewer_package" translatable="false"></string>
     <bool name="list_divider_inset_left">true</bool>
     <bool name="always_show_summary">false</bool>
 </resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index b97918e..3dc111a 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -134,6 +134,8 @@
     <string name="copy_notification_title">Copying files</string>
     <!-- Title of the move notification [CHAR LIMIT=24] -->
     <string name="move_notification_title">Moving files</string>
+    <!-- Title of the move notification [CHAR LIMIT=24] -->
+    <string name="delete_notification_title">Deleting files</string>
     <!-- Text shown on the copy notification to indicate remaining time, in minutes [CHAR LIMIT=24] -->
     <string name="copy_remaining"><xliff:g id="duration" example="3 minutes">%s</xliff:g> left</string>
     <!-- Toast shown when a file copy is kicked off -->
@@ -206,4 +208,11 @@
     <string name="allow">Allow</string>
     <!-- Text in the button asking user to deny access to a given directory. -->
     <string name="deny">Deny</string>
+    <!-- Dialog title shown to users when asking if they want to delete files (a confirmation). -->
+    <string name="delete_confirmation_title">Delete files?</string>
+    <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). -->
+    <plurals name="delete_confirmation_message">
+        <item quantity="one">Are you sure you want to delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item>
+        <item quantity="other">Are you sure you want to delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item>
+    </plurals>
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index cb6cf8d..6211085 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -31,6 +31,7 @@
 import android.annotation.StringRes;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.AlertDialog;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -38,6 +39,7 @@
 import android.content.ClipData;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
@@ -52,6 +54,7 @@
 import android.provider.DocumentsContract.Document;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
+import android.support.v13.view.DragStartHelper;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
 import android.support.v7.widget.RecyclerView;
@@ -72,7 +75,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -149,9 +151,6 @@
 
     private static final String TAG = "DirectoryFragment";
     private static final int LOADER_ID = 42;
-    private static final int DELETE_UNDO_TIMEOUT = 5000;
-    private static final int DELETE_JOB_DELAY = 5500;
-    private static final int EMPTY_REVEAL_DURATION = 250;
 
     private Model mModel;
     private MultiSelectManager mSelectionManager;
@@ -258,7 +257,8 @@
         }
         mRecView.setLayoutManager(mLayout);
 
-        mGestureDetector = new ListeningGestureDetector(this.getContext(), new GestureListener());
+        mGestureDetector =
+                new ListeningGestureDetector(this.getContext(), mDragHelper, new GestureListener());
 
         mRecView.addOnItemTouchListener(mGestureDetector);
 
@@ -703,46 +703,28 @@
         final DocumentInfo srcParent = getDisplayState().stack.peek();
         new GetDocumentsTask() {
             @Override
-            void onDocumentsReady(List<DocumentInfo> docs) {
-                // Hide the files in the UI.
-                final SparseArray<String> hidden = mAdapter.hide(selected.getAll());
-
-                checkState(DELETE_JOB_DELAY > DELETE_UNDO_TIMEOUT);
-                String operationId = FileOperations.delete(
-                        getActivity(), docs, srcParent, getDisplayState().stack,
-                        DELETE_JOB_DELAY);
-                showDeleteSnackbar(hidden, operationId);
-            }
-        }.execute(selected);
-    }
-
-    private void showDeleteSnackbar(final SparseArray<String> hidden, final String jobId) {
-
-        Context context = getActivity();
-        String message = Shared.getQuantityString(context, R.plurals.deleting, hidden.size());
-
-        // Show a snackbar informing the user that files will be deleted, and give them an option to
-        // cancel.
-        final Activity activity = getActivity();
-        Snackbars.makeSnackbar(activity, message, DELETE_UNDO_TIMEOUT)
-                .setAction(
-                        R.string.undo,
-                        new View.OnClickListener() {
-                            @Override
-                            public void onClick(View view) {}
-                        })
-                .setCallback(
-                        new Snackbar.Callback() {
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) {
-                                    // If the delete was cancelled, just unhide the files.
-                                    FileOperations.cancel(activity, jobId);
-                                    mAdapter.unhide(hidden);
-                                }
+            void onDocumentsReady(final List<DocumentInfo> docs) {
+                new AlertDialog.Builder(getActivity())
+                    .setTitle(R.string.delete_confirmation_title)
+                    .setMessage(
+                            Shared.getQuantityString(
+                                    getActivity(),
+                                    R.plurals.delete_confirmation_message,
+                                    docs.size()))
+                    .setPositiveButton(
+                         android.R.string.yes,
+                         new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int id) {
+                                // Hide the files in the UI.
+                                mAdapter.hide(selected.getAll());
+                                FileOperations.delete(
+                                        getActivity(), docs, srcParent, getDisplayState().stack);
                             }
                         })
-                .show();
+                    .setNegativeButton(android.R.string.no, null)
+                    .show();
+            }
+        }.execute(selected);
     }
 
     private void transferDocuments(final Selection selected, final @OpType int mode) {
@@ -1051,7 +1033,7 @@
             view.setOnDragListener(mOnDragListener);
         }
 
-        view.setOnLongClickListener(mLongClickListener);
+        view.setOnLongClickListener(mDragHelper);
     }
 
     private View.OnDragListener mOnDragListener = new View.OnDragListener() {
@@ -1062,11 +1044,15 @@
                     // TODO: Check if the event contains droppable data.
                     return true;
 
-                // TODO: Highlight potential drop target directory?
                 // TODO: Expand drop target directory on hover?
                 case DragEvent.ACTION_DRAG_ENTERED:
-                case DragEvent.ACTION_DRAG_LOCATION:
+                    setDropTargetHighlight(v, true);
+                    return true;
                 case DragEvent.ACTION_DRAG_EXITED:
+                    setDropTargetHighlight(v, false);
+                    return true;
+
+                case DragEvent.ACTION_DRAG_LOCATION:
                 case DragEvent.ACTION_DRAG_ENDED:
                     return true;
 
@@ -1080,10 +1066,24 @@
                         // TODO: Do not drop into the directory where the documents came from.
                     }
                     copyFromClipData(event.getClipData(), dstDir);
+                    // Clean up the UI.
+                    setDropTargetHighlight(v, false);
+                    mSelectionManager.clearSelection();
                     return true;
             }
             return false;
         }
+
+        private void setDropTargetHighlight(View v, boolean highlight) {
+            // Note: use exact comparison - this code is searching for views which are children of
+            // the RecyclerView instance in the UI.
+            if (v.getParent() == mRecView) {
+                RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(v);
+                if (vh instanceof DocumentHolder) {
+                    ((DocumentHolder) vh).setHighlighted(highlight);
+                }
+            }
+        }
     };
 
     /**
@@ -1109,21 +1109,14 @@
      *     a document item view.
      */
     private String getModelId(View view) {
-        while (true) {
-            if (view.getLayoutParams() instanceof RecyclerView.LayoutParams) {
-                RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(view);
-                if (vh instanceof DocumentHolder) {
-                    return ((DocumentHolder) vh).modelId;
-                } else {
-                    return null;
-                }
+        View itemView = mRecView.findContainingItemView(view);
+        if (itemView != null) {
+            RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(itemView);
+            if (vh instanceof DocumentHolder) {
+                return ((DocumentHolder) vh).modelId;
             }
-            ViewParent parent = view.getParent();
-            if (parent == null || !(parent instanceof View)) {
-                return null;
-            }
-            view = (View) parent;
         }
+        return null;
     }
 
     private List<DocumentInfo> getDraggableDocuments(View currentItemView) {
@@ -1304,10 +1297,10 @@
         }
     }
 
-    private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
+    private DragStartHelper mDragHelper = new DragStartHelper(null) {
         @Override
-        public boolean onLongClick(View v) {
-            if (mGestureDetector.mouseSpawnedLastEvent()) {
+        protected boolean onDragStart(View v) {
+            if (isSelected(getModelId(v))) {
                 List<DocumentInfo> docs = getDraggableDocuments(v);
                 if (docs.isEmpty()) {
                     return false;
@@ -1333,9 +1326,12 @@
             implements OnItemTouchListener {
 
         private int mLastTool = -1;
+        private DragStartHelper mDragHelper;
 
-        public ListeningGestureDetector(Context context, GestureListener listener) {
+        public ListeningGestureDetector(
+                Context context, DragStartHelper dragHelper, GestureListener listener) {
             super(context, listener);
+            mDragHelper = dragHelper;
             setOnDoubleTapListener(listener);
         }
 
@@ -1350,12 +1346,27 @@
         @Override
         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
             mLastTool = e.getToolType(0);
-            onTouchEvent(e);  // bounce this forward to our detecty heart
+
+            // Detect drag events. When a drag is detected, intercept the rest of the gesture.
+            View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+            if (itemView != null && mDragHelper.handleTouch(itemView,  e)) {
+                return true;
+            }
+            // Forward unhandled events to the GestureDetector.
+            onTouchEvent(e);
+
             return false;
         }
 
         @Override
-        public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+            View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+            mDragHelper.handleTouch(itemView,  e);
+            // Note: even though this event is being handled as part of a drag gesture, continue
+            // forwarding to the GestureDetector. The detector needs to see the entire cluster of
+            // events in order to properly interpret gestures.
+            onTouchEvent(e);
+        }
 
         @Override
         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
index 1bfc6e9..2967a90 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -75,11 +75,25 @@
      */
     public abstract void bind(Cursor cursor, String modelId, State state);
 
+    /**
+     * Makes the associated item view appear selected. Note that this merely affects the appearance
+     * of the view, it doesn't actually select the item.
+     *
+     * @param selected
+     */
     public void setSelected(boolean selected) {
         itemView.setActivated(selected);
         itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
     }
 
+    /**
+     * Highlights the associated item view.
+     * @param highlighted
+     */
+    public void setHighlighted(boolean highlighted) {
+        itemView.setBackgroundColor(highlighted ? mSelectedItemColor : mDefaultItemColor);
+    }
+
     @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         // Event listener should always be set.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
index 844d07a..faedd5e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -67,12 +67,15 @@
 import java.util.List;
 
 class CopyJob extends Job {
+
     private static final String TAG = "CopyJob";
-    private static final int PROGRESS_INTERVAL_MILLIS = 1000;
+    private static final int PROGRESS_INTERVAL_MILLIS = 500;
+
     final List<DocumentInfo> mSrcs;
     final ArrayList<DocumentInfo> convertedFiles = new ArrayList<>();
 
     private long mStartTime = -1;
+
     private long mBatchSize;
     private long mBytesCopied;
     private long mLastNotificationTime;
@@ -496,8 +499,8 @@
             try {
                 while ((len = in.read(buffer)) != -1) {
                     if (isCanceled()) {
-                        throw new ResourceException("Canceled copy mid-copy of %s",
-                                src.derivedUri);
+                        if (DEBUG) Log.d(TAG, "Canceled copy mid-copy of: " + src.derivedUri);
+                        return;
                     }
                     out.write(buffer, 0, len);
                     makeCopyProgress(len);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
index 25bca4f..8f45162 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
@@ -57,8 +57,8 @@
     @Override
     Builder createProgressBuilder() {
         return super.createProgressBuilder(
-                service.getString(R.string.move_notification_title),
-                R.drawable.ic_menu_copy,
+                service.getString(R.string.delete_notification_title),
+                R.drawable.ic_menu_delete,
                 service.getString(android.R.string.cancel),
                 R.drawable.ic_cab_cancel);
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
index 9d017ee..b53e165 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
@@ -22,7 +22,6 @@
 import static com.android.documentsui.Shared.asArrayList;
 import static com.android.documentsui.Shared.getQuantityString;
 import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL;
-import static com.android.documentsui.services.FileOperationService.EXTRA_DELAY;
 import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID;
 import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
 import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
@@ -165,19 +164,16 @@
      *     Use {@link #createJobId} if you don't have one handy.
      * @param srcDocs A list of src files to copy.
      * @param srcParent Parent of all the source documents.
-     * @param delay Number of milliseconds to wait before executing the job.
      * @return Id of the job.
      */
     public static String delete(
             Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent,
-            DocumentStack location, int delay) {
+            DocumentStack location) {
         String jobId = createJobId();
-        if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId
-                + " delayed by " + delay + " milliseconds.");
+        if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId + ".");
 
         Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent,
                 location);
-        intent.putExtra(EXTRA_DELAY, delay);
         activity.startService(intent);
 
         return jobId;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
index afb3374..a158654 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
@@ -201,8 +201,8 @@
     }
 
     Notification getSetupNotification(String content) {
-        mProgressBuilder.setProgress(0, 0, true);
-        mProgressBuilder.setContentText(content);
+        mProgressBuilder.setProgress(0, 0, true)
+                .setContentText(content);
         return mProgressBuilder.build();
     }
 
@@ -221,6 +221,7 @@
                 .setCategory(Notification.CATEGORY_ERROR)
                 .setSmallIcon(icon)
                 .setAutoCancel(true);
+
         return errorBuilder.build();
     }
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 38e1257..498471e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -119,24 +119,23 @@
         device.waitForIdle();
         bots.main.menuDelete().click();
 
-        bots.directory.waitForDeleteSnackbar();
+        bots.main.findDialogOkButton().click();
+
         bots.directory.assertDocumentsAbsent("file1.png");
+    }
 
-        bots.directory.waitForDeleteSnackbarGone();
-        bots.directory.assertDocumentsAbsent("file1.png");
+    public void testDeleteDocument_Cancel() throws Exception {
+        initTestFiles();
 
-        // Now delete from another root.
-        bots.roots.openRoot(ROOT_1_ID);
+        bots.roots.openRoot(ROOT_0_ID);
 
-        bots.directory.clickDocument("poodles.text");
+        bots.directory.clickDocument("file1.png");
         device.waitForIdle();
         bots.main.menuDelete().click();
 
-        bots.directory.waitForDeleteSnackbar();
-        bots.directory.assertDocumentsAbsent("poodles.text");
+        bots.main.findDialogCancelButton().click();
 
-        bots.directory.waitForDeleteSnackbarGone();
-        bots.directory.assertDocumentsAbsent("poodles.text");
+        bots.directory.assertDocumentsPresent("file1.png");
     }
 
     // Tests that pressing tab switches focus between the roots and directory listings.
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
index 2833418..5f33b32 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
@@ -80,7 +80,7 @@
         bots.main.setDialogText(newName);
 
         device.waitForIdle(TIMEOUT);
-        bots.main.findRenameDialogOkButton().click();
+        bots.main.findDialogOkButton().click();
         device.waitForIdle(TIMEOUT);
 
         bots.directory.assertDocumentsAbsent(fileName1);
@@ -108,7 +108,7 @@
         bots.main.setDialogText(newName);
 
         device.waitForIdle(TIMEOUT);
-        bots.main.findRenameDialogCancelButton().click();
+        bots.main.findDialogCancelButton().click();
         device.waitForIdle(TIMEOUT);
 
         bots.directory.assertDocumentsPresent(fileName1);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
index fe2a3c3..c112867 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
@@ -168,12 +168,16 @@
         return findObject("android:id/content", "android:id/text1");
     }
 
-    public UiObject findRenameDialogOkButton() {
-        return findObject("android:id/content", "android:id/button1");
+    public UiObject findDialogOkButton() {
+        UiObject object = findObject("android:id/content", "android:id/button1");
+        object.waitForExists(mTimeout);
+        return object;
     }
 
-    public UiObject findRenameDialogCancelButton() {
-        return findObject("android:id/content", "android:id/button2");
+    public UiObject findDialogCancelButton() {
+        UiObject object = findObject("android:id/content", "android:id/button2");
+        object.waitForExists(mTimeout);
+        return object;
     }
 
     UiObject findMenuLabelWithName(String label) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 6a98405..38435f4 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -133,6 +133,8 @@
             return mCallback.readObjectBytes(inode, offset, size, mBuffer);
         } catch (IOException e) {
             return -OsConstants.EIO;
+        } catch (UnsupportedOperationException e) {
+            return -OsConstants.ENOTSUP;
         }
     }
 
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 9578e6b..4bed003 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -89,7 +89,8 @@
      * @return If roots are added or removed from the database.
      * @throws FileNotFoundException
      */
-    synchronized boolean putStorageDocuments(String parentDocumentId, MtpRoot[] roots)
+    synchronized boolean putStorageDocuments(
+            String parentDocumentId, int[] operationsSupported, MtpRoot[] roots)
             throws FileNotFoundException {
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
@@ -100,7 +101,11 @@
                 valuesList[i] = new ContentValues();
                 extraValuesList[i] = new ContentValues();
                 MtpDatabase.getStorageDocumentValues(
-                        valuesList[i], extraValuesList[i], parentDocumentId, roots[i]);
+                        valuesList[i],
+                        extraValuesList[i],
+                        parentDocumentId,
+                        operationsSupported,
+                        roots[i]);
             }
             final boolean changed = putDocuments(
                     parentDocumentId,
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index e14109a..701543b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -689,9 +689,7 @@
         values.putNull(Document.COLUMN_SIZE);
 
         extraValues.clear();
-        extraValues.put(
-                Root.COLUMN_FLAGS,
-                Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+        extraValues.put(Root.COLUMN_FLAGS, getRootFlags(device.operationsSupported));
         extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES);
         extraValues.putNull(Root.COLUMN_CAPACITY_BYTES);
         extraValues.put(Root.COLUMN_MIME_TYPES, "");
@@ -700,12 +698,16 @@
     /**
      * Gets {@link ContentValues} for the given root.
      * @param values {@link ContentValues} that receives values.
+     * @param extraValues {@link ContentValues} that receives extra values for roots.
+     * @param parentDocumentId Parent document ID.
+     * @param supportedOperations Array of Operation code supported by the device.
      * @param root Root to be converted {@link ContentValues}.
      */
     static void getStorageDocumentValues(
             ContentValues values,
             ContentValues extraValues,
             String parentDocumentId,
+            int[] operationsSupported,
             MtpRoot root) {
         values.clear();
         values.put(COLUMN_DEVICE_ID, root.mDeviceId);
@@ -722,9 +724,7 @@
         values.put(Document.COLUMN_FLAGS, 0);
         values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace);
 
-        extraValues.put(
-                Root.COLUMN_FLAGS,
-                Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
+        extraValues.put(Root.COLUMN_FLAGS, getRootFlags(operationsSupported));
         extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
         extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
         extraValues.put(Root.COLUMN_MIME_TYPES, "");
@@ -785,6 +785,14 @@
         return "application/octet-stream";
     }
 
+    private static int getRootFlags(int[] operationsSupported) {
+        int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD;
+        if (MtpDeviceRecord.isWritingSupported(operationsSupported)) {
+            rootFlag |= Root.FLAG_SUPPORTS_CREATE;
+        }
+        return rootFlag;
+    }
+
     static String[] strings(Object... args) {
         final String[] results = new String[args.length];
         for (int i = 0; i < args.length; i++) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
index 71716bd..393c4de 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -17,6 +17,7 @@
 package com.android.mtp;
 
 import android.annotation.Nullable;
+import android.mtp.MtpConstants;
 
 class MtpDeviceRecord {
     public final int deviceId;
@@ -38,4 +39,29 @@
         this.operationsSupported = operationsSupported;
         this.eventsSupported = eventsSupported;
     }
+
+    /**
+     * Helper method to check operations/events are supported by the device or not.
+     */
+    static boolean isSupported(@Nullable int[] supportedList, int code) {
+        if (supportedList == null) {
+            return false;
+        }
+        for (int i = 0; i < supportedList.length; i++) {
+            if (supportedList[i] == code) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static boolean isPartialReadSupported(@Nullable int[] supportedList, long fileSize) {
+        return fileSize <= 0xffffffffl &&
+                 isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT);
+    }
+
+    static boolean isWritingSupported(@Nullable int[] supportedList) {
+        return isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT_INFO) &&
+                isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT);
+    }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 4849978..a1c5c9b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -201,6 +201,7 @@
         final Identifier identifier = mDatabase.createIdentifier(documentId);
         try {
             openDevice(identifier.mDeviceId);
+            final MtpDeviceRecord device = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
             switch (mode) {
                 case "r":
                     final long fileSize = getFileSize(documentId);
@@ -208,7 +209,8 @@
                     // 4GB. Fallback to non-seekable file descriptor.
                     // TODO: Use getPartialObject64 for MTP devices that support Android vendor
                     // extension.
-                    if (fileSize <= 0xffffffffl) {
+                    if (MtpDeviceRecord.isPartialReadSupported(
+                            device.operationsSupported, fileSize)) {
                         return mAppFuse.openFile(Integer.parseInt(documentId));
                     } else {
                         return getPipeManager(identifier).readDocument(mMtpManager, identifier);
@@ -216,8 +218,13 @@
                 case "w":
                     // TODO: Clear the parent document loader task (if exists) and call notify
                     // when writing is completed.
-                    return getPipeManager(identifier).writeDocument(
-                            getContext(), mMtpManager, identifier);
+                    if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) {
+                        return getPipeManager(identifier).writeDocument(
+                                getContext(), mMtpManager, identifier);
+                    } else {
+                        throw new UnsupportedOperationException(
+                                "The device does not support writing operation.");
+                    }
                 case "rw":
                     // TODO: Add support for "rw" mode.
                     throw new UnsupportedOperationException(
@@ -290,6 +297,10 @@
         try {
             final Identifier parentId = mDatabase.createIdentifier(parentDocumentId);
             openDevice(parentId.mDeviceId);
+            final MtpDeviceRecord record = getDeviceToolkit(parentId.mDeviceId).mDeviceRecord;
+            if (!MtpDeviceRecord.isWritingSupported(record.operationsSupported)) {
+                throw new UnsupportedOperationException();
+            }
             final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe();
             pipe[0].close();  // 0 bytes for a new document.
             final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ?
@@ -323,9 +334,9 @@
             if (DEBUG) {
                 Log.d(TAG, "Open device " + deviceId);
             }
-            mMtpManager.openDevice(deviceId);
+            final MtpDeviceRecord device = mMtpManager.openDevice(deviceId);
             final DeviceToolkit toolkit =
-                    new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase);
+                    new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase, device);
             mDeviceToolkits.put(deviceId, toolkit);
             mIntentSender.sendUpdateNotificationIntent();
             try {
@@ -347,20 +358,15 @@
         mIntentSender.sendUpdateNotificationIntent();
     }
 
-    int[] getOpenedDeviceIds() {
+    MtpDeviceRecord[] getOpenedDeviceRecordsCache() {
         synchronized (mDeviceListLock) {
-            return mMtpManager.getOpenedDeviceIds();
-        }
-    }
-
-    String getDeviceName(int deviceId) throws IOException {
-        synchronized (mDeviceListLock) {
-            for (final MtpDeviceRecord device : mMtpManager.getDevices()) {
-                if (device.deviceId == deviceId) {
-                    return device.name;
-                }
+            final MtpDeviceRecord[] records = new MtpDeviceRecord[mDeviceToolkits.size()];
+            int i = 0;
+            for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+                records[i] = toolkit.mDeviceRecord;
+                i++;
             }
-            throw new IOException("Not found the device: " + Integer.toString(deviceId));
+            return records;
         }
     }
 
@@ -391,7 +397,10 @@
     public void shutdown() {
         synchronized (mDeviceListLock) {
             try {
-                for (final int id : mMtpManager.getOpenedDeviceIds()) {
+                // Copy the opened key set because it will be modified when closing devices.
+                final Integer[] keySet =
+                        mDeviceToolkits.keySet().toArray(new Integer[mDeviceToolkits.size()]);
+                for (final int id : keySet) {
                     closeDeviceInternal(id);
                 }
             } catch (InterruptedException|IOException e) {
@@ -432,7 +441,7 @@
         getDeviceToolkit(deviceId).mDocumentLoader.close();
         mDeviceToolkits.remove(deviceId);
         mMtpManager.closeDevice(deviceId);
-        if (getOpenedDeviceIds().length == 0) {
+        if (mDeviceToolkits.size() == 0) {
             mRootScanner.pause();
         }
     }
@@ -488,11 +497,14 @@
     private static class DeviceToolkit {
         public final PipeManager mPipeManager;
         public final DocumentLoader mDocumentLoader;
+        public final MtpDeviceRecord mDeviceRecord;
 
         public DeviceToolkit(
-                int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database) {
+                int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database,
+                MtpDeviceRecord record) {
             mPipeManager = new PipeManager(database);
             mDocumentLoader = new DocumentLoader(deviceId, manager, resolver, database);
+            mDeviceRecord = record;
         }
     }
 
@@ -501,8 +513,13 @@
         public long readObjectBytes(
                 int inode, long offset, long size, byte[] buffer) throws IOException {
             final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
-            return mMtpManager.getPartialObject(
-                    identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
+            final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
+            if (MtpDeviceRecord.isPartialReadSupported(record.operationsSupported, offset)) {
+                return mMtpManager.getPartialObject(
+                        identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
+            } else {
+                throw new UnsupportedOperationException();
+            }
         }
 
         @Override
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
index 9c4952b..9b42b78 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
@@ -67,38 +67,25 @@
      */
     private boolean updateForegroundState() {
         final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
-        final int[] deviceIds = provider.getOpenedDeviceIds();
         int notificationId = 0;
         Notification notification = null;
         // TODO: Hide notification if the device has already been removed.
-        for (final int deviceId : deviceIds) {
-            try {
-                final String title = getResources().getString(
-                        R.string.accessing_notification_title,
-                        provider.getDeviceName(deviceIds[0]));
-                final String description = getResources().getString(
-                        R.string.accessing_notification_description);
-                notificationId = deviceId;
-                notification = new Notification.Builder(this)
-                        .setLocalOnly(true)
-                        .setContentTitle(title)
-                        .setContentText(description)
-                        .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb)
-                        .setCategory(Notification.CATEGORY_SYSTEM)
-                        .setPriority(Notification.PRIORITY_LOW)
-                        .build();
-                mNotificationManager.notify(deviceId, notification);
-            } catch (IOException exp) {
-                logErrorMessage(exp);
-                // If we failed to obtain device name, it looks the device is unusable.
-                // Because this is the last device we opened, we should hide the notification
-                // for the case.
-                try {
-                    provider.closeDevice(deviceIds[0]);
-                } catch (IOException | InterruptedException closeError) {
-                    logErrorMessage(closeError);
-                }
-            }
+        for (final MtpDeviceRecord record : provider.getOpenedDeviceRecordsCache()) {
+            final String title = getResources().getString(
+                    R.string.accessing_notification_title,
+                    record.name);
+            final String description = getResources().getString(
+                    R.string.accessing_notification_description);
+            notificationId = record.deviceId;
+            notification = new Notification.Builder(this)
+                    .setLocalOnly(true)
+                    .setContentTitle(title)
+                    .setContentText(description)
+                    .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb)
+                    .setCategory(Notification.CATEGORY_SYSTEM)
+                    .setPriority(Notification.PRIORITY_LOW)
+                    .build();
+            mNotificationManager.notify(record.deviceId, notification);
         }
 
         if (notification != null) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 37dc761..c49005f2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -65,16 +65,14 @@
      */
     private static final int PROTOCOL_MTP = 0;
 
-
     private final UsbManager mManager;
-    // TODO: Save and restore the set of opened device.
     private final SparseArray<MtpDevice> mDevices = new SparseArray<>();
 
     MtpManager(Context context) {
         mManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
     }
 
-    synchronized void openDevice(int deviceId) throws IOException {
+    synchronized MtpDeviceRecord openDevice(int deviceId) throws IOException {
         UsbDevice rawDevice = null;
         for (final UsbDevice candidate : mManager.getDeviceList().values()) {
             if (candidate.getDeviceId() == deviceId) {
@@ -113,6 +111,8 @@
         }
 
         mDevices.put(deviceId, device);
+
+        return createDeviceRecord(rawDevice);
     }
 
     synchronized void closeDevice(int deviceId) throws IOException {
@@ -126,45 +126,11 @@
             if (!isMtpDevice(device)) {
                 continue;
             }
-            final MtpDevice mtpDevice = mDevices.get(device.getDeviceId());
-            final boolean opened = mtpDevice != null;
-            final String name = device.getProductName();
-            MtpRoot[] roots;
-            int[] operationsSupported = null;
-            int[] eventsSupported = null;
-            if (opened) {
-                try {
-                    roots = getRoots(device.getDeviceId());
-                } catch (IOException exp) {
-                    Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
-                    // If we failed to fetch roots for the device, we still returns device model
-                    // with an empty set of roots so that the device is shown DocumentsUI as long as
-                    // the device is physically connected.
-                    roots = new MtpRoot[0];
-                }
-                final MtpDeviceInfo info = mtpDevice.getDeviceInfo();
-                if (info != null) {
-                    operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported();
-                    eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported();
-                }
-            } else {
-                roots = new MtpRoot[0];
-            }
-            devices.add(new MtpDeviceRecord(
-                    device.getDeviceId(), name, device.getSerialNumber(), opened, roots,
-                    operationsSupported, eventsSupported));
+            devices.add(createDeviceRecord(device));
         }
         return devices.toArray(new MtpDeviceRecord[devices.size()]);
     }
 
-    synchronized int[] getOpenedDeviceIds() {
-        final int[] result = new int[mDevices.size()];
-        for (int i = 0; i < result.length; i++) {
-            result[i] = mDevices.keyAt(i);
-        }
-        return result;
-    }
-
     MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
             throws IOException {
         final MtpDevice device = getDevice(deviceId);
@@ -281,6 +247,36 @@
         }
     }
 
+    private MtpDeviceRecord createDeviceRecord(UsbDevice device) {
+        final MtpDevice mtpDevice = mDevices.get(device.getDeviceId());
+        final boolean opened = mtpDevice != null;
+        final String name = device.getProductName();
+        MtpRoot[] roots;
+        int[] operationsSupported = null;
+        int[] eventsSupported = null;
+        if (opened) {
+            try {
+                roots = getRoots(device.getDeviceId());
+            } catch (IOException exp) {
+                Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp);
+                // If we failed to fetch roots for the device, we still returns device model
+                // with an empty set of roots so that the device is shown DocumentsUI as long as
+                // the device is physically connected.
+                roots = new MtpRoot[0];
+            }
+            final MtpDeviceInfo info = mtpDevice.getDeviceInfo();
+            if (info != null) {
+                operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported();
+                eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported();
+            }
+        } else {
+            roots = new MtpRoot[0];
+        }
+        return new MtpDeviceRecord(
+                device.getDeviceId(), name, device.getSerialNumber(), opened, roots,
+                operationsSupported, eventsSupported);
+    }
+
     static boolean isMtpDevice(UsbDevice device) {
         for (int i = 0; i < device.getInterfaceCount(); i++) {
             final UsbInterface usbInterface = device.getInterface(i);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index a48bf12..82ba21f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -149,7 +149,8 @@
                     }
                     try {
                         mDatabase.getMapper().startAddingDocuments(documentId);
-                        if (mDatabase.getMapper().putStorageDocuments(documentId, device.roots)) {
+                        if (mDatabase.getMapper().putStorageDocuments(
+                                documentId, device.eventsSupported, device.roots)) {
                             changed = true;
                         }
                         if (mDatabase.getMapper().stopAddingDocuments(documentId)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index b75a9e6..a000895 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -48,7 +48,7 @@
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", new int[0], new MtpRoot[] {
                 new MtpRoot(0, 0, "Storage", 1000, 1000, "")
         });
         mDatabase.getMapper().stopAddingDocuments("1");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 05c9c57..48cde4c 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -30,10 +30,11 @@
 import static android.provider.DocumentsContract.Document.*;
 import static com.android.mtp.MtpDatabase.strings;
 import static com.android.mtp.MtpDatabaseConstants.*;
+import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
 
 @SmallTest
 public class MtpDatabaseTest extends AndroidTestCase {
-    private final String[] COLUMN_NAMES = new String[] {
+    private static final String[] COLUMN_NAMES = new String[] {
         DocumentsContract.Document.COLUMN_DOCUMENT_ID,
         MtpDatabaseConstants.COLUMN_DEVICE_ID,
         MtpDatabaseConstants.COLUMN_STORAGE_ID,
@@ -75,13 +76,10 @@
     }
 
     public void testPutSingleStorageDocuments() throws Exception {
-        mDatabase.getMapper().startAddingDocuments(null);
-        mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
-                0, "Device", null /* deviceKey */, true, new MtpRoot[0], null, null));
-        mDatabase.getMapper().stopAddingDocuments(null);
+        addTestDevice();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 1, "Storage", 1000, 2000, "")
         });
         mDatabase.getMapper().stopAddingDocuments("1");
@@ -143,7 +141,7 @@
         addTestDevice();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 1, "Storage", 1000, 2000, ""),
                 new MtpRoot(0, 2, "Storage", 2000, 4000, ""),
                 new MtpRoot(0, 3, "/@#%&<>Storage", 3000, 6000,"")
@@ -273,7 +271,7 @@
         // Add device and two storages.
         addTestDevice();
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage A", 1000, 0, ""),
                 new MtpRoot(0, 101, "Storage B", 1001, 0, "")
         });
@@ -304,7 +302,7 @@
 
         // Add two storages, but one's name is different from previous one.
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 200, "Storage A", 2000, 0, ""),
                 new MtpRoot(0, 202, "Storage C", 2002, 0, "")
         });
@@ -398,10 +396,10 @@
 
         mDatabase.getMapper().startAddingDocuments("1");
         mDatabase.getMapper().startAddingDocuments("2");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage", 0, 0, "")
         });
-        mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(1, 100, "Storage", 0, 0, "")
         });
 
@@ -442,10 +440,10 @@
 
         mDatabase.getMapper().startAddingDocuments("1");
         mDatabase.getMapper().startAddingDocuments("2");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 200, "Storage", 2000, 0, "")
         });
-        mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(1, 300, "Storage", 3000, 0, "")
         });
         mDatabase.getMapper().stopAddingDocuments("1");
@@ -562,7 +560,7 @@
         addTestDevice();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
@@ -576,7 +574,7 @@
         }
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 200, "Storage", 2000, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
@@ -584,7 +582,7 @@
         addTestDevice();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 300, "Storage", 3000, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
@@ -625,7 +623,7 @@
         // Add a device and two storages that has same name.
         addTestDevice();
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 200, "Storage", 2000, 0, ""),
                 new MtpRoot(0, 201, "Storage", 2001, 0, ""),
         });
@@ -658,13 +656,13 @@
         // The client code should be able to replace existing rows with new information.
         // Add one.
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
         // Replace it.
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage B", 1000, 1000, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
@@ -703,7 +701,7 @@
         // Add one.
         addTestDevice();
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().clearMapping();
@@ -717,11 +715,11 @@
 
             // Add one.
             mDatabase.getMapper().startAddingDocuments("1");
-            mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+            mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                     new MtpRoot(0, 101, "Storage B", 1000, 1000, ""),
             });
             // Add one more before resolving unmapped documents.
-            mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+            mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                     new MtpRoot(0, 102, "Storage B", 1000, 1000, ""),
             });
             mDatabase.getMapper().stopAddingDocuments("1");
@@ -763,7 +761,7 @@
         }
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage A", 0, 0, "")
         });
         mDatabase.getMapper().stopAddingDocuments("1");
@@ -778,7 +776,7 @@
         }
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
                 new MtpRoot(0, 101, "Storage B", 0, 0, "")
         });
@@ -798,7 +796,7 @@
         addTestDevice();
 
         mDatabase.getMapper().startAddingDocuments("1");
-        mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] {
+        mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage A", 0, 0, ""),
         });
         mDatabase.getMapper().stopAddingDocuments("1");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 884d132..db82bcb 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -38,6 +38,7 @@
 import java.util.concurrent.TimeoutException;
 
 import static com.android.mtp.MtpDatabase.strings;
+import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
 
 @MediumTest
 public class MtpDocumentsProviderTest extends AndroidTestCase {
@@ -77,7 +78,7 @@
                             2048 /* total space */,
                             "" /* no volume identifier */)
                 },
-                null,
+                OPERATIONS_SUPPORTED,
                 null));
 
         mProvider.resumeRootScanner();
@@ -98,7 +99,7 @@
         } catch (Throwable error) {
             assertTrue(error instanceof IOException);
         }
-        assertEquals(0, mProvider.getOpenedDeviceIds().length);
+        assertEquals(0, mProvider.getOpenedDeviceRecordsCache().length);
 
         // Check if the following notification is the first one or not.
         mMtpManager.addValidDevice(new MtpDeviceRecord(
@@ -115,7 +116,7 @@
                             2048 /* total space */,
                             "" /* no volume identifier */)
                 },
-                null,
+                OPERATIONS_SUPPORTED,
                 null));
         mProvider.resumeRootScanner();
         mResolver.waitForNotification(ROOTS_URI, 1);
@@ -139,7 +140,7 @@
                             2048 /* total space */,
                             "" /* no volume identifier */)
                 },
-                null,
+                OPERATIONS_SUPPORTED,
                 null));
         mMtpManager.setObjectHandles(0, 1, -1, new int[0]);
         mProvider.resumeRootScanner();
@@ -155,16 +156,16 @@
             assertEquals(1, cursor.getLong(1));
         }
         {
-            final int [] openedDevice = mProvider.getOpenedDeviceIds();
+            final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache();
             assertEquals(0, openedDevice.length);
         }
         // Device is opened automatically when querying its children.
         try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {}
 
         {
-            final int [] openedDevice = mProvider.getOpenedDeviceIds();
+            final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache();
             assertEquals(1, openedDevice.length);
-            assertEquals(0, openedDevice[0]);
+            assertEquals(0, openedDevice[0].deviceId);
         }
     }
 
@@ -184,7 +185,7 @@
                                 2048 /* total space */,
                                 "" /* no volume identifier */)
                 },
-                null,
+                OPERATIONS_SUPPORTED,
                 null));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
@@ -200,7 +201,7 @@
                             4096 /* total space */,
                             "Identifier B" /* no volume identifier */)
                 },
-                null,
+                new int[0] /* No operations supported */,
                 null));
 
         {
@@ -225,7 +226,7 @@
             cursor.moveToNext();
             cursor.moveToNext();
             assertEquals("2", cursor.getString(0));
-            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
+            assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1));
             assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device B Storage B", cursor.getString(3));
             assertEquals("2", cursor.getString(4));
@@ -241,7 +242,7 @@
                 "Device key A",
                 false /* unopened */,
                 new MtpRoot[0],
-                null,
+                OPERATIONS_SUPPORTED,
                 null));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
@@ -257,7 +258,7 @@
                             4096 /* total space */,
                             "Identifier B" /* no volume identifier */)
                 },
-                null,
+                OPERATIONS_SUPPORTED,
                 null));
         {
             mProvider.openDevice(0);
@@ -544,14 +545,14 @@
     public void testBusyDevice() throws Exception {
         mMtpManager = new TestMtpManager(getContext()) {
             @Override
-            void openDevice(int deviceId) throws IOException {
+            MtpDeviceRecord openDevice(int deviceId) throws IOException {
                 throw new BusyDeviceException();
             }
         };
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mMtpManager.addValidDevice(new MtpDeviceRecord(
-                0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0], null,
-                null));
+                0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0],
+                OPERATIONS_SUPPORTED, null));
 
         mProvider.resumeRootScanner();
         mResolver.waitForNotification(ROOTS_URI, 1);
@@ -571,7 +572,8 @@
     public void testLockedDevice() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mMtpManager.addValidDevice(new MtpDeviceRecord(
-                0, "Device A", null, false /* unopened */, new MtpRoot[0], null, null));
+                0, "Device A", null, false /* unopened */, new MtpRoot[0], OPERATIONS_SUPPORTED,
+                null));
 
         mProvider.resumeRootScanner();
         mResolver.waitForNotification(ROOTS_URI, 1);
@@ -661,6 +663,60 @@
         }
     }
 
+    public void testCreateDocument_noWritingSupport() throws Exception {
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        mMtpManager.addValidDevice(new MtpDeviceRecord(
+                0, "Device A", null /* deviceKey */, false /* unopened */,
+                new MtpRoot[] {
+                        new MtpRoot(
+                                0 /* deviceId */,
+                                1 /* storageId */,
+                                "Storage A" /* volume description */,
+                                1024 /* free space */,
+                                2048 /* total space */,
+                                "" /* no volume identifier */)
+                },
+                new int[0] /* no operations supported */, null));
+        mProvider.resumeRootScanner();
+        mResolver.waitForNotification(ROOTS_URI, 1);
+        try {
+            mProvider.createDocument("1", "text/palin", "note.txt");
+            fail();
+        } catch (UnsupportedOperationException exception) {}
+    }
+
+    public void testOpenDocument_noWritingSupport() throws Exception {
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        mMtpManager.addValidDevice(new MtpDeviceRecord(
+                0, "Device A", null /* deviceKey */, false /* unopened */,
+                new MtpRoot[] {
+                        new MtpRoot(
+                                0 /* deviceId */,
+                                1 /* storageId */,
+                                "Storage A" /* volume description */,
+                                1024 /* free space */,
+                                2048 /* total space */,
+                                "" /* no volume identifier */)
+                },
+                new int[0] /* no operations supported */, null));
+        mMtpManager.setObjectHandles(
+                0, 1, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, new int[] { 100 });
+        mMtpManager.setObjectInfo(
+                0, new MtpObjectInfo.Builder().setObjectHandle(100).setName("note.txt").build());
+        mProvider.resumeRootScanner();
+        mResolver.waitForNotification(ROOTS_URI, 1);
+        try (final Cursor cursor = mProvider.queryChildDocuments(
+                "1", strings(Document.COLUMN_DOCUMENT_ID), null)) {
+            assertEquals(1, cursor.getCount());
+            cursor.moveToNext();
+            assertEquals("3", cursor.getString(0));
+        }
+        try {
+            mProvider.openDocument("3", "w", null);
+            fail();
+        } catch (UnsupportedOperationException exception) {}
+    }
+
     private void setupProvider(int flag) {
         mDatabase = new MtpDatabase(getContext(), flag);
         mProvider = new MtpDocumentsProvider();
@@ -691,7 +747,7 @@
         final int changeCount = mResolver.getChangeCount(ROOTS_URI);
         mMtpManager.addValidDevice(
                 new MtpDeviceRecord(deviceId, "Device", null /* deviceKey */, false /* unopened */,
-                roots, null, null));
+                roots, OPERATIONS_SUPPORTED, null));
         mProvider.openDevice(deviceId);
         mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
         return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 3043ce8..5e0ee1e 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -22,6 +22,7 @@
 import android.util.SparseArray;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -84,16 +85,16 @@
     }
 
     @Override
-    void openDevice(int deviceId) throws IOException {
+    MtpDeviceRecord openDevice(int deviceId) throws IOException {
         final MtpDeviceRecord device = mDevices.get(deviceId);
         if (device == null) {
             throw new IOException();
         }
-        mDevices.put(
-                deviceId,
-                new MtpDeviceRecord(
-                        device.deviceId, device.name, device.deviceKey, true, device.roots, null,
-                        null));
+        final MtpDeviceRecord record = new MtpDeviceRecord(
+                device.deviceId, device.name, device.deviceKey, true, device.roots,
+                device.operationsSupported, device.eventsSupported);
+        mDevices.put(deviceId, record);
+        return record;
     }
 
     @Override
@@ -198,19 +199,6 @@
     }
 
     @Override
-    int[] getOpenedDeviceIds() {
-        final int[] result = new int[mDevices.size()];
-        int count = 0;
-        for (int i = 0; i < mDevices.size(); i++) {
-            final MtpDeviceRecord device = mDevices.valueAt(i);
-            if (device.opened) {
-                result[count++] = device.deviceId;
-            }
-        }
-        return Arrays.copyOf(result, count);
-    }
-
-    @Override
     byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException {
         return mImportFileBytes.get(pack(deviceId, objectHandle));
     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
index 34dd77b..8adb68f 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -19,6 +19,7 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
 import android.os.SystemClock;
 
 import java.io.FileNotFoundException;
@@ -32,6 +33,12 @@
 final class TestUtil {
     private TestUtil() {}
 
+    static final int[] OPERATIONS_SUPPORTED = new int[] {
+            MtpConstants.OPERATION_GET_PARTIAL_OBJECT,
+            MtpConstants.OPERATION_SEND_OBJECT,
+            MtpConstants.OPERATION_SEND_OBJECT_INFO,
+    };
+
     /**
      * Requests permission for a MTP device and returns the first MTP device that has at least one
      * storage.
@@ -59,14 +66,14 @@
     static void addTestDevice(MtpDatabase database) throws FileNotFoundException {
         database.getMapper().startAddingDocuments(null);
         database.getMapper().putDeviceDocument(new MtpDeviceRecord(
-                0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], null,
-                null));
+                0, "Device", "device_key", /* opened is */ true, new MtpRoot[0],
+                OPERATIONS_SUPPORTED, null));
         database.getMapper().stopAddingDocuments(null);
     }
 
     static void addTestStorage(MtpDatabase database, String parentId) throws FileNotFoundException {
         database.getMapper().startAddingDocuments(parentId);
-        database.getMapper().putStorageDocuments(parentId, new MtpRoot[] {
+        database.getMapper().putStorageDocuments(parentId, OPERATIONS_SUPPORTED, new MtpRoot[] {
                 new MtpRoot(0, 100, "Storage", 1024, 1024, ""),
         });
         database.getMapper().stopAddingDocuments(parentId);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 2ae3ec62..7a6aad6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -16,6 +16,7 @@
 
 package com.android.printspooler.model;
 
+import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.net.Uri;
@@ -57,6 +58,8 @@
 
     private static final boolean DEBUG = false;
 
+    private static final long FORCE_CANCEL_TIMEOUT = 1000; // ms
+
     private static final int STATE_INITIAL = 0;
     private static final int STATE_STARTED = 1;
     private static final int STATE_UPDATING = 2;
@@ -212,7 +215,7 @@
             // cancellation and start over.
             if (mCurrentCommand != null && (mCurrentCommand.isRunning()
                     || mCurrentCommand.isPending())) {
-                mCurrentCommand.cancel();
+                mCurrentCommand.cancel(false);
             }
 
             // Schedule a layout command.
@@ -233,7 +236,7 @@
             // Cancel the current write as a new one is to be scheduled.
             if (mCurrentCommand instanceof WriteCommand
                     && (mCurrentCommand.isPending() || mCurrentCommand.isRunning())) {
-                mCurrentCommand.cancel();
+                mCurrentCommand.cancel(false);
             }
 
             // Schedule a write command.
@@ -277,9 +280,9 @@
         }
     }
 
-    public void cancel() {
+    public void cancel(boolean force) {
         if (DEBUG) {
-            Log.i(LOG_TAG, "[CALLED] cancel()");
+            Log.i(LOG_TAG, "[CALLED] cancel(" + force + ")");
         }
 
         mNextCommand = null;
@@ -290,7 +293,7 @@
 
         mState = STATE_CANCELING;
 
-        mCurrentCommand.cancel();
+        mCurrentCommand.cancel(force);
     }
 
     public void destroy() {
@@ -441,8 +444,9 @@
         if (mCurrentCommand != null) {
             if (mCurrentCommand.isPending()) {
                 mCurrentCommand.run();
+
+                mState = STATE_UPDATING;
             }
-            mState = STATE_UPDATING;
         } else {
             mState = STATE_UPDATED;
         }
@@ -535,14 +539,17 @@
 
         protected final CommandDoneCallback mDoneCallback;
 
+        private final Handler mHandler;
+
         protected ICancellationSignal mCancellation;
 
         private CharSequence mError;
 
         private int mState = STATE_PENDING;
 
-        public AsyncCommand(IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
+        public AsyncCommand(Looper looper, IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
                 CommandDoneCallback doneCallback) {
+            mHandler = new AsyncCommandHandler(looper);
             mAdapter = adapter;
             mDocument = document;
             mDoneCallback = doneCallback;
@@ -556,7 +563,29 @@
             return mState == STATE_CANCELED;
         }
 
-        public final void cancel() {
+        /**
+         * If a force cancel is pending, remove it. This is usually called when a command returns
+         * and thereby does not need to be canceled anymore.
+         */
+        protected void removeForceCancel() {
+            if (DEBUG) {
+                if (mHandler.hasMessages(AsyncCommandHandler.MSG_FORCE_CANCEL)) {
+                    Log.i(LOG_TAG, "[FORCE CANCEL] Removed");
+                }
+            }
+
+            mHandler.removeMessages(AsyncCommandHandler.MSG_FORCE_CANCEL);
+        }
+
+        /**
+         * Cancel the current command.
+         *
+         * @param force If set, does not wait for the {@link PrintDocumentAdapter} to cancel. This
+         *              should only be used if this is the last command send to the as otherwise the
+         *              {@link PrintDocumentAdapter adapter} might get commands while it is still
+         *              running the old one.
+         */
+        public final void cancel(boolean force) {
             if (isRunning()) {
                 canceling();
                 if (mCancellation != null) {
@@ -566,14 +595,25 @@
                         Log.w(LOG_TAG, "Error while canceling", re);
                     }
                 }
-            } else if (isCanceling()) {
-                // Nothing to do
-            } else {
-                canceled();
-
-                // Done.
-                mDoneCallback.onDone();
             }
+
+            if (isCanceling()) {
+                if (force) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "[FORCE CANCEL] queued");
+                    }
+                    mHandler.sendMessageDelayed(
+                            mHandler.obtainMessage(AsyncCommandHandler.MSG_FORCE_CANCEL),
+                            FORCE_CANCEL_TIMEOUT);
+                }
+
+                return;
+            }
+
+            canceled();
+
+            // Done.
+            mDoneCallback.onDone();
         }
 
         protected final void canceling() {
@@ -617,7 +657,7 @@
         }
 
         protected final void failed(CharSequence error) {
-            if (mState != STATE_RUNNING) {
+            if (mState != STATE_RUNNING && mState != STATE_CANCELING) {
                 throw new IllegalStateException("Not running.");
             }
             mState = STATE_FAILED;
@@ -632,6 +672,37 @@
         public CharSequence getError() {
             return mError;
         }
+
+        /**
+         * Handler for the async command.
+         */
+        private class AsyncCommandHandler extends Handler {
+            /** Message indicated the desire for to force cancel a command */
+            final static int MSG_FORCE_CANCEL = 0;
+
+            AsyncCommandHandler(@NonNull Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_FORCE_CANCEL:
+                        if (isCanceling()) {
+                            if (DEBUG) {
+                                Log.i(LOG_TAG, "[FORCE CANCEL] executed");
+                            }
+                            failed("Command did not respond to cancellation in "
+                                    + FORCE_CANCEL_TIMEOUT + " ms");
+
+                            mDoneCallback.onDone();
+                        }
+                        break;
+                    default:
+                        // not reached;
+                }
+            }
+        }
     }
 
     private static final class LayoutCommand extends AsyncCommand {
@@ -646,7 +717,7 @@
         public LayoutCommand(Looper looper, IPrintDocumentAdapter adapter,
                 RemotePrintDocumentInfo document, PrintAttributes oldAttributes,
                 PrintAttributes newAttributes, boolean preview, CommandDoneCallback callback) {
-            super(adapter, document, callback);
+            super(looper, adapter, document, callback);
             mHandler = new LayoutHandler(looper);
             mRemoteResultCallback = new LayoutResultCallback(mHandler);
             mOldAttributes.copyFrom(oldAttributes);
@@ -795,6 +866,21 @@
 
             @Override
             public void handleMessage(Message message) {
+                // The command might have been force canceled, see
+                // AsyncCommand.AsyncCommandHandler#handleMessage
+                if (isFailed()) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "[CALLBACK] on canceled layout command");
+                    }
+
+                    return;
+                } else {
+                    if (message.what != MSG_ON_LAYOUT_STARTED) {
+                        // No need to force cancel anymore if layout finished
+                        removeForceCancel();
+                    }
+                }
+
                 switch (message.what) {
                     case MSG_ON_LAYOUT_STARTED: {
                         ICancellationSignal cancellation = (ICancellationSignal) message.obj;
@@ -882,7 +968,7 @@
         public WriteCommand(Context context, Looper looper, IPrintDocumentAdapter adapter,
                 RemotePrintDocumentInfo document, int pageCount, PageRange[] pages,
                 MutexFileProvider fileProvider, CommandDoneCallback callback) {
-            super(adapter, document, callback);
+            super(looper, adapter, document, callback);
             mContext = context;
             mHandler = new WriteHandler(looper);
             mRemoteResultCallback = new WriteResultCallback(mHandler);
@@ -1052,6 +1138,21 @@
 
             @Override
             public void handleMessage(Message message) {
+                // The command might have been force canceled, see
+                // AsyncCommand.AsyncCommandHandler#handleMessage
+                if (isFailed()) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "[CALLBACK] on canceled write command");
+                    }
+
+                    return;
+                } else {
+                    if (message.what != MSG_ON_WRITE_STARTED) {
+                        // No need to force cancel anymore if write finished
+                        removeForceCancel();
+                    }
+                }
+
                 switch (message.what) {
                     case MSG_ON_WRITE_STARTED: {
                         ICancellationSignal cancellation = (ICancellationSignal) message.obj;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 08cd0b6..64f5cc6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -320,8 +320,8 @@
                 if (isFinishing() || (isFinalState(mState) && !mPrintedDocument.isUpdating())) {
                     return;
                 }
-                mPrintedDocument.cancel();
                 setState(STATE_PRINT_CANCELED);
+                mPrintedDocument.cancel(true);
                 doFinish();
             }
         }, PrintActivity.this);
@@ -1013,7 +1013,7 @@
     }
 
     private void requestCreatePdfFileOrFinish() {
-        mPrintedDocument.cancel();
+        mPrintedDocument.cancel(false);
 
         if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) {
             startCreateDocumentActivity();
@@ -1130,7 +1130,7 @@
     private void cancelPrint() {
         setState(STATE_PRINT_CANCELED);
         updateOptionsUi();
-        mPrintedDocument.cancel();
+        mPrintedDocument.cancel(true);
         doFinish();
     }
 
@@ -1889,7 +1889,7 @@
     public void onPrinterUnavailable(PrinterInfo printer) {
         if (mCurrentPrinter.getId().equals(printer.getId())) {
             setState(STATE_PRINTER_UNAVAILABLE);
-            mPrintedDocument.cancel();
+            mPrintedDocument.cancel(false);
             ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
                     PrintErrorFragment.ACTION_NONE);
             updateOptionsUi();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 1c032fa..a578055 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -18,6 +18,7 @@
 import android.annotation.LayoutRes;
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -235,20 +236,24 @@
                     Intent.FLAG_ACTIVITY_CLEAR_TASK));
             return true;
         }
-        int numUserHandles = tile.userHandle.size();
-        if (numUserHandles > 1) {
-            ProfileSelectDialog.show(getFragmentManager(), tile);
-            return false;
-        } else if (numUserHandles == 1) {
-            // Show menu on top level items.
-            tile.intent.putExtra(EXTRA_SHOW_MENU, true);
-            tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            startActivityAsUser(tile.intent, tile.userHandle.get(0));
-        } else {
-            // Show menu on top level items.
-            tile.intent.putExtra(EXTRA_SHOW_MENU, true);
-            tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            startActivity(tile.intent);
+        try {
+            int numUserHandles = tile.userHandle.size();
+            if (numUserHandles > 1) {
+                ProfileSelectDialog.show(getFragmentManager(), tile);
+                return false;
+            } else if (numUserHandles == 1) {
+                // Show menu on top level items.
+                tile.intent.putExtra(EXTRA_SHOW_MENU, true);
+                tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                startActivityAsUser(tile.intent, tile.userHandle.get(0));
+            } else {
+                // Show menu on top level items.
+                tile.intent.putExtra(EXTRA_SHOW_MENU, true);
+                tile.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                startActivity(tile.intent);
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "Couldn't find tile " + tile.intent, e);
         }
         return true;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 5b8ed28..380fcd4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -110,6 +110,7 @@
     private final Context mContext;
 
     private String ssid;
+    private String bssid;
     private int security;
     private int networkId = WifiConfiguration.INVALID_NETWORK_ID;
 
@@ -335,6 +336,10 @@
         return ssid;
     }
 
+    public String getBssid() {
+        return bssid;
+    }
+
     public CharSequence getSsid() {
         SpannableString str = new SpannableString(ssid);
         str.setSpan(new TtsSpan.VerbatimBuilder(ssid).build(), 0, ssid.length(),
@@ -657,6 +662,7 @@
         else
             ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
 
+        bssid = config.BSSID;
         security = getSecurity(config);
         networkId = config.networkId;
         mConfig = config;
@@ -664,6 +670,7 @@
 
     private void initWithScanResult(ScanResult result) {
         ssid = result.SSID;
+        bssid = result.BSSID;
         security = getSecurity(result);
         if (security == SECURITY_PSK)
             pskType = getPskType(result);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 20aca0e..36097a1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -149,6 +149,9 @@
     private static final int MSG_SCREENSHOT_REQUEST = 4;
     private static final int MSG_SCREENSHOT_RESPONSE = 5;
 
+    // Passed to Message.obtain() when msg.arg2 is not used.
+    private static final int UNUSED_ARG2 = -2;
+
     /**
      * Delay before a screenshot is taken.
      * <p>
@@ -302,8 +305,7 @@
             }
             final String action = intent.getAction();
             final int pid = intent.getIntExtra(EXTRA_PID, 0);
-            // TODO: temporarily using pid as id until test cases and dumpstate are changed.
-            final int id = intent.getIntExtra(EXTRA_ID, pid);
+            final int id = intent.getIntExtra(EXTRA_ID, 0);
             final int max = intent.getIntExtra(EXTRA_MAX, -1);
             final String name = intent.getStringExtra(EXTRA_NAME);
 
@@ -434,10 +436,12 @@
         final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
         infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH);
         infoIntent.putExtra(EXTRA_ID, info.id);
+        final PendingIntent infoPendingIntent =
+                PendingIntent.getService(mContext, info.id, infoIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
         final Action infoAction = new Action.Builder(null,
                 mContext.getString(R.string.bugreport_info_action),
-                PendingIntent.getService(mContext, info.id, infoIntent,
-                        PendingIntent.FLAG_UPDATE_CURRENT)).build();
+                infoPendingIntent).build();
         final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class);
         screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT);
         screenshotIntent.putExtra(EXTRA_ID, info.id);
@@ -464,6 +468,7 @@
                 .setLocalOnly(true)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color))
+                .setContentIntent(infoPendingIntent)
                 .addAction(infoAction)
                 .addAction(screenshotAction)
                 .addAction(cancelAction)
@@ -474,7 +479,10 @@
                     + info + ")");
             return;
         }
-        Log.v(TAG, "Sending 'Progress' notification for id " + info.id + ": " + percentText);
+        if (DEBUG) {
+            Log.d(TAG, "Sending 'Progress' notification for id " + info.id + "(pid " + info.pid
+                    + "): " + percentText);
+        }
         NotificationManager.from(mContext).notify(TAG, info.id, notification);
     }
 
@@ -541,7 +549,7 @@
             final int pid = info.pid;
             final int id = info.id;
             if (info.finished) {
-                if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + "(id: " + id + ")");
+                if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + " (id: " + id + ")");
                 continue;
             }
             activeProcesses++;
@@ -662,11 +670,8 @@
         final String screenshotPath =
                 new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath();
 
-        final Message requestMsg = new Message();
-        requestMsg.what = MSG_SCREENSHOT_REQUEST;
-        requestMsg.arg1 = id;
-        requestMsg.obj = screenshotPath;
-        mScreenshotHandler.sendMessage(requestMsg);
+        Message.obtain(mScreenshotHandler, MSG_SCREENSHOT_REQUEST, id, UNUSED_ARG2, screenshotPath)
+                .sendToTarget();
     }
 
     /**
@@ -692,12 +697,8 @@
         boolean taken = takeScreenshot(mContext, screenshotFile);
         setTakingScreenshot(false);
 
-        final Message resultMsg = new Message();
-        resultMsg.what = MSG_SCREENSHOT_RESPONSE;
-        resultMsg.arg1 = requestMsg.arg1;
-        resultMsg.arg2 = taken ? 1 : 0;
-        resultMsg.obj = screenshotFile;
-        mMainHandler.sendMessage(resultMsg);
+        Message.obtain(mMainHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0,
+                screenshotFile).sendToTarget();
     }
 
     private void handleScreenshotResponse(Message resultMsg) {
@@ -708,7 +709,7 @@
         }
         final File screenshotFile = new File((String) resultMsg.obj);
 
-        final int msgId;
+        final String msg;
         if (taken) {
             info.addScreenshot(screenshotFile);
             if (info.finished) {
@@ -716,14 +717,13 @@
                 info.renameScreenshots(mScreenshotsDir);
                 sendBugreportNotification(mContext, info);
             }
-            msgId = R.string.bugreport_screenshot_taken;
+            msg = mContext.getString(R.string.bugreport_screenshot_taken);
         } else {
             // TODO: try again using Framework APIs instead of relying on screencap.
-            msgId = R.string.bugreport_screenshot_failed;
+            msg = mContext.getString(R.string.bugreport_screenshot_failed);
+            Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
         }
-        final String msg = mContext.getString(msgId);
         Log.d(TAG, msg);
-        Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
     }
 
     /**
@@ -1125,7 +1125,7 @@
 
     private static boolean setSystemProperty(String key, String value) {
         try {
-            if (DEBUG) Log.v(TAG, "Setting system property" + key + " to " + value);
+            if (DEBUG) Log.v(TAG, "Setting system property " + key + " to " + value);
             SystemProperties.set(key, value);
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Could not set property " + key + " to " + value, e);
@@ -1249,6 +1249,8 @@
          * Sets its internal state and displays the dialog.
          */
         private void initialize(final Context context, BugreportInfo info) {
+            final String dialogTitle =
+                    context.getString(R.string.bugreport_info_dialog_title, info.id);
             // First initializes singleton.
             if (mDialog == null) {
                 @SuppressLint("InflateParams")
@@ -1272,7 +1274,7 @@
 
                 mDialog = new AlertDialog.Builder(context)
                         .setView(view)
-                        .setTitle(context.getString(R.string.bugreport_info_dialog_title, info.id))
+                        .setTitle(dialogTitle)
                         .setCancelable(false)
                         .setPositiveButton(context.getString(com.android.internal.R.string.ok),
                                 null)
@@ -1297,6 +1299,12 @@
                         new WindowManager.LayoutParams(
                                 WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG));
 
+            } else {
+                // Re-use view, but reset fields first.
+                mDialog.setTitle(dialogTitle);
+                mInfoName.setText(null);
+                mInfoTitle.setText(null);
+                mInfoDescription.setText(null);
             }
 
             // Then set fields.
@@ -1372,7 +1380,7 @@
             // Must update system property for the cases where dumpstate finishes
             // while the user is still entering other fields (like title or
             // description)
-            setBugreportNameProperty(mId, name);
+            setBugreportNameProperty(mPid, name);
         }
 
        /**
@@ -1517,7 +1525,7 @@
             final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
             for (File oldFile : screenshotFiles) {
                 final String oldName = oldFile.getName();
-                final String newName = oldName.replace(Integer.toString(pid), name);
+                final String newName = oldName.replaceFirst(Integer.toString(pid), name);
                 final File newFile;
                 if (!newName.equals(oldName)) {
                     final File renamedFile = new File(screenshotDir, newName);
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 8c62670..d0499a5 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -19,6 +19,7 @@
 import static android.test.MoreAsserts.assertContainsRegex;
 import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
 import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
+import static com.android.shell.BugreportProgressService.EXTRA_ID;
 import static com.android.shell.BugreportProgressService.EXTRA_MAX;
 import static com.android.shell.BugreportProgressService.EXTRA_NAME;
 import static com.android.shell.BugreportProgressService.EXTRA_PID;
@@ -60,6 +61,7 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.text.TextUtils;
@@ -98,24 +100,33 @@
     private static final String BUGREPORTS_DIR = "bugreports";
     private static final String BUGREPORT_FILE = "test_bugreport.txt";
     private static final String ZIP_FILE = "test_bugreport.zip";
+    private static final String ZIP_FILE2 = "test_bugreport2.zip";
     private static final String SCREENSHOT_FILE = "test_screenshot.png";
 
     private static final String BUGREPORT_CONTENT = "Dump, might as well dump!\n";
     private static final String SCREENSHOT_CONTENT = "A picture is worth a thousand words!\n";
 
     private static final int PID = 42;
-    private static final String PROGRESS_PROPERTY = "dumpstate.42.progress";
-    private static final String MAX_PROPERTY = "dumpstate.42.max";
-    private static final String NAME_PROPERTY = "dumpstate.42.name";
+    private static final int PID2 = 24;
+    private static final int ID = 108;
+    private static final int ID2 = 801;
+    private static final String PROGRESS_PROPERTY = "dumpstate." + PID + ".progress";
+    private static final String MAX_PROPERTY = "dumpstate." + PID + ".max";
+    private static final String NAME_PROPERTY = "dumpstate." + PID + ".name";
     private static final String NAME = "BUG, Y U NO REPORT?";
+    private static final String NAME2 = "A bugreport's life";
     private static final String NEW_NAME = "Bug_Forrest_Bug";
+    private static final String NEW_NAME2 = "BugsyReportsy";
     private static final String TITLE = "Wimbugdom Champion 2015";
+    private static final String TITLE2 = "Master of the Universe";
+    private static final String DESCRIPTION = "One's description...";
+    private static final String DESCRIPTION2 = "...is another's treasure.";
 
     private static final String NO_DESCRIPTION = null;
     private static final String NO_NAME = null;
     private static final String NO_SCREENSHOT = null;
     private static final String NO_TITLE = null;
-    private static final int NO_PID = 0;
+    private static final int NO_ID = 0;
     private static final boolean RENAMED_SCREENSHOTS = true;
     private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
 
@@ -123,6 +134,7 @@
 
     private String mPlainTextPath;
     private String mZipPath;
+    private String mZipPath2;
     private String mScreenshotPath;
 
     private Context mContext;
@@ -141,10 +153,12 @@
 
         mPlainTextPath = getPath(BUGREPORT_FILE);
         mZipPath = getPath(ZIP_FILE);
+        mZipPath2 = getPath(ZIP_FILE2);
         mScreenshotPath = getPath(SCREENSHOT_FILE);
         createTextFile(mPlainTextPath, BUGREPORT_CONTENT);
         createTextFile(mScreenshotPath, SCREENSHOT_CONTENT);
         createZipFile(mZipPath, BUGREPORT_FILE, BUGREPORT_CONTENT);
+        createZipFile(mZipPath2, BUGREPORT_FILE, BUGREPORT_CONTENT);
 
         // Creates a multi-line description.
         StringBuilder sb = new StringBuilder();
@@ -177,13 +191,32 @@
         assertProgressNotification(NAME, nf.format(0.25));
 
         Bundle extras =
-                sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+                sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
                 NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
 
+    public void testProgress_cancel() throws Exception {
+        resetProperties();
+        sendBugreportStarted(1000);
+        waitForScreenshotButtonEnabled(true);
+
+        final NumberFormat nf = NumberFormat.getPercentInstance();
+        nf.setMinimumFractionDigits(2);
+        nf.setMaximumFractionDigits(2);
+
+        assertProgressNotification(NAME, nf.format(0));
+
+        openProgressNotification(ID);
+        UiObject cancelButton = mUiBot.getVisibleObject(mContext.getString(
+                com.android.internal.R.string.cancel).toUpperCase());
+        mUiBot.click(cancelButton, "cancel_button");
+
+        waitForService(false);
+    }
+
     public void testProgress_takeExtraScreenshot() throws Exception {
         takeExtraScreenshotTest(false);
     }
@@ -201,15 +234,15 @@
         assertScreenshotButtonEnabled(false);
         waitForScreenshotButtonEnabled(true);
 
-        sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
+        sendBugreportFinished(ID, mPlainTextPath, mScreenshotPath);
 
         if (serviceDies) {
-            waitShareNotification(PID);
+            waitShareNotification(ID);
             killService();
         }
 
-        Bundle extras = acceptBugreportAndGetSharedIntent(PID);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+        Bundle extras = acceptBugreportAndGetSharedIntent(ID);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
                 NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
@@ -227,8 +260,8 @@
         resetProperties();
 
         sendBugreportStarted(1000);
-        sendBugreportFinished(PID, mPlainTextPath, NO_SCREENSHOT);
-        waitShareNotification(PID);
+        sendBugreportFinished(ID, mPlainTextPath, NO_SCREENSHOT);
+        waitShareNotification(ID);
 
         // There's no indication in the UI about the screenshot finish, so just sleep like a baby...
         Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
@@ -237,8 +270,8 @@
             killService();
         }
 
-        Bundle extras = acceptBugreportAndGetSharedIntent(PID);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, PID, ZIP_FILE,
+        Bundle extras = acceptBugreportAndGetSharedIntent(ID);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID, PID, ZIP_FILE,
                 NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
@@ -249,11 +282,10 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
 
         // Check initial name.
-        String actualName = detailsUi.nameField.getText().toString();
-        assertEquals("Wrong value on field 'name'", NAME, actualName);
+        detailsUi.assertName(NAME);
 
         // Change name - it should have changed system property once focus is changed.
         detailsUi.nameField.setText(NEW_NAME);
@@ -281,9 +313,9 @@
         assertPropertyValue(NAME_PROPERTY, NEW_NAME);
         assertProgressNotification(NEW_NAME, "0.00%");
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath,
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
                 mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
                 NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
@@ -302,11 +334,10 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
 
         // Check initial name.
-        String actualName = detailsUi.nameField.getText().toString();
-        assertEquals("Wrong value on field 'name'", NAME, actualName);
+        detailsUi.assertName(NAME);
 
         // Change fields.
         detailsUi.reOpen();
@@ -319,33 +350,87 @@
         assertPropertyValue(NAME_PROPERTY, NEW_NAME);
         assertProgressNotification(NEW_NAME, "0.00%");
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID,
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
                 plainText? mPlainTextPath : mZipPath, mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
                 NEW_NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
 
-    public void testProgress_changeJustDetails() throws Exception {
+    public void testProgress_changeJustDetailsTouchingDetails() throws Exception {
+        changeJustDetailsTest(true);
+    }
+
+    public void testProgress_changeJustDetailsTouchingNotification() throws Exception {
+        changeJustDetailsTest(false);
+    }
+
+    private void changeJustDetailsTest(boolean touchDetails) throws Exception {
         resetProperties();
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, ID, touchDetails);
 
         detailsUi.nameField.setText("");
         detailsUi.titleField.setText("");
         detailsUi.descField.setText(mDescription);
         detailsUi.clickOk();
 
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mZipPath, mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, ZIP_FILE,
                 NO_NAME, NO_TITLE, mDescription, 1, DIDNT_RENAME_SCREENSHOTS);
 
         assertServiceNotRunning();
     }
 
+    /*
+     * TODO: this test can be flanky because it relies in the order the notifications are displayed,
+     * since mUiBot gets the first notification.
+     * Ideally, openProgressNotification() should return the whole notification, so DetailsUi
+     * could use it and find children instead, but unfortunately the notification object hierarchy
+     * is too complex and getting it from the notification text object would be to fragile
+     * (for instance, it could require navigating many parents up in the hierarchy).
+     */
+    public void testProgress_changeJustDetailsIsClearedOnSecondBugreport() throws Exception {
+        resetProperties();
+        sendBugreportStarted(ID, PID, NAME, 1000);
+        waitForScreenshotButtonEnabled(true);
+
+        DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
+        detailsUi.assertName(NAME);
+        detailsUi.assertTitle(mContext.getString(R.string.bugreport_info_title));
+        detailsUi.assertDescription(mContext.getString(R.string.bugreport_info_description));
+        detailsUi.nameField.setText(NEW_NAME);
+        detailsUi.titleField.setText(TITLE);
+        detailsUi.descField.setText(DESCRIPTION);
+        detailsUi.clickOk();
+
+        sendBugreportStarted(ID2, PID2, NAME2, 1000);
+
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
+                NEW_NAME, TITLE, DESCRIPTION, 1, RENAMED_SCREENSHOTS);
+
+        detailsUi = new DetailsUi(mUiBot, ID2);
+        detailsUi.assertName(NAME2);
+        detailsUi.assertTitle(mContext.getString(R.string.bugreport_info_title));
+        detailsUi.assertDescription(mContext.getString(R.string.bugreport_info_description));
+        detailsUi.nameField.setText(NEW_NAME2);
+        detailsUi.titleField.setText(TITLE2);
+        detailsUi.descField.setText(DESCRIPTION2);
+        detailsUi.clickOk();
+
+        // Must use a different zip file otherwise it will fail because zip already contains
+        // title.txt and description.txt entries.
+        extras = sendBugreportFinishedAndGetSharedIntent(ID2, mZipPath2, NO_SCREENSHOT);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, ID2, PID2, TITLE2,
+                NEW_NAME2, TITLE2, DESCRIPTION2, 1, RENAMED_SCREENSHOTS);
+
+        assertServiceNotRunning();
+    }
+
     /**
      * Tests the scenario where the initial screenshot and dumpstate are finished while the user
      * is changing the info in the details screen.
@@ -369,14 +454,14 @@
             waitForScreenshotButtonEnabled(true);
         }
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, ID);
 
         // Finish the bugreport while user's still typing the name.
         detailsUi.nameField.setText(NEW_NAME);
-        sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
+        sendBugreportFinished(ID, mPlainTextPath, mScreenshotPath);
 
         // Wait until the share notification is received...
-        waitShareNotification(PID);
+        waitShareNotification(ID);
         // ...then close notification bar.
         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
 
@@ -390,8 +475,8 @@
         detailsUi.clickOk();
 
         // Finally, share bugreport.
-        Bundle extras = acceptBugreportAndGetSharedIntent(PID);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+        Bundle extras = acceptBugreportAndGetSharedIntent(ID);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
                 NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
 
         assertServiceNotRunning();
@@ -402,8 +487,8 @@
         BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
 
         // Send notification and click on share.
-        sendBugreportFinished(NO_PID, mPlainTextPath, null);
-        acceptBugreport(NO_PID);
+        sendBugreportFinished(NO_ID, mPlainTextPath, null);
+        acceptBugreport(NO_ID);
 
         // Handle the warning
         mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
@@ -425,9 +510,9 @@
     }
 
     public void testShareBugreportAfterServiceDies() throws Exception {
-        sendBugreportFinished(NO_PID, mPlainTextPath, NO_SCREENSHOT);
+        sendBugreportFinished(NO_ID, mPlainTextPath, NO_SCREENSHOT);
         killService();
-        Bundle extras = acceptBugreportAndGetSharedIntent(NO_PID);
+        Bundle extras = acceptBugreportAndGetSharedIntent(NO_ID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
     }
 
@@ -463,32 +548,38 @@
     private void assertProgressNotification(String name, String percent) {
         // TODO: it currently looks for 3 distinct objects, without taking advantage of their
         // relationship.
-        openProgressNotification(PID);
+        openProgressNotification(ID);
         Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
         mUiBot.getObject(name);
         mUiBot.getObject(percent);
     }
 
-    private void openProgressNotification(int pid) {
-        String title = mContext.getString(R.string.bugreport_in_progress_title, pid);
+    private UiObject openProgressNotification(int id) {
+        String title = mContext.getString(R.string.bugreport_in_progress_title, id);
         Log.v(TAG, "Looking for progress notification title: '" + title + "'");
-        mUiBot.getNotification(title);
+        return mUiBot.getNotification(title);
     }
 
     void resetProperties() {
         // TODO: call method to remove property instead
-        SystemProperties.set(PROGRESS_PROPERTY, "0");
-        SystemProperties.set(MAX_PROPERTY, "0");
+        SystemProperties.set(PROGRESS_PROPERTY, "Reset");
+        SystemProperties.set(MAX_PROPERTY, "Reset");
+        SystemProperties.set(NAME_PROPERTY, "Reset");
     }
 
     /**
      * Sends a "bugreport started" intent with the default values.
      */
     private void sendBugreportStarted(int max) throws Exception {
+        sendBugreportStarted(ID, PID, NAME, max);
+    }
+
+    private void sendBugreportStarted(int id, int pid, String name, int max) throws Exception {
         Intent intent = new Intent(INTENT_BUGREPORT_STARTED);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        intent.putExtra(EXTRA_PID, PID);
-        intent.putExtra(EXTRA_NAME, NAME);
+        intent.putExtra(EXTRA_ID, id);
+        intent.putExtra(EXTRA_PID, pid);
+        intent.putExtra(EXTRA_NAME, name);
         intent.putExtra(EXTRA_MAX, max);
         mContext.sendBroadcast(intent);
     }
@@ -500,7 +591,7 @@
      */
     private Bundle sendBugreportFinishedAndGetSharedIntent(String bugreportPath,
             String screenshotPath) {
-        return sendBugreportFinishedAndGetSharedIntent(NO_PID, bugreportPath, screenshotPath);
+        return sendBugreportFinishedAndGetSharedIntent(NO_ID, bugreportPath, screenshotPath);
     }
 
     /**
@@ -508,10 +599,10 @@
      *
      * @return extras sent in the shared intent.
      */
-    private Bundle sendBugreportFinishedAndGetSharedIntent(int pid, String bugreportPath,
+    private Bundle sendBugreportFinishedAndGetSharedIntent(int id, String bugreportPath,
             String screenshotPath) {
-        sendBugreportFinished(pid, bugreportPath, screenshotPath);
-        return acceptBugreportAndGetSharedIntent(pid);
+        sendBugreportFinished(id, bugreportPath, screenshotPath);
+        return acceptBugreportAndGetSharedIntent(id);
     }
 
     /**
@@ -519,8 +610,8 @@
      *
      * @return extras sent in the shared intent.
      */
-    private Bundle acceptBugreportAndGetSharedIntent(int pid) {
-        acceptBugreport(pid);
+    private Bundle acceptBugreportAndGetSharedIntent(int id) {
+        acceptBugreport(id);
         mUiBot.chooseActivity(UI_NAME);
         return mListener.getExtras();
     }
@@ -528,25 +619,25 @@
     /**
      * Waits for the notification to share the finished bugreport.
      */
-    private void waitShareNotification(int pid) {
-        mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, pid));
+    private void waitShareNotification(int id) {
+        mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, id));
     }
 
     /**
      * Accepts the notification to share the finished bugreport.
      */
-    private void acceptBugreport(int pid) {
-        mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, pid));
+    private void acceptBugreport(int id) {
+        mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, id));
     }
 
     /**
      * Sends a "bugreport finished" intent.
      */
-    private void sendBugreportFinished(int pid, String bugreportPath, String screenshotPath) {
+    private void sendBugreportFinished(int id, String bugreportPath, String screenshotPath) {
         Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        if (pid != NO_PID) {
-            intent.putExtra(EXTRA_PID, pid);
+        if (id != NO_ID) {
+            intent.putExtra(EXTRA_ID, id);
         }
         if (bugreportPath != null) {
             intent.putExtra(EXTRA_BUGREPORT, bugreportPath);
@@ -563,7 +654,7 @@
      */
     private void assertActionSendMultiple(Bundle extras, String bugreportContent,
             String screenshotContent) throws IOException {
-        assertActionSendMultiple(extras, bugreportContent, screenshotContent, PID, ZIP_FILE,
+        assertActionSendMultiple(extras, bugreportContent, screenshotContent, ID, PID, ZIP_FILE,
                 NO_NAME, NO_TITLE, NO_DESCRIPTION, 0, DIDNT_RENAME_SCREENSHOTS);
     }
 
@@ -573,6 +664,7 @@
      * @param extras extras received in the intent
      * @param bugreportContent expected content in the bugreport file
      * @param screenshotContent expected content in the screenshot file (sent by dumpstate), if any
+     * @param id emulated dumpstate id
      * @param pid emulated dumpstate pid
      * @param name expected subject
      * @param name bugreport name as provided by the user (or received by dumpstate)
@@ -582,7 +674,7 @@
      * @param renamedScreenshots whether the screenshots are expected to be renamed
      */
     private void assertActionSendMultiple(Bundle extras, String bugreportContent,
-            String screenshotContent, int pid, String subject,
+            String screenshotContent, int id, int pid, String subject,
             String name, String title, String description,
             int numberScreenshots, boolean renamedScreenshots) throws IOException {
         String body = extras.getString(Intent.EXTRA_TEXT);
@@ -745,7 +837,7 @@
         fail("Service status didn't change to " + expectRunning);
     }
 
-    private static void createTextFile(String path, String content) throws IOException {
+    private void createTextFile(String path, String content) throws IOException {
         Log.v(TAG, "createFile(" + path + ")");
         try (Writer writer = new BufferedWriter(new OutputStreamWriter(
                 new FileOutputStream(path)))) {
@@ -781,7 +873,7 @@
      * Gets the notification button used to take a screenshot.
      */
     private UiObject getScreenshotButton() {
-        openProgressNotification(PID);
+        openProgressNotification(ID);
         return mUiBot.getVisibleObject(
                 mContext.getString(R.string.bugreport_screenshot_action).toUpperCase());
     }
@@ -833,15 +925,28 @@
         /**
          * Gets the UI objects by opening the progress notification and clicking DETAILS.
          */
-        DetailsUi(UiBot uiBot, int pid) throws UiObjectNotFoundException {
-            openProgressNotification(pid);
-            detailsButton = mUiBot.getVisibleObject(
-                    mContext.getString(R.string.bugreport_info_action).toUpperCase());
-            mUiBot.click(detailsButton, "details_button");
+        DetailsUi(UiBot uiBot, int id) throws UiObjectNotFoundException {
+            this(uiBot, id, true);
+        }
+
+        /**
+         * Gets the UI objects by opening the progress notification and clicking on DETAILS or in
+         * the notification itself.
+         */
+        DetailsUi(UiBot uiBot, int id, boolean clickDetails) throws UiObjectNotFoundException {
+            UiObject notification = openProgressNotification(id);
+            detailsButton = mUiBot.getVisibleObject(mContext.getString(
+                    R.string.bugreport_info_action).toUpperCase());
+
+            if (clickDetails) {
+                mUiBot.click(detailsButton, "details_button");
+            } else {
+                mUiBot.click(notification, "notification");
+            }
             // TODO: unhardcode resource ids
             UiObject dialogTitle = mUiBot.getVisibleObjectById("android:id/alertTitle");
             assertEquals("Wrong title", mContext.getString(R.string.bugreport_info_dialog_title,
-                    pid), dialogTitle.getText().toString());
+                    id), dialogTitle.getText().toString());
             nameField = mUiBot.getVisibleObjectById("com.android.shell:id/name");
             titleField = mUiBot.getVisibleObjectById("com.android.shell:id/title");
             descField = mUiBot.getVisibleObjectById("com.android.shell:id/description");
@@ -849,6 +954,28 @@
             cancelButton = mUiBot.getObjectById("android:id/button2");
         }
 
+        private void assertField(String name, UiObject field, String expected) {
+            try {
+                String actual = field.getText().toString();
+                assertEquals("Wrong value on field '" + name + "'", expected, actual);
+            } catch (UiObjectNotFoundException e) {
+                // Should not happen...
+                throw new IllegalStateException("field not found: " + name, e);
+            }
+        }
+
+        void assertName(String expected) {
+            assertField("name", nameField, expected);
+        }
+
+        void assertTitle(String expected) {
+            assertField("title", titleField, expected);
+        }
+
+        void assertDescription(String expected) {
+            assertField("description", descField, expected);
+        }
+
         /**
          * Takes focus away from the name field so it can be validated.
          */
@@ -858,7 +985,7 @@
         }
 
         void reOpen() {
-            openProgressNotification(PID);
+            openProgressNotification(ID);
             mUiBot.click(detailsButton, "details_button");
 
         }
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 88313bb..ad3c26b 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -1,10 +1,24 @@
 LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := SystemUI-proto-tags
+
+LOCAL_SRC_FILES := $(call all-proto-files-under,src) \
+    src/com/android/systemui/EventLogTags.logtags
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# ------------------
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) $(call all-Iaidl-files-under, src) \
-    src/com/android/systemui/EventLogTags.logtags
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     Keyguard \
@@ -13,13 +27,12 @@
     android-support-v7-appcompat \
     android-support-v14-preference \
     android-support-v17-leanback \
-    framework-protos
+    framework-protos \
+    SystemUI-proto-tags
 
 LOCAL_JAVA_LIBRARIES := telephony-common
 
 LOCAL_PACKAGE_NAME := SystemUI
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
 LOCAL_CERTIFICATE := platform
 LOCAL_PRIVILEGED_MODULE := true
 
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
new file mode 100644
index 0000000..5cabb77
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <size
+        android:width="36dp"
+        android:height="36dp" />
+    <solid
+        android:color="#4DFFFFFF" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_close_button.xml b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
new file mode 100644
index 0000000..86fda0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+    android:constantSize="true">
+    <item android:state_focused="true">
+        <layer-list>
+            <item android:drawable="@drawable/tv_pip_button_focused" />
+            <item android:drawable="@drawable/ic_close_white" />
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_close_white" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_full_button.xml b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
new file mode 100644
index 0000000..332c669
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+    android:constantSize="true">
+    <item android:state_focused="true">
+        <layer-list>
+            <item android:drawable="@drawable/tv_pip_button_focused" />
+            <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_outline.xml b/packages/SystemUI/res/drawable/tv_pip_outline.xml
new file mode 100644
index 0000000..c84438c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_outline.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <stroke android:width="2dp" android:color="#EEEEEE" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
new file mode 100644
index 0000000..d277b07
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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"
+    android:constantSize="true">
+    <item android:state_focused="true">
+        <layer-list>
+            <item android:drawable="@drawable/tv_pip_button_focused" />
+            <item android:drawable="@drawable/ic_pause_white_24dp" />
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_pause_white_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
index 7ea5027..cfaf018 100644
--- a/packages/SystemUI/res/layout/docked_stack_divider.xml
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -27,6 +27,7 @@
     <com.android.systemui.stackdivider.DividerHandleView
         style="@style/DockedDividerHandle"
         android:id="@+id/docked_divider_handle"
+        android:contentDescription="@string/accessibility_divider"
         android:background="@null"/>
 
 </com.android.systemui.stackdivider.DividerView>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 4d0eb96..62f2b47 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -26,7 +26,7 @@
         android:orientation="vertical"
         android:paddingStart="@*android:dimen/notification_content_margin_start"
         android:paddingEnd="8dp"
-        android:background="@color/notification_guts_bg_color" >
+        android:background="@color/notification_guts_bg_color">
 
     <!-- header -->
     <LinearLayout
@@ -58,8 +58,38 @@
                 android:layout_gravity="bottom|start"
                 android:visibility="gone" />
     </LinearLayout>
+    <!-- Importance radio buttons -->
+    <RadioGroup
+            android:id="@+id/importance_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dip"
+            android:paddingEnd="8dp" >
+        <RadioButton
+                android:id="@+id/silent_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:text="@string/show_silently"
+                style="@style/TextAppearance.NotificationGuts.Primary"
+                android:buttonTint="#858383" />
+        <RadioButton
+                android:id="@+id/block_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:text="@string/block"
+                style="@style/TextAppearance.NotificationGuts.Primary"
+                android:buttonTint="#858383" />
+        <RadioButton
+                android:id="@+id/reset_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                style="@style/TextAppearance.NotificationGuts.Primary"
+                android:buttonTint="#858383" />
+    </RadioGroup>
     <!-- Importance slider -->
     <LinearLayout
+            android:id="@+id/importance_slider"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center_vertical"
@@ -67,7 +97,8 @@
             android:clickable="false"
             android:focusable="false"
             android:paddingBottom="8dip"
-            android:paddingEnd="8dp" >
+            android:paddingEnd="8dp"
+            android:visibility="gone">
         <TextView
                 android:id="@+id/title"
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 458ce2d..582ad48 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -34,7 +34,9 @@
         android:id="@android:id/list"
         android:layout_width="@dimen/notification_panel_width"
         android:layout_height="0dp"
-        android:layout_weight="1" />
+        android:layout_weight="1"
+        android:scrollIndicators="top"
+        android:scrollbars="vertical" />
 
     <View
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 3ee2475..994d3c9 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -28,9 +28,7 @@
             android:layout_marginTop="@dimen/status_bar_header_height"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingBottom="8dp"
-            android:clipToPadding="false"
-            android:clipChildren="false" />
+            android:paddingBottom="8dp" />
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 2e41561..fa65758 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -30,21 +30,41 @@
         android:paddingBottom="8dp"
         android:paddingStart="12dp"
         android:paddingEnd="16dp" />
-    <TextView
-        android:id="@+id/title"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
         android:layout_marginStart="56dp"
-        android:layout_marginEnd="112dp"
-        android:textSize="16sp"
-        android:textColor="#ffffffff"
-        android:text="@string/recents_empty_message"
-        android:fontFamily="sans-serif-medium"
-        android:singleLine="true"
-        android:maxLines="2"
-        android:ellipsize="marquee"
-        android:fadingEdge="horizontal" />
+        android:layout_marginEnd="56dp"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textSize="16sp"
+            android:textColor="#ffffffff"
+            android:text="@string/recents_empty_message"
+            android:fontFamily="sans-serif-medium"
+            android:singleLine="true"
+            android:maxLines="1"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+        <TextView
+            android:id="@+id/sub_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textSize="11sp"
+            android:textColor="#ffffffff"
+            android:text="@string/recents_launch_non_dockable_task_label"
+            android:fontFamily="sans-serif-medium"
+            android:singleLine="true"
+            android:maxLines="1"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:visibility="gone" />
+    </LinearLayout>
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/move_task"
         android:layout_width="@dimen/recents_task_view_header_button_width"
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 3562c64..0b98d0e 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -18,36 +18,94 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_gravity="end"
-    android:paddingStart="10dp"
-    android:paddingEnd="10dp"
-    android:background="#88FFFFFF"
-    android:gravity="center_vertical" >
+    android:orientation="horizontal"
+    android:paddingTop="350dp"
+    android:background="#CC000000"
+    android:gravity="top|center_horizontal"
+    android:clipChildren="false">
 
-    <Button android:id="@+id/full"
-        android:layout_width="match_parent"
+    <LinearLayout
+        android:layout_width="34dp"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:text="@string/pip_fullscreen"
-        android:textSize="10sp"
-        android:focusable="true" />
+        android:layout_marginEnd="3dp"
+        android:orientation="vertical"
+        android:gravity="center"
+        android:clipChildren="false">
 
-    <Button android:id="@+id/exit"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:text="@string/pip_exit"
-        android:textSize="10sp"
-        android:focusable="true" />
+        <ImageView android:id="@+id/full"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:src="@drawable/tv_pip_full_button" />
 
-    <Button android:id="@+id/cancel"
-        android:layout_width="match_parent"
+        <TextView android:id="@+id/full_desc"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:gravity="center"
+            android:visibility="invisible"
+            android:text="@string/pip_fullscreen"
+            android:fontFamily="sans-serif"
+            android:textSize="12sp"
+            android:textColor="#EEEEEE"
+            android:clipChildren="false" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="34dp"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:text="@string/pip_cancel"
-        android:textSize="10sp"
-        android:focusable="true" />
+        android:layout_marginStart="3dp"
+        android:layout_marginEnd="3dp"
+        android:orientation="vertical"
+        android:gravity="center"
+        android:visibility="gone"
+        android:clipChildren="false">
+
+        <ImageView android:id="@+id/play_pause"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:src="@drawable/tv_pip_pause_button" />
+
+        <TextView android:id="@+id/play_pause_desc"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:gravity="center"
+            android:visibility="invisible"
+            android:text="@string/pip_pause"
+            android:fontFamily="sans-serif"
+            android:textSize="12sp"
+            android:textColor="#EEEEEE"
+            android:clipChildren="false" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="34dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="3dp"
+        android:orientation="vertical"
+        android:gravity="center"
+        android:clipChildren="false">
+
+        <ImageView android:id="@+id/close"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:src="@drawable/tv_pip_close_button" />
+
+        <TextView android:id="@+id/close_desc"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:gravity="center"
+            android:visibility="invisible"
+            android:text="@string/pip_close"
+            android:fontFamily="sans-serif"
+            android:textSize="12sp"
+            android:textColor="#EEEEEE"
+            android:clipChildren="false" />
+    </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_onboarding.xml b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
index ef39555..f031bb4 100644
--- a/packages/SystemUI/res/layout/tv_pip_onboarding.xml
+++ b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
@@ -18,34 +18,43 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pip_onboarding"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="#C00288D1"
-    android:gravity="center"
-    android:orientation="vertical" >
+    android:gravity="top|center_horizontal"
+    android:orientation="vertical">
 
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="30sp"
-        android:textColor="@android:color/white"
-        android:text="@string/pip_onboarding_title" />
+    <!-- A rectangle arounds the PIP.
+         Size and positions will be programatically set up
+         to comply with config_defaultPictureInPictureBounds. -->
     <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/ic_sysbar_home" />
+        android:id="@+id/pip_outline"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:src="@drawable/tv_pip_outline" />
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="30dp"
-        android:textSize="13sp"
-        android:textColor="@android:color/white"
+        android:layout_marginTop="24dp"
+        android:fontFamily="sans-serif"
+        android:textSize="16sp"
+        android:textColor="#EEEEEE"
+        android:lineSpacingMultiplier="1.28"
         android:text="@string/pip_onboarding_description" />
     <Button
         android:id="@+id/close"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="15sp"
+        android:layout_height="36dp"
+        android:layout_marginTop="24dp"
+        android:gravity="center"
+        android:paddingStart="24dp"
+        android:paddingEnd="24dp"
+        android:fontFamily="sans-serif-condensed"
+        android:textSize="16sp"
+        android:textColor="#026089"
         android:textAllCaps="true"
-        android:text="@string/pip_onboarding_button" />
+        android:text="@string/pip_onboarding_button"
+        android:background="#EEEEEE"
+        android:elevation="4dp" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 4d7d6b5..791d761 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
-        <item name="android:layout_width">480dp</item>
+        <item name="android:layout_width">@dimen/standard_notification_panel_width</item>
     </style>
 
     <style name="UserDetailView">
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 77605bd..bf32cc7 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -31,4 +31,7 @@
     <!-- Values for focus animation -->
     <dimen name="recents_tv_unselected_item_z">6dp</dimen>
     <dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
-</resources>
\ No newline at end of file
+
+    <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
+    <dimen name="tv_pip_bounds_space">3dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 87aedab..b9eee2e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -44,6 +44,10 @@
     <item type="id" name="notification_screenshot"/>
     <item type="id" name="notification_hidden"/>
     <item type="id" name="notification_volumeui"/>
+    <item type="id" name="transformation_start_x_tag"/>
+    <item type="id" name="transformation_start_y_tag"/>
+    <item type="id" name="transformation_start_scale_x_tag"/>
+    <item type="id" name="transformation_start_scale_y_tag"/>
 
     <!-- Whether the icon is from a notification for which targetSdk < L -->
     <item type="id" name="icon_is_pre_L"/>
@@ -56,5 +60,11 @@
     <item type="id" name="image_icon_tag" />
     <item type="id" name="contains_transformed_view" />
     <item type="id" name="is_clicked_heads_up_tag" />
+
+    <!-- Accessibility actions for the docked stack divider -->
+    <item type="id" name="action_move_left" />
+    <item type="id" name="action_move_right" />
+    <item type="id" name="action_move_up" />
+    <item type="id" name="action_move_down" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6f6e515..1f239c3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -497,6 +497,8 @@
     <string name="accessibility_quick_settings_less_time">Less time.</string>
     <!-- Content description of the flashlight tile in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_flashlight_off">Flashlight off.</string>
+    <!-- Content description of the flashlight tile in quick settings when unavailable (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_flashlight_unavailable">Flashlight unavailable.</string>
     <!-- Content description of the flashlight tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_flashlight_on">Flashlight on.</string>
     <!-- Announcement made when the flashlight state changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -722,6 +724,10 @@
     <string name="recents_history_button_label">History</string>
     <!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
     <string name="recents_history_clear_all_button_label">Clear</string>
+    <!-- Recents: Non-dockable task drag message. [CHAR LIMIT=NONE] -->
+    <string name="recents_drag_non_dockable_task_message">This app does not support multi-window</string>
+    <!-- Recents: Non-dockable task launch sub header. [CHAR LIMIT=NONE] -->
+    <string name="recents_launch_non_dockable_task_label">App does not support multi-window</string>
 
     <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
     <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1201,12 +1207,22 @@
     <!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
     <string name="enable_bluetooth_confirmation_ok">Turn on</string>
 
-    <!-- Apply notification importance setting to a topic [CHAR LIMIT=NONE] -->
-    <string name="apply_to_topic">Apply to <xliff:g id="topic_name" example="Friend Request">%1$s</xliff:g> notifications</string>
-    <!-- Apply notification importance setting to an app [CHAR LIMIT=NONE] -->
-    <string name="apply_to_app">Apply to all notifications from this app</string>
+    <!-- [CHAR LIMIT=100] Notification importance option -->
+    <string name="show_silently">Show notifications silently</string>
+    <!-- [CHAR LIMIT=100] Notification importance option -->
+    <string name="block">Block all notifications</string>
+    <!-- [CHAR LIMIT=100] Notification importance option -->
+    <string name="do_not_silence">Don\'t silence</string>
+    <!-- [CHAR LIMIT=100] Notification importance option -->
+    <string name="do_not_silence_block">Don\'t silence or block</string>
+
+    <!-- [CHAR LIMIT=NONE] Importance Tuner setting title -->
+    <string name="tuner_full_importance_settings">Show full importance settings</string>
+
     <!-- Notification importance title, blocked status-->
     <string name="blocked_importance">Blocked</string>
+    <!-- Notification importance title, min status-->
+    <string name="min_importance">Min importance</string>
     <!-- Notification importance title, low status-->
     <string name="low_importance">Low importance</string>
     <!-- Notification importance title, normal status-->
@@ -1219,17 +1235,20 @@
     <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
     <string name="notification_importance_blocked">Never show these notifications</string>
 
+    <!-- [CHAR LIMIT=100] Notification Importance slider: min importance level description -->
+    <string name="notification_importance_min">Silently show at the bottom of the notification list</string>
+
     <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
-    <string name="notification_importance_low">Silently show at the bottom of the notification list</string>
+    <string name="notification_importance_low">Silently show these notifications</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
-    <string name="notification_importance_default">Silently show these notifications</string>
+    <string name="notification_importance_default">Allow these notification to make sounds</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
-    <string name="notification_importance_high">Show at the top of the notifications list and make sound</string>
+    <string name="notification_importance_high">Peek onto the screen and allow sound and allow sound</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
-    <string name="notification_importance_max">Peek onto the screen and make sound</string>
+    <string name="notification_importance_max">Show at the top of the notifications list, peek onto the screen and allow sound</string>
 
     <!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] -->
     <string name="notification_more_settings">More settings</string>
@@ -1430,4 +1449,22 @@
         <item>Don\'t show this icon</item>
     </string-array>
 
+    <!-- SysUI Tuner: Other section -->
+    <string name="other">Other</string>
+
+    <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] -->
+    <string name="accessibility_divider">Split-screen divider</string>
+
+    <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_move_down">Move down</string>
+
+    <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_move_up">Move up</string>
+
+    <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_move_left">Move left</string>
+
+    <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_move_right">Move right</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 7c4768d..4f382ea 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -20,7 +20,7 @@
     <!-- Picture-in-Picture menu -->
     <eat-comment />
     <!-- Button to close PIP on PIP UI -->
-    <string name="pip_exit" translatable="false">Close PIP</string>
+    <string name="pip_close" translatable="false">Close PIP</string>
     <!-- Button to move PIP screen to the fullscreen on PIP UI -->
     <string name="pip_fullscreen" translatable="false">Full screen</string>
     <!-- Button to play the current media on PIP UI -->
@@ -34,8 +34,6 @@
 
     <!-- Picture-in-Picture onboarding screen -->
     <eat-comment />
-    <!-- Title for onboarding screen. -->
-    <string name="pip_onboarding_title" translatable="false">Picture-in-picture</string>
     <!-- Description for onboarding screen. -->
     <string name="pip_onboarding_description" translatable="false">Press and hold the HOME\nbutton to close or control it</string>
     <!-- Button to close onboarding screen. -->
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index f4a0cc9..ddc03a3 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -134,8 +134,8 @@
     -->
 
     <PreferenceScreen
-        android:key="overview"
-        android:title="@string/overview" >
+        android:key="other"
+        android:title="@string/other" >
 
         <com.android.systemui.tuner.TunerSwitch
             android:key="overview_disable_fast_toggle_via_button"
@@ -147,6 +147,11 @@
             android:title="@string/overview_nav_bar_gesture"
             android:summary="@string/overview_nav_bar_gesture_desc" />
 
+        <!-- importance -->
+        <com.android.systemui.tuner.TunerSwitch
+                android:key="show_importance_slider"
+                android:title="@string/tuner_full_importance_settings" />
+
     </PreferenceScreen>
 
     <!-- Warning, this goes last. -->
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 1471622..9f2745b 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -48,6 +48,7 @@
         Key.QS_DATA_SAVER_ADDED,
         Key.QS_INVERT_COLORS_ADDED,
         Key.QS_WORK_ADDED,
+        Key.QS_NIGHT_ADDED,
     })
     public @interface Key {
         String OVERVIEW_SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
@@ -68,6 +69,7 @@
         String QS_DATA_SAVER_ADDED = "QsDataSaverAdded";
         String QS_INVERT_COLORS_ADDED = "QsInvertColorsAdded";
         String QS_WORK_ADDED = "QsWorkAdded";
+        String QS_NIGHT_ADDED = "QsNightAdded";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 0915ee1..2c5cb89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -38,7 +38,7 @@
                 if (mPageIndicator == null) return;
                 mPageIndicator.setLocation(position);
                 if (mPageListener != null) {
-                    mPageListener.onPageChanged(position);
+                    mPageListener.onPageChanged(position == 0);
                 }
             }
 
@@ -47,6 +47,9 @@
                     int positionOffsetPixels) {
                 if (mPageIndicator == null) return;
                 mPageIndicator.setLocation(position + positionOffset);
+                if (mPageListener != null) {
+                    mPageListener.onPageChanged(position == 0 && positionOffsetPixels == 0);
+                }
             }
 
             @Override
@@ -57,6 +60,11 @@
     }
 
     @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
@@ -209,6 +217,6 @@
     };
 
     public interface PageListener {
-        void onPageChanged(int page);
+        void onPageChanged(boolean isFirst);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index dc64591..c31bb33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -69,11 +69,13 @@
     }
 
     @Override
-    public void onPageChanged(int page) {
-        mOnFirstPage = page == 0;
-        if (!mOnFirstPage) {
+    public void onPageChanged(boolean isFirst) {
+        if (mOnFirstPage == isFirst) return;
+        if (!isFirst) {
+            setPosition(1);
             clearAnimationState();
         }
+        mOnFirstPage = isFirst;
     }
 
     private void updateAnimators() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index abe4c77..4408dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -183,5 +183,10 @@
             // No resources here.
             return false;
         }
+
+        @Override
+        public boolean hasOverlappingRendering() {
+            return false;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 94d8524..026dd0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -58,7 +58,7 @@
         }
         if (mListener != null) {
             if (mLastT == 0 || mLastT == 1) {
-                if (t != 0) {
+                if (t != mLastT) {
                     mListener.onAnimationStarted();
                 }
             } else if (t == 1) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 876f417..c6c497f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -107,7 +107,10 @@
         mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
         mTiles.clear();
         for (int i = 0; i < mCurrentSpecs.size(); i++) {
-            mTiles.add(getAndRemoveOther(mCurrentSpecs.get(i)));
+            final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));
+            if (tile != null) {
+                mTiles.add(tile);
+            }
         }
         mTiles.add(null);
         mTiles.addAll(mOtherTiles);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index c3610d9..aa85f78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -28,11 +28,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.service.quicksettings.TileService;
+import com.android.systemui.Prefs;
+import com.android.systemui.Prefs.Key;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTile.DrawableIcon;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.TunerService;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -54,7 +57,8 @@
     }
 
     private void addSystemTiles(QSTileHost host) {
-        boolean hasColorMod = host.getNightModeController().isEnabled();
+        boolean hasColorMod = Prefs.getBoolean(host.getContext(), Key.QS_NIGHT_ADDED, false)
+                && TunerService.isTunerEnabled(host.getContext());
         String possible = mContext.getString(R.string.quick_settings_tiles_default)
                 + ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",night" : "");
         String[] possibleTiles = possible.split(",");
@@ -88,7 +92,12 @@
         qsHandler.post(new Runnable() {
             @Override
             public void run() {
-                new QueryTilesTask().execute();
+                mainHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        new QueryTilesTask().execute();
+                    }
+                });
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index c10843a..e4e3790 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,9 +17,11 @@
 package com.android.systemui.qs.tiles;
 
 import android.app.ActivityManager;
-
 import android.content.Intent;
+import android.graphics.drawable.Drawable;
 import android.provider.MediaStore;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
@@ -79,9 +81,19 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        // TODO: Flashlight available handling...
-//        state.visible = mFlashlightController.isAvailable();
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
+        if (!mFlashlightController.isAvailable()) {
+            Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable);
+            final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+            icon.setTint(disabledColor);
+            state.icon = new DrawableIcon(icon);
+            state.label = new SpannableStringBuilder().append(state.label,
+                    new ForegroundColorSpan(disabledColor),
+                    SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
+            state.contentDescription = mContext.getString(
+                    R.string.accessibility_quick_settings_flashlight_unavailable);
+            return;
+        }
         if (arg instanceof Boolean) {
             boolean value = (Boolean) arg;
             if (value == state.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c2a6108..c41098f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -528,17 +528,16 @@
     @Override
     public void onMultiWindowChanged(boolean inMultiWindow) {
         super.onMultiWindowChanged(inMultiWindow);
-        if (!inMultiWindow) {
-            RecentsTaskLoader loader = Recents.getTaskLoader();
-            RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-            launchOpts.loadIcons = false;
-            launchOpts.loadThumbnails = false;
-            launchOpts.onlyLoadForCache = true;
-            RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
-            loader.preloadTasks(loadPlan, -1, false);
-            loader.loadTasks(this, loadPlan, launchOpts);
-            EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack()));
-        }
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.loadIcons = false;
+        launchOpts.loadThumbnails = false;
+        launchOpts.onlyLoadForCache = true;
+        RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+        loader.preloadTasks(loadPlan, -1, false);
+        loader.loadTasks(this, loadPlan, launchOpts);
+        EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack(),
+                inMultiWindow));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8de964b..28b2fae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -161,10 +161,8 @@
     Handler mHandler;
     TaskStackListenerImpl mTaskStackListener;
     RecentsAppWidgetHost mAppWidgetHost;
-    boolean mBootCompleted;
     boolean mCanReuseTaskStackViews = true;
     boolean mDraggingInRecents;
-    boolean mReloadTasks;
     boolean mLaunchedWhileDocking;
 
     // Task launching
@@ -236,7 +234,6 @@
     }
 
     public void onBootCompleted() {
-        mBootCompleted = true;
         updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
     }
 
@@ -317,23 +314,21 @@
     }
 
     public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        if (mBootCompleted) {
-            if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
-                // The user has released alt-tab before the trigger has run, so just show the next
-                // task immediately
-                showNextTask();
+        if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
+            // The user has released alt-tab before the trigger has run, so just show the next
+            // task immediately
+            showNextTask();
 
-                // Cancel the fast alt-tab trigger
-                mFastAltTabTrigger.stopDozing();
-                mFastAltTabTrigger.resetTrigger();
-                return;
-            }
-
-            // Defer to the activity to handle hiding recents, if it handles it, then it must still
-            // be visible
-            EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
-                    triggeredFromHomeKey));
+            // Cancel the fast alt-tab trigger
+            mFastAltTabTrigger.stopDozing();
+            mFastAltTabTrigger.resetTrigger();
+            return;
         }
+
+        // Defer to the activity to handle hiding recents, if it handles it, then it must still
+        // be visible
+        EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
+                triggeredFromHomeKey));
     }
 
     public void toggleRecents() {
@@ -347,7 +342,6 @@
         mTriggeredFromAltTab = false;
 
         try {
-            ViewConfiguration viewConfig = ViewConfiguration.get(mContext);
             SystemServicesProxy ssp = Recents.getSystemServices();
             ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
             MutableBoolean isTopTaskHome = new MutableBoolean(true);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
index f87f6de..0d614e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
@@ -28,8 +28,10 @@
      * A new TaskStack instance representing the latest stack state.
      */
     public final TaskStack stack;
+    public final boolean inMultiWindow;
 
-    public TaskStackUpdatedEvent(TaskStack stack) {
+    public TaskStackUpdatedEvent(TaskStack stack, boolean inMultiWindow) {
         this.stack = stack;
+        this.inMultiWindow = inMultiWindow;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 824231a..6fef8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -198,8 +198,8 @@
             // Add the task to the stack
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                     thumbnail, title, contentDescription, dismissDescription, activityColor,
-                    backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.bounds,
-                    t.taskDescription);
+                    backgroundColor, !isStackTask, isLaunchTarget, isSystemApp, t.isDockable,
+                    t.bounds, t.taskDescription);
 
             allTasks.add(task);
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 8ed6dd7..e5d4f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -168,6 +168,8 @@
     public boolean isHistorical;
     @ViewDebug.ExportedProperty(category="recents")
     public boolean isSystemApp;
+    @ViewDebug.ExportedProperty(category="recents")
+    public boolean isDockable;
 
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
@@ -178,8 +180,8 @@
     public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
                 Bitmap thumbnail, String title, String contentDescription,
                 String dismissDescription, int colorPrimary, int colorBackground,
-                boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp, Rect bounds,
-                ActivityManager.TaskDescription taskDescription) {
+                boolean isHistorical, boolean isLaunchTarget, boolean isSystemApp,
+                boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription) {
         boolean isInAffiliationGroup = (affiliationTaskId != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
         this.key = key;
@@ -199,6 +201,7 @@
         this.isLaunchTarget = isLaunchTarget;
         this.isHistorical = isHistorical;
         this.isSystemApp = isSystemApp;
+        this.isDockable = isDockable;
     }
 
     /** Copies the other task. */
@@ -219,6 +222,7 @@
         this.isLaunchTarget = o.isLaunchTarget;
         this.isHistorical = o.isHistorical;
         this.isSystemApp = o.isSystemApp;
+        this.isDockable = o.isDockable;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 2e45627..5dde926 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -663,8 +663,10 @@
     }
 
     public final void onBusEvent(TaskStackUpdatedEvent event) {
-        mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
-        mStack.createAffiliatedGroupings(getContext());
+        if (!event.inMultiWindow) {
+            mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
+            mStack.createAffiliatedGroupings(getContext());
+        }
     }
 
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 016d937..079d7b9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -21,7 +21,9 @@
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.view.ViewDebug;
+import android.widget.Toast;
 
+import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.EventBus;
@@ -148,11 +150,16 @@
 
         mVisibleDockStates.clear();
         if (!ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) {
-            // Add the dock state drop targets (these take priority)
-            TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
-            for (TaskStack.DockState dockState : dockStates) {
-                registerDropTargetForCurrentDrag(dockState);
-                mVisibleDockStates.add(dockState);
+            if (!event.task.isDockable) {
+                Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message,
+                        Toast.LENGTH_SHORT).show();
+            } else {
+                // Add the dock state drop targets (these take priority)
+                TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
+                for (TaskStack.DockState dockState : dockStates) {
+                    registerDropTargetForCurrentDrag(dockState);
+                    mVisibleDockStates.add(dockState);
+                }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index d71d70f..e1a81c8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1573,6 +1573,9 @@
             Task launchTask = mStack.getStackTasks().get(launchTaskIndex);
             EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(launchTask),
                     launchTask, null, INVALID_STACK_ID, false /* screenPinningRequested */));
+
+            MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
+                    launchTask.key.getComponent().toString());
         }
     }
 
@@ -1830,12 +1833,28 @@
     }
 
     public final void onBusEvent(TaskStackUpdatedEvent event) {
-        // Scroll the stack to the front after it has been updated
+        if (!event.inMultiWindow) {
+            // Scroll the stack to the front after it has been updated
+            event.addPostAnimationCallback(new Runnable() {
+                @Override
+                public void run() {
+                    mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
+                            null /* postScrollRunnable */);
+                }
+            });
+        }
+        // When the multi-window state changes, rebind all task view headers again to update their
+        // dockable state
         event.addPostAnimationCallback(new Runnable() {
             @Override
             public void run() {
-                mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
-                        null /* postScrollRunnable */);
+                List<TaskView> taskViews = getTaskViews();
+                int taskViewCount = taskViews.size();
+                for (int i = 0; i < taskViewCount; i++) {
+                    TaskView tv = taskViews.get(i);
+                    tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
+                            tv.mIsDisabledInSafeMode);
+                }
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0543dd5..05a8527 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -139,6 +139,7 @@
     // Header views
     ImageView mIconView;
     TextView mTitleView;
+    TextView mSubTitleView;
     ImageView mMoveTaskButton;
     ImageView mDismissButton;
     ViewStub mAppOverlayViewStub;
@@ -237,6 +238,7 @@
         mIconView.setClickable(false);
         mIconView.setOnLongClickListener(this);
         mTitleView = (TextView) findViewById(R.id.title);
+        mSubTitleView = (TextView) findViewById(R.id.sub_title);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
         if (ssp.hasFreeformWorkspaceSupport()) {
             mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
@@ -367,6 +369,7 @@
 
     /** Binds the bar view to the task */
     public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
         mTask = t;
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
@@ -384,6 +387,13 @@
         mTitleView.setContentDescription(t.contentDescription);
         mTitleView.setTextColor(t.useLightOnPrimaryColor ?
                 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
+        if (!t.isDockable && ssp.hasDockedTask()) {
+            mSubTitleView.setVisibility(View.VISIBLE);
+            mSubTitleView.setTextColor(t.useLightOnPrimaryColor ?
+                    mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
+        } else {
+            mSubTitleView.setVisibility(View.GONE);
+        }
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(t.dismissDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 9118e9c..d5f7b39 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -26,7 +26,7 @@
 import android.graphics.Paint;
 import android.util.AttributeSet;
 import android.util.Property;
-import android.widget.ImageButton;
+import android.view.View;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -34,7 +34,7 @@
 /**
  * View for the handle in the docked stack divider.
  */
-public class DividerHandleView extends ImageButton {
+public class DividerHandleView extends View {
 
     private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
             = new Property<DividerHandleView, Integer>(Integer.class, "width") {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 65a2f8f..da5cbe7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -27,6 +27,7 @@
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -40,6 +41,8 @@
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
@@ -132,6 +135,42 @@
     private boolean mGrowRecents;
     private Animator mCurrentAnimator;
 
+    private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            if (isHorizontalDivision()) {
+                info.addAction(new AccessibilityAction(R.id.action_move_up,
+                        mContext.getString(R.string.accessibility_action_divider_move_up)));
+                info.addAction(new AccessibilityAction(R.id.action_move_down,
+                        mContext.getString(R.string.accessibility_action_divider_move_down)));
+            } else {
+                info.addAction(new AccessibilityAction(R.id.action_move_left,
+                        mContext.getString(R.string.accessibility_action_divider_move_left)));
+                info.addAction(new AccessibilityAction(R.id.action_move_right,
+                        mContext.getString(R.string.accessibility_action_divider_move_right)));
+            }
+        }
+
+        @Override
+        public boolean performAccessibilityAction(View host, int action, Bundle args) {
+            if (action == R.id.action_move_up || action == R.id.action_move_down
+                    || action == R.id.action_move_left || action == R.id.action_move_right) {
+                int position = getCurrentPosition();
+                SnapTarget currentTarget = mSnapAlgorithm.calculateSnapTarget(
+                        position, 0 /* velocity */);
+                SnapTarget nextTarget =
+                        action == R.id.action_move_up || action == R.id.action_move_left
+                                ? mSnapAlgorithm.getPreviousTarget(currentTarget)
+                                : mSnapAlgorithm.getNextTarget(currentTarget);
+                startDragging(true /* animate */, false /* touching */);
+                stopDragging(getCurrentPosition(), nextTarget, 250, Interpolators.FAST_OUT_SLOW_IN);
+                return true;
+            }
+            return super.performAccessibilityAction(host, action, args);
+        }
+    };
+
     public DividerView(Context context) {
         super(context);
     }
@@ -171,6 +210,7 @@
         mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
                 landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+        mHandle.setAccessibilityDelegate(mHandleDelegate);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a0c63be..9bd645d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -110,7 +110,7 @@
 import java.util.List;
 import java.util.Locale;
 
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 
 public abstract class BaseStatusBar extends SystemUI implements
@@ -1007,7 +1007,7 @@
             }
         });
 
-        guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
+        guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey()));
     }
 
     protected GearDisplayedListener getGearDisplayedListener() {
@@ -1044,9 +1044,9 @@
 
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
 
-                // ensure that it's layouted but not visible until actually laid out
+                // ensure that it's laid but not visible until actually laid out
                 guts.setVisibility(View.INVISIBLE);
-                // Post to ensure the the guts are properly layed out.
+                // Post to ensure the the guts are properly laid out.
                 guts.post(new Runnable() {
                     public void run() {
                         dismissPopups();
@@ -1142,6 +1142,11 @@
     }
 
     @Override
+    public void toggleSplitScreen() {
+        toggleSplitScreenMode();
+    }
+
+    @Override
     public void preloadRecentApps() {
         int msg = MSG_PRELOAD_RECENT_APPS;
         mHandler.removeMessages(msg);
@@ -1211,6 +1216,13 @@
         }
     };
 
+    /**
+     * Toggle docking the app window
+     *
+     * @return {@code true} if the app window is docked after the toggle, {@code false} otherwise.
+     */
+    protected abstract boolean toggleSplitScreenMode();
+
     /** Proxy for RecentsComponent */
 
     protected void showRecents(boolean triggeredFromAltTab) {
@@ -1872,7 +1884,7 @@
         return entry;
     }
 
-    protected StatusBarIconView createIcon(StatusBarNotification sbn) {
+    public StatusBarIconView createIcon(StatusBarNotification sbn) {
         // Construct the icon.
         Notification n = sbn.getNotification();
         final StatusBarIconView iconView = new StatusBarIconView(mContext,
@@ -2203,6 +2215,16 @@
             return false;
         }
 
+        if (isSnoozedPackage(sbn)) {
+            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
+            return false;
+        }
+
+        if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
+            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
+            return false;
+        }
+
         if (sbn.getNotification().fullScreenIntent != null) {
             if (mAccessibilityManager.isTouchExplorationEnabled()) {
                 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
@@ -2212,16 +2234,6 @@
             }
         }
 
-        if (isSnoozedPackage(sbn)) {
-            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
-            return false;
-        }
-
-        if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_MAX) {
-            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
-            return false;
-        }
-
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3b960ee..6a98488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -72,6 +72,7 @@
     private static final int MSG_ADD_QS_TILE                   = 27 << MSG_SHIFT;
     private static final int MSG_REMOVE_QS_TILE                = 28 << MSG_SHIFT;
     private static final int MSG_CLICK_QS_TILE                 = 29 << MSG_SHIFT;
+    private static final int MSG_TOGGLE_APP_SPLIT_SCREEN       = 30 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -104,6 +105,7 @@
         public void showRecentApps(boolean triggeredFromAltTab);
         public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
         public void toggleRecentApps();
+        public void toggleSplitScreen();
         public void preloadRecentApps();
         public void toggleKeyboardShortcutsMenu();
         public void cancelPreloadRecentApps();
@@ -223,6 +225,13 @@
         }
     }
 
+    public void toggleSplitScreen() {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_TOGGLE_APP_SPLIT_SCREEN);
+            mHandler.obtainMessage(MSG_TOGGLE_APP_SPLIT_SCREEN, 0, 0, null).sendToTarget();
+        }
+    }
+
     public void toggleRecentApps() {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
@@ -464,6 +473,9 @@
                 case MSG_CLICK_QS_TILE:
                     mCallbacks.clickTile((ComponentName) msg.obj);
                     break;
+                case MSG_TOGGLE_APP_SPLIT_SCREEN:
+                    mCallbacks.toggleSplitScreen();
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 212d290..123dc69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -19,6 +19,7 @@
 import android.view.View;
 
 import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 /**
  * A helper to fade views in and out.
@@ -44,7 +45,34 @@
         if (view.hasOverlappingRendering()) {
             view.animate().withLayer();
         }
+    }
 
+    public static void fadeOut(View view, float fadeOutAmount) {
+        view.animate().cancel();
+        if (fadeOutAmount == 1.0f) {
+            view.setVisibility(View.INVISIBLE);
+        } else if (view.getVisibility() == View.INVISIBLE) {
+            view.setVisibility(View.VISIBLE);
+        }
+        fadeOutAmount = mapToFadeDuration(fadeOutAmount);
+        float alpha = Interpolators.ALPHA_OUT.getInterpolation(1.0f - fadeOutAmount);
+        view.setAlpha(alpha);
+        updateLayerType(view, alpha);
+    }
+
+    private static float mapToFadeDuration(float fadeOutAmount) {
+        // Assuming a linear interpolator, we can easily map it to our new duration
+        float endPoint = (float) ANIMATION_DURATION_LENGTH
+                / (float) StackStateAnimator.ANIMATION_DURATION_STANDARD;
+        return Math.min(fadeOutAmount / endPoint, 1.0f);
+    }
+
+    private static void updateLayerType(View view, float alpha) {
+        if (view.hasOverlappingRendering() && alpha > 0.0f && alpha < 1.0f) {
+            view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE) {
+            view.setLayerType(View.LAYER_TYPE_NONE, null);
+        }
     }
 
     public static void fadeIn(final View view) {
@@ -62,4 +90,15 @@
             view.animate().withLayer();
         }
     }
+
+    public static void fadeIn(View view, float fadeInAmount) {
+        view.animate().cancel();
+        if (view.getVisibility() == View.INVISIBLE) {
+            view.setVisibility(View.VISIBLE);
+        }
+        fadeInAmount = mapToFadeDuration(fadeInAmount);
+        float alpha = Interpolators.ALPHA_IN.getInterpolation(fadeInAmount);
+        view.setAlpha(alpha);
+        updateLayerType(view, alpha);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index c72cec3..da125d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -868,6 +868,10 @@
 
     public void setUserLocked(boolean userLocked) {
         mUserLocked = userLocked;
+        mPrivateLayout.setUserExpanding(userLocked);
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setUserLocked(userLocked);
+        }
     }
 
     /**
@@ -1174,6 +1178,9 @@
         int contentHeight = Math.max(getMinHeight(), height);
         mPrivateLayout.setContentHeight(contentHeight);
         mPublicLayout.setContentHeight(contentHeight);
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setActualHeight(height);
+        }
         if (mGuts != null) {
             mGuts.setActualHeight(height);
         }
@@ -1259,7 +1266,7 @@
         return mMaxExpandHeight != 0;
     }
 
-    private NotificationContentView getShowingLayout() {
+    public NotificationContentView getShowingLayout() {
         return mShowingPublic ? mPublicLayout : mPrivateLayout;
     }
 
@@ -1295,8 +1302,15 @@
     }
 
     @Override
-    public boolean needsIncreasedPadding() {
-        return mIsSummaryWithChildren && isGroupExpanded();
+    public float getIncreasedPaddingAmount() {
+        if (mIsSummaryWithChildren) {
+            if (isGroupExpanded()) {
+                return 1.0f;
+            } else if (isUserLocked()) {
+                return mChildrenContainer.getChildExpandFraction();
+            }
+        }
+        return 0.0f;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index c9e1cff..1ff87f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -391,8 +391,11 @@
     public void setShadowAlpha(float shadowAlpha) {
     }
 
-    public boolean needsIncreasedPadding() {
-        return false;
+    /**
+     * @return an amount between 0 and 1 of increased padding that this child needs
+     */
+    public float getIncreasedPaddingAmount() {
+        return 0.0f;
     }
 
     public boolean mustStayOnScreen() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index bc85922..977b37e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -48,6 +48,7 @@
     private static final int VISIBLE_TYPE_EXPANDED = 1;
     private static final int VISIBLE_TYPE_HEADSUP = 2;
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
+    private static final int UNDEFINED = -1;
 
     private final Rect mClipBounds = new Rect();
     private final int mMinContractedHeight;
@@ -102,6 +103,8 @@
     private boolean mExpandable;
     private boolean mClipToActualHeight = true;
     private ExpandableNotificationRow mContainingNotification;
+    private int mTransformationStartVisibleType;
+    private boolean mUserExpanding;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -349,6 +352,41 @@
         invalidateOutline();
     }
 
+    private void updateContentTransformation() {
+        int visibleType = calculateVisibleType();
+        if (visibleType != mVisibleType) {
+            // A new transformation starts
+            mTransformationStartVisibleType = mVisibleType;
+            final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
+            final TransformableView hiddenView = getTransformableViewForVisibleType(
+                    mTransformationStartVisibleType);
+            shownView.transformFrom(hiddenView, 0.0f);
+            getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
+            hiddenView.transformTo(shownView, 0.0f);
+            mVisibleType = visibleType;
+        }
+        if (mTransformationStartVisibleType != UNDEFINED
+                && mVisibleType != mTransformationStartVisibleType) {
+            final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
+            final TransformableView hiddenView = getTransformableViewForVisibleType(
+                    mTransformationStartVisibleType);
+            float transformationAmount = calculateTransformationAmount();
+            shownView.transformFrom(hiddenView, transformationAmount);
+            hiddenView.transformTo(shownView, transformationAmount);
+        } else {
+            updateViewVisibilities(visibleType);
+        }
+    }
+
+    private float calculateTransformationAmount() {
+        int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
+        int endHeight = getViewForVisibleType(mVisibleType).getHeight();
+        int progress = Math.abs(mContentHeight - startHeight);
+        int totalDistance = Math.abs(endHeight - startHeight);
+        float amount = (float) progress / (float) totalDistance;
+        return Math.min(1.0f, amount);
+    }
+
     public int getContentHeight() {
         return mContentHeight;
     }
@@ -363,10 +401,14 @@
     }
 
     public int getMinHeight() {
-        if (mIsChildInGroup && !isGroupExpanded()) {
-            return mSingleLineView.getHeight();
-        } else {
+        return getMinHeight(false /* likeGroupExpanded */);
+    }
+
+    public int getMinHeight(boolean likeGroupExpanded) {
+        if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
             return mContractedChild.getHeight();
+        } else {
+            return mSingleLineView.getHeight();
         }
     }
 
@@ -397,6 +439,10 @@
         if (mContractedChild == null) {
             return;
         }
+        if (mUserExpanding) {
+            updateContentTransformation();
+            return;
+        }
         int visibleType = calculateVisibleType();
         if (visibleType != mVisibleType || force) {
             if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
@@ -492,13 +538,28 @@
      * @return one of the static enum types in this view, calculated form the current state
      */
     private int calculateVisibleType() {
-        boolean noExpandedChild = mExpandedChild == null;
-
+        if (mUserExpanding) {
+            int height = !mIsChildInGroup || isGroupExpanded()
+                    || mContainingNotification.isExpanded()
+                    ? mContainingNotification.getMaxContentHeight()
+                    : mContainingNotification.getShowingLayout().getMinHeight();
+            int expandedVisualType = getVisualTypeForHeight(height);
+            int collapsedVisualType = getVisualTypeForHeight(
+                    mContainingNotification.getMinExpandHeight());
+            return mTransformationStartVisibleType == collapsedVisualType
+                    ? expandedVisualType
+                    : collapsedVisualType;
+        }
         int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight());
+        return getVisualTypeForHeight(viewHeight);
+    }
+
+    private int getVisualTypeForHeight(float viewHeight) {
+        boolean noExpandedChild = mExpandedChild == null;
         if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
             return VISIBLE_TYPE_EXPANDED;
         }
-        if (mIsChildInGroup && !isGroupExpanded()) {
+        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
             return VISIBLE_TYPE_SINGLELINE;
         }
 
@@ -723,4 +784,15 @@
             updateSingleLineView();
         }
     }
+
+    public void setUserExpanding(boolean userExpanding) {
+        mUserExpanding = userExpanding;
+        if (userExpanding) {
+            mTransformationStartVisibleType = mVisibleType;
+        } else {
+            mTransformationStartVisibleType = UNDEFINED;
+            mVisibleType = calculateVisibleType();
+            updateViewVisibilities(mVisibleType);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index ccd0ad8..e300614 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -203,11 +203,11 @@
 
             String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
 
-            // PRIORITY_MIN media streams are allowed to drift to the bottom
+            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
             final boolean aMedia = a.key.equals(mediaNotification)
-                    && aImportance > Ranking.IMPORTANCE_LOW;
+                    && aImportance > Ranking.IMPORTANCE_MIN;
             final boolean bMedia = b.key.equals(mediaNotification)
-                    && bImportance > Ranking.IMPORTANCE_LOW;
+                    && bImportance > Ranking.IMPORTANCE_MIN;
 
             boolean aSystemMax = aImportance >= Ranking.IMPORTANCE_MAX &&
                     isSystemNotification(na);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index fe84d813..1c16bdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar;
 
 import android.app.INotificationManager;
-import android.app.Notification;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -39,23 +38,31 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.tuner.TunerService;
 
 /**
  * The guts of a notification revealed when performing a long press.
  */
-public class NotificationGuts extends LinearLayout {
+public class NotificationGuts extends LinearLayout implements TunerService.Tunable {
+    public static final String SHOW_SLIDER = "show_importance_slider";
 
     private Drawable mBackground;
     private int mClipTopAmount;
     private int mActualHeight;
     private boolean mExposed;
-    private SeekBar mSeekBar;
     private INotificationManager mINotificationManager;
     private int mStartingImportance;
+    private boolean mShowSlider;
+
+    private SeekBar mSeekBar;
+    private RadioButton mBlock;
+    private RadioButton mSilent;
+    private RadioButton mReset;
 
     public NotificationGuts(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(false);
+        TunerService.get(mContext).addTunable(this, SHOW_SLIDER);
     }
 
     @Override
@@ -102,31 +109,81 @@
         }
     }
 
-    void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
-            final int importance) {
+    void bindImportance(final PackageManager pm, final StatusBarNotification sbn,
+            final ExpandableNotificationRow row, final int importance) {
         mStartingImportance = importance;
         mINotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-
-        final TextView importanceSummary = ((TextView) row.findViewById(R.id.summary));
-        final TextView importanceTitle = ((TextView) row.findViewById(R.id.title));
-        mSeekBar = (SeekBar) row.findViewById(R.id.seekbar);
         boolean systemApp = false;
         try {
-            final PackageManager pm = BaseStatusBar.getPackageManagerForUser(
-                    getContext(), sbn.getUser().getIdentifier());
             final PackageInfo info =
                     pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES);
             systemApp = Utils.isSystemPackage(pm, info);
         } catch (PackageManager.NameNotFoundException e) {
             // unlikely.
         }
+
+        final View importanceSlider = row.findViewById(R.id.importance_slider);
+        final View importanceButtons = row.findViewById(R.id.importance_buttons);
+        if (mShowSlider) {
+            bindSlider(importanceSlider, sbn, systemApp);
+            importanceSlider.setVisibility(View.VISIBLE);
+            importanceButtons.setVisibility(View.GONE);
+        } else {
+            bindToggles(importanceButtons, sbn, systemApp);
+            importanceButtons.setVisibility(View.VISIBLE);
+            importanceSlider.setVisibility(View.GONE);
+        }
+    }
+
+    void saveImportance(final StatusBarNotification sbn) {
+        int progress;
+        if (mSeekBar!= null && mSeekBar.isShown()) {
+            progress = mSeekBar.getProgress();
+        } else {
+            if (mBlock.isChecked()) {
+                progress = NotificationListenerService.Ranking.IMPORTANCE_NONE;
+            } else if (mSilent.isChecked()) {
+                progress = NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
+            } else {
+                progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+            }
+        }
+        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
+                progress - mStartingImportance);
+        try {
+            mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress);
+        } catch (RemoteException e) {
+            // :(
+        }
+    }
+
+    private void bindToggles(final View importanceButtons, final StatusBarNotification sbn,
+            final boolean systemApp) {
+        mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
+        mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
+        mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
         if (systemApp) {
-            ((ImageView) row.findViewById(R.id.low_importance)).getDrawable().setTint(
+            mBlock.setVisibility(View.GONE);
+            mReset.setText(mContext.getString(R.string.do_not_silence));
+        } else {
+            mReset.setText(mContext.getString(R.string.do_not_silence_block));
+        }
+        mReset.setChecked(true);
+    }
+
+    private void bindSlider(final View importanceSlider, final StatusBarNotification sbn,
+            final boolean systemApp) {
+        final TextView importanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary));
+        final TextView importanceTitle = ((TextView) importanceSlider.findViewById(R.id.title));
+        mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar);
+
+        if (systemApp) {
+            ((ImageView) importanceSlider.findViewById(R.id.low_importance)).getDrawable().setTint(
                     mContext.getColor(R.color.notification_guts_disabled_icon_tint));
         }
         final int minProgress = systemApp ?
-                NotificationListenerService.Ranking.IMPORTANCE_LOW
+                NotificationListenerService.Ranking.IMPORTANCE_MIN
                 : NotificationListenerService.Ranking.IMPORTANCE_NONE;
         mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX);
         mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@@ -159,6 +216,11 @@
                                 R.string.notification_importance_blocked));
                         importanceTitle.setText(mContext.getString(R.string.blocked_importance));
                         break;
+                    case NotificationListenerService.Ranking.IMPORTANCE_MIN:
+                        importanceSummary.setText(mContext.getString(
+                                R.string.notification_importance_min));
+                        importanceTitle.setText(mContext.getString(R.string.min_importance));
+                        break;
                     case NotificationListenerService.Ranking.IMPORTANCE_LOW:
                         importanceSummary.setText(mContext.getString(
                                 R.string.notification_importance_low));
@@ -182,18 +244,7 @@
                 }
             }
         });
-        mSeekBar.setProgress(importance);
-    }
-
-    void saveImportance(final StatusBarNotification sbn) {
-        int progress = mSeekBar.getProgress();
-        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
-                progress - mStartingImportance);
-        try {
-            mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress);
-        } catch (RemoteException e) {
-            // :(
-        }
+        mSeekBar.setProgress(mStartingImportance);
     }
 
     public void setActualHeight(int actualHeight) {
@@ -224,4 +275,11 @@
     public boolean areGutsExposed() {
         return mExposed;
     }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (SHOW_SLIDER.equals(key)) {
+            mShowSlider = newValue != null && Integer.parseInt(newValue) != 0;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
index 38b6497..009eed7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
@@ -30,6 +30,7 @@
 
     /**
      * Get the current state of a view in a transform animation
+     *
      * @param fadingView which view we are interested in
      * @return the current transform state of this viewtype
      */
@@ -37,18 +38,37 @@
 
     /**
      * Transform to the given view
+     *
      * @param notification the view to transform to
      */
     void transformTo(TransformableView notification, Runnable endRunnable);
 
     /**
+     * Transform to the given view by a specified amount.
+     *
+     * @param notification the view to transform to
+     * @param transformationAmount how much transformation should be done
+     */
+    void transformTo(TransformableView notification, float transformationAmount);
+
+    /**
      * Transform to this view from the given view
+     *
      * @param notification the view to transform from
      */
     void transformFrom(TransformableView notification);
 
     /**
+     * Transform to this view from the given view by a specified amount.
+     *
+     * @param notification the view to transform from
+     * @param transformationAmount how much transformation should be done
+     */
+    void transformFrom(TransformableView notification, float transformationAmount);
+
+    /**
      * Set this view to be fully visible or gone
+     *
      * @param visible
      */
     void setVisible(boolean visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 63ff5aa..bf05d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -16,13 +16,17 @@
 
 package com.android.systemui.statusbar;
 
-import android.os.Handler;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.util.ArrayMap;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.util.Stack;
 
@@ -33,9 +37,9 @@
 
     private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
 
-    private final Handler mHandler = new Handler();
     private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
     private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
+    private ValueAnimator mViewTransformationAnimation;
 
     public void addTransformedView(int key, View transformedView) {
         mTransformedViews.put(key, transformedView);
@@ -59,61 +63,123 @@
     }
 
     @Override
-    public void transformTo(TransformableView notification, Runnable endRunnable) {
-        Runnable runnable = endRunnable;
+    public void transformTo(final TransformableView notification, final Runnable endRunnable) {
+        if (mViewTransformationAnimation != null) {
+            mViewTransformationAnimation.cancel();
+        }
+        mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+        mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                transformTo(notification, animation.getAnimatedFraction());
+            }
+        });
+        mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
+        mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        if (endRunnable != null) {
+            mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
+                public boolean mCancelled;
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    endRunnable.run();
+                    if (!mCancelled) {
+                        setVisible(false);
+                    } else {
+                        abortTransformations();
+                    }
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    mCancelled = true;
+                }
+            });
+        }
+        mViewTransformationAnimation.start();
+    }
+
+    @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
         for (Integer viewType : mTransformedViews.keySet()) {
             TransformState ownState = getCurrentState(viewType);
             if (ownState != null) {
                 CustomTransformation customTransformation = mCustomTransformations.get(viewType);
                 if (customTransformation != null && customTransformation.transformTo(
-                        ownState, notification, runnable)) {
+                        ownState, notification, transformationAmount)) {
                     ownState.recycle();
-                    runnable = null;
                     continue;
                 }
                 TransformState otherState = notification.getCurrentState(viewType);
                 if (otherState != null) {
-                    boolean run = ownState.transformViewTo(otherState, runnable);
+                    ownState.transformViewTo(otherState, transformationAmount);
                     otherState.recycle();
-                    if (run) {
-                        runnable = null;
-                    }
                 } else {
                     // there's no other view available
-                    CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), runnable);
-                    runnable = null;
+                    CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), transformationAmount);
                 }
                 ownState.recycle();
             }
         }
-        if (runnable != null) {
-            // We need to post, since the visible type is only set after the transformation is
-            // started
-            mHandler.post(runnable);
-        }
     }
 
     @Override
-    public void transformFrom(TransformableView notification) {
+    public void transformFrom(final TransformableView notification) {
+        if (mViewTransformationAnimation != null) {
+            mViewTransformationAnimation.cancel();
+        }
+        mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+        mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                transformFrom(notification, animation.getAnimatedFraction());
+            }
+        });
+        mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
+            public boolean mCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mCancelled) {
+                    setVisible(true);
+                } else {
+                    abortTransformations();
+                }
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+        });
+        mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
+        mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        mViewTransformationAnimation.start();
+    }
+
+    @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
         for (Integer viewType : mTransformedViews.keySet()) {
             TransformState ownState = getCurrentState(viewType);
             if (ownState != null) {
                 CustomTransformation customTransformation = mCustomTransformations.get(viewType);
                 if (customTransformation != null && customTransformation.transformFrom(
-                        ownState, notification)) {
+                        ownState, notification, transformationAmount)) {
                     ownState.recycle();
                     continue;
                 }
                 TransformState otherState = notification.getCurrentState(viewType);
                 if (otherState != null) {
-                    ownState.transformViewFrom(otherState);
+                    ownState.transformViewFrom(otherState, transformationAmount);
                     otherState.recycle();
                 } else {
                     // There's no other view, lets fade us in
                     // Certain views need to prepare the fade in and make sure its children are
                     // completely visible. An example is the notification header.
-                    ownState.prepareFadeIn();
-                    CrossFadeHelper.fadeIn(mTransformedViews.get(viewType));
+                    if (transformationAmount == 0.0f) {
+                        ownState.prepareFadeIn();
+                    }
+                    CrossFadeHelper.fadeIn(mTransformedViews.get(viewType), transformationAmount);
                 }
                 ownState.recycle();
             }
@@ -131,6 +197,16 @@
         }
     }
 
+    private void abortTransformations() {
+        for (Integer viewType : mTransformedViews.keySet()) {
+            TransformState ownState = getCurrentState(viewType);
+            if (ownState != null) {
+                ownState.abortTransformation();
+                ownState.recycle();
+            }
+        }
+    }
+
     /**
      * Add the remaining transformation views such that all views are being transformed correctly
      * @param viewRoot the root below which all elements need to be transformed
@@ -173,22 +249,44 @@
         }
     }
 
-    public interface CustomTransformation {
+    public static abstract class CustomTransformation {
         /**
          * Transform a state to the given view
          * @param ownState the state to transform
          * @param notification the view to transform to
+         * @param transformationAmount how much transformation should be done
          * @return whether a custom transformation is performed
          */
-        boolean transformTo(TransformState ownState, TransformableView notification,
-                Runnable endRunnable);
+        public abstract boolean transformTo(TransformState ownState,
+                TransformableView notification,
+                float transformationAmount);
 
         /**
          * Transform to this state from the given view
          * @param ownState the state to transform to
          * @param notification the view to transform from
+         * @param transformationAmount how much transformation should be done
          * @return whether a custom transformation is performed
          */
-        boolean transformFrom(TransformState ownState, TransformableView notification);
+        public abstract boolean transformFrom(TransformState ownState,
+                TransformableView notification,
+                float transformationAmount);
+
+        /**
+         * Perform a custom initialisation before transforming.
+         *
+         * @param ownState our own state
+         * @param otherState the other state
+         * @return whether a custom initialization is done
+         */
+        public boolean initTransformation(TransformState ownState,
+                TransformState otherState) {
+            return false;
+        }
+
+        public boolean customTransformTarget(TransformState ownState,
+                TransformState otherState) {
+            return false;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
index 81483c6..b66e9f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
@@ -46,7 +46,7 @@
     }
 
     @Override
-    public boolean transformViewTo(TransformState otherState, Runnable endRunnable) {
+    public boolean transformViewTo(TransformState otherState, float transformationAmount) {
         // if the transforming notification has a header, we have ensured that it looks the same
         // but the expand button, so lets fade just that one and transform the work profile icon.
         if (!(mTransformedView instanceof NotificationHeaderView)) {
@@ -62,14 +62,14 @@
             if (headerChild != mExpandButton) {
                 headerChild.setVisibility(View.INVISIBLE);
             } else {
-                CrossFadeHelper.fadeOut(mExpandButton, endRunnable);
+                CrossFadeHelper.fadeOut(mExpandButton, transformationAmount);
             }
         }
         return true;
     }
 
     @Override
-    public void transformViewFrom(TransformState otherState) {
+    public void transformViewFrom(TransformState otherState, float transformationAmount) {
         // if the transforming notification has a header, we have ensured that it looks the same
         // but the expand button, so lets fade just that one and transform the work profile icon.
         if (!(mTransformedView instanceof NotificationHeaderView)) {
@@ -85,12 +85,13 @@
                 continue;
             }
             if (headerChild == mExpandButton) {
-                CrossFadeHelper.fadeIn(mExpandButton);
+                CrossFadeHelper.fadeIn(mExpandButton, transformationAmount);
             } else {
                 headerChild.setVisibility(View.VISIBLE);
                 if (headerChild == mWorkProfileIcon) {
-                    mWorkProfileState.animateViewFrom(
-                            ((HeaderTransformState) otherState).mWorkProfileState);
+                    mWorkProfileState.transformViewFullyFrom(
+                            ((HeaderTransformState) otherState).mWorkProfileState,
+                            transformationAmount);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index 81144d5..c80cad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -71,13 +71,13 @@
                 new ViewTransformationHelper.CustomTransformation() {
                     @Override
                     public boolean transformTo(TransformState ownState, TransformableView notification,
-                            Runnable endRunnable) {
+                            float transformationAmount) {
                         // We want to transform to the same y location as the title
                         TransformState otherState = notification.getCurrentState(
                                 TRANSFORMING_VIEW_TITLE);
-                        CrossFadeHelper.fadeOut(mTextView, endRunnable);
+                        CrossFadeHelper.fadeOut(mTextView, transformationAmount);
                         if (otherState != null) {
-                            ownState.animateViewVerticalTo(otherState, endRunnable);
+                            ownState.transformViewVerticalTo(otherState, transformationAmount);
                             otherState.recycle();
                         }
                         return true;
@@ -85,13 +85,13 @@
 
                     @Override
                     public boolean transformFrom(TransformState ownState,
-                            TransformableView notification) {
+                            TransformableView notification, float transformationAmount) {
                         // We want to transform from the same y location as the title
                         TransformState otherState = notification.getCurrentState(
                                 TRANSFORMING_VIEW_TITLE);
-                        CrossFadeHelper.fadeIn(mTextView);
+                        CrossFadeHelper.fadeIn(mTextView, transformationAmount);
                         if (otherState != null) {
-                            ownState.animateViewVerticalFrom(otherState);
+                            ownState.transformViewVerticalFrom(otherState, transformationAmount);
                             otherState.recycle();
                         }
                         return true;
@@ -133,11 +133,21 @@
     }
 
     @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformTo(notification, transformationAmount);
+    }
+
+    @Override
     public void transformFrom(TransformableView notification) {
         mTransformationHelper.transformFrom(notification);
     }
 
     @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformFrom(notification, transformationAmount);
+    }
+
+    @Override
     public void setVisible(boolean visible) {
         setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
         mTransformationHelper.setVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index e891a97..45027c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -62,7 +62,7 @@
     }
 
     @Override
-    protected boolean animateScale() {
+    protected boolean transformScale() {
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 5a71caf..000f957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -142,7 +142,8 @@
 
     protected void updateTransformedTypes() {
         mTransformationHelper.reset();
-        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER, mNotificationHeader);
+        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER,
+                mNotificationHeader);
     }
 
     @Override
@@ -299,11 +300,21 @@
     }
 
     @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformTo(notification, transformationAmount);
+    }
+
+    @Override
     public void transformFrom(TransformableView notification) {
         mTransformationHelper.transformFrom(notification);
     }
 
     @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
+        mTransformationHelper.transformFrom(notification, transformationAmount);
+    }
+
+    @Override
     public void setVisible(boolean visible) {
         super.setVisible(visible);
         mTransformationHelper.setVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 0c21f0b..fd4eca8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -49,76 +49,65 @@
                 new ViewTransformationHelper.CustomTransformation() {
                     @Override
                     public boolean transformTo(TransformState ownState,
-                            TransformableView notification, final Runnable endRunnable) {
+                            TransformableView notification, final float transformationAmount) {
                         if (!(notification instanceof HybridNotificationView)) {
                             return false;
                         }
                         TransformState otherState = notification.getCurrentState(
                                 TRANSFORMING_VIEW_TITLE);
                         final View text = ownState.getTransformedView();
-                        CrossFadeHelper.fadeOut(text, endRunnable);
+                        CrossFadeHelper.fadeOut(text, transformationAmount);
                         if (otherState != null) {
-                            int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
-                            int[] ownPosition = ownState.getLaidOutLocationOnScreen();
-                            text.animate()
-                                    .translationY((otherStablePosition[1]
-                                            + otherState.getTransformedView().getHeight()
-                                            - ownPosition[1]) * 0.33f)
-                                    .setDuration(
-                                            StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                                    .withEndAction(new Runnable() {
-                                        @Override
-                                        public void run() {
-                                            if (endRunnable != null) {
-                                                endRunnable.run();
-                                            }
-                                            TransformState.setClippingDeactivated(text,
-                                                    false);
-                                        }
-                                    });
-                            TransformState.setClippingDeactivated(text, true);
+                            ownState.transformViewVerticalTo(otherState, this,
+                                    transformationAmount);
                             otherState.recycle();
                         }
                         return true;
                     }
 
                     @Override
+                    public boolean customTransformTarget(TransformState ownState,
+                            TransformState otherState) {
+                        float endY = getTransformationY(ownState, otherState);
+                        ownState.setTransformationEndY(endY);
+                        return true;
+                    }
+
+                    @Override
                     public boolean transformFrom(TransformState ownState,
-                            TransformableView notification) {
+                            TransformableView notification, float transformationAmount) {
                         if (!(notification instanceof HybridNotificationView)) {
                             return false;
                         }
                         TransformState otherState = notification.getCurrentState(
                                 TRANSFORMING_VIEW_TITLE);
                         final View text = ownState.getTransformedView();
-                        boolean isVisible = text.getVisibility() == View.VISIBLE;
-                        CrossFadeHelper.fadeIn(text);
+                        CrossFadeHelper.fadeIn(text, transformationAmount);
                         if (otherState != null) {
-                            int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
-                            int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
-                            if (!isVisible) {
-                                text.setTranslationY((otherStablePosition[1]
-                                        + otherState.getTransformedView().getHeight()
-                                        - ownStablePosition[1]) * 0.33f);
-                            }
-                            text.animate()
-                                    .translationY(0)
-                                    .setDuration(
-                                            StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                                    .withEndAction(new Runnable() {
-                                        @Override
-                                        public void run() {
-                                            TransformState.setClippingDeactivated(text,
-                                                    false);
-                                        }
-                                    });
-                            TransformState.setClippingDeactivated(text, true);
+                            ownState.transformViewVerticalFrom(otherState, this,
+                                    transformationAmount);
                             otherState.recycle();
                         }
                         return true;
                     }
+
+                    @Override
+                    public boolean initTransformation(TransformState ownState,
+                            TransformState otherState) {
+                        float startY = getTransformationY(ownState, otherState);
+                        ownState.setTransformationStartY(startY);
+                        return true;
+                    }
+
+                    private float getTransformationY(TransformState ownState,
+                            TransformState otherState) {
+                        int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
+                        int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
+                        return (otherStablePosition[1]
+                                + otherState.getTransformedView().getHeight()
+                                - ownStablePosition[1]) * 0.33f;
+                    }
+
                 }, TRANSFORMING_VIEW_TEXT);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 7089b78..4738657 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -34,4 +34,8 @@
         v.setTag(R.id.icon_is_grayscale, grayscale);
         return grayscale;
     }
+
+    public static float interpolate(float start, float end, float amount) {
+        return start * (1.0f - amount) + end * amount;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 328f8b5..d3503e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -98,12 +98,22 @@
     }
 
     @Override
+    public void transformTo(TransformableView notification, float transformationAmount) {
+        CrossFadeHelper.fadeOut(mView, transformationAmount);
+    }
+
+    @Override
     public void transformFrom(TransformableView notification) {
         // By default we are fading in completely
         CrossFadeHelper.fadeIn(mView);
     }
 
     @Override
+    public void transformFrom(TransformableView notification, float transformationAmount) {
+        CrossFadeHelper.fadeIn(mView, transformationAmount);
+    }
+
+    @Override
     public void setVisible(boolean visible) {
         mView.animate().cancel();
         mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 67d31be..f04fe5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -30,23 +30,30 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.ViewTransformationHelper;
 
 /**
  * A transform state of a view.
 */
 public class TransformState {
 
-    private static final int ANIMATE_X = 0x1;
-    private static final int ANIMATE_Y = 0x10;
-    private static final int ANIMATE_ALL = ANIMATE_X | ANIMATE_Y;
+    private static final float UNDEFINED = -1f;
+    private static final int TRANSOFORM_X = 0x1;
+    private static final int TRANSOFORM_Y = 0x10;
+    private static final int TRANSOFORM_ALL = TRANSOFORM_X | TRANSOFORM_Y;
     private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
     private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
     private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
+    private static final int TRANSFORMATION_START_X = R.id.transformation_start_x_tag;
+    private static final int TRANSFORMATION_START_Y = R.id.transformation_start_y_tag;
+    private static final int TRANSFORMATION_START_SCLALE_X = R.id.transformation_start_scale_x_tag;
+    private static final int TRANSFORMATION_START_SCLALE_Y = R.id.transformation_start_scale_y_tag;
     private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40);
 
     protected View mTransformedView;
     private int[] mOwnPosition = new int[2];
+    private float mTransformationEndY = UNDEFINED;
+    private float mTransformationEndX = UNDEFINED;
 
     public void initFrom(View view) {
         mTransformedView = view;
@@ -55,129 +62,233 @@
     /**
      * Transforms the {@link #mTransformedView} from the given transformviewstate
      * @param otherState the state to transform from
+     * @param transformationAmount how much to transform
      */
-    public void transformViewFrom(TransformState otherState) {
+    public void transformViewFrom(TransformState otherState, float transformationAmount) {
         mTransformedView.animate().cancel();
         if (sameAs(otherState)) {
-            // We have the same content, lets show ourselves
-            mTransformedView.setAlpha(1.0f);
-            mTransformedView.setVisibility(View.VISIBLE);
+            if (mTransformedView.getVisibility() == View.INVISIBLE) {
+                // We have the same content, lets show ourselves
+                mTransformedView.setAlpha(1.0f);
+                mTransformedView.setVisibility(View.VISIBLE);
+            }
         } else {
-            CrossFadeHelper.fadeIn(mTransformedView);
+            CrossFadeHelper.fadeIn(mTransformedView, transformationAmount);
         }
-        animateViewFrom(otherState);
+        transformViewFullyFrom(otherState, transformationAmount);
     }
 
-    public void animateViewFrom(TransformState otherState) {
-        animateViewFrom(otherState, ANIMATE_ALL);
+    public void transformViewFullyFrom(TransformState otherState, float transformationAmount) {
+        transformViewFrom(otherState, TRANSOFORM_ALL, null, transformationAmount);
     }
 
-    public void animateViewVerticalFrom(TransformState otherState) {
-        animateViewFrom(otherState, ANIMATE_Y);
+    public void transformViewVerticalFrom(TransformState otherState,
+            ViewTransformationHelper.CustomTransformation customTransformation,
+            float transformationAmount) {
+        transformViewFrom(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
     }
 
-    private void animateViewFrom(TransformState otherState, int animationFlags) {
+    public void transformViewVerticalFrom(TransformState otherState, float transformationAmount) {
+        transformViewFrom(otherState, TRANSOFORM_Y, null, transformationAmount);
+    }
+
+    private void transformViewFrom(TransformState otherState, int transformationFlags,
+            ViewTransformationHelper.CustomTransformation customTransformation,
+            float transformationAmount) {
         final View transformedView = mTransformedView;
+        boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
+        boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
+        boolean transformScale = transformScale();
         // lets animate the positions correctly
-        int[] otherPosition = otherState.getLocationOnScreen();
-        int[] ownStablePosition = getLaidOutLocationOnScreen();
-        if ((animationFlags & ANIMATE_X) != 0) {
-            transformedView.setTranslationX(otherPosition[0] - ownStablePosition[0]);
-            transformedView.animate().translationX(0);
-        }
-        if ((animationFlags & ANIMATE_Y) != 0) {
-            transformedView.setTranslationY(otherPosition[1] - ownStablePosition[1]);
-            transformedView.animate().translationY(0);
-        }
-        if (animateScale()) {
-            // we also want to animate the scale if we're the same
-            View otherView = otherState.getTransformedView();
-            if (otherView.getWidth() != transformedView.getWidth()) {
-                float scaleX = (otherView.getWidth() * otherView.getScaleX()
-                        / (float) transformedView.getWidth());
-                transformedView.setScaleX(scaleX);
-                transformedView.setPivotX(0);
-                transformedView.animate().scaleX(1.0f);
+        if (transformationAmount == 0.0f) {
+            int[] otherPosition = otherState.getLocationOnScreen();
+            int[] ownStablePosition = getLaidOutLocationOnScreen();
+            if (customTransformation == null
+                    || !customTransformation.initTransformation(this, otherState)) {
+                if (transformX) {
+                    setTransformationStartX(otherPosition[0] - ownStablePosition[0]);
+                }
+                if (transformY) {
+                    setTransformationStartY(otherPosition[1] - ownStablePosition[1]);
+                }
+                // we also want to animate the scale if we're the same
+                View otherView = otherState.getTransformedView();
+                if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
+                    setTransformationStartScaleX(otherView.getWidth() * otherView.getScaleX()
+                            / (float) transformedView.getWidth());
+                    transformedView.setPivotX(0);
+                } else {
+                    setTransformationStartScaleX(UNDEFINED);
+                }
+                if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
+                    setTransformationStartScaleY(otherView.getHeight() * otherView.getScaleY()
+                            / (float) transformedView.getHeight());
+                    transformedView.setPivotY(0);
+                } else {
+                    setTransformationStartScaleY(UNDEFINED);
+                }
             }
-            if (otherView.getHeight() != transformedView.getHeight()) {
-                float scaleY = (otherView.getHeight() * otherView.getScaleY()
-                        / (float) transformedView.getHeight());
-                transformedView.setScaleY(scaleY);
-                transformedView.setPivotY(0);
-                transformedView.animate().scaleY(1.0f);
+            if (!transformX) {
+                setTransformationStartX(UNDEFINED);
+            }
+            if (!transformY) {
+                setTransformationStartY(UNDEFINED);
+            }
+            if (!transformScale) {
+                setTransformationStartScaleX(UNDEFINED);
+                setTransformationStartScaleY(UNDEFINED);
+            }
+            setClippingDeactivated(transformedView, true);
+        }
+        float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                transformationAmount);
+        if (transformX) {
+            transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
+                    0.0f,
+                    interpolatedValue));
+        }
+        if (transformY) {
+            transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
+                    0.0f,
+                    interpolatedValue));
+        }
+        if (transformScale) {
+            float transformationStartScaleX = getTransformationStartScaleX();
+            if (transformationStartScaleX != UNDEFINED) {
+                transformedView.setScaleX(
+                        NotificationUtils.interpolate(transformationStartScaleX,
+                                1.0f,
+                                interpolatedValue));
+            }
+            float transformationStartScaleY = getTransformationStartScaleY();
+            if (transformationStartScaleY != UNDEFINED) {
+                transformedView.setScaleY(
+                        NotificationUtils.interpolate(transformationStartScaleY,
+                                1.0f,
+                                interpolatedValue));
             }
         }
-        transformedView.animate()
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        setClippingDeactivated(transformedView, false);
-                    }
-                });
-        setClippingDeactivated(transformedView, true);
     }
 
-    protected boolean animateScale() {
+    protected boolean transformScale() {
         return false;
     }
 
     /**
      * Transforms the {@link #mTransformedView} to the given transformviewstate
      * @param otherState the state to transform from
-     * @param endRunnable a runnable to run at the end of the animation
+     * @param transformationAmount how much to transform
      * @return whether an animation was started
      */
-    public boolean transformViewTo(TransformState otherState, final Runnable endRunnable) {
+    public boolean transformViewTo(TransformState otherState, float transformationAmount) {
         mTransformedView.animate().cancel();
         if (sameAs(otherState)) {
             // We have the same text, lets show ourselfs
-            mTransformedView.setAlpha(0.0f);
-            mTransformedView.setVisibility(View.INVISIBLE);
+            if (mTransformedView.getVisibility() == View.VISIBLE) {
+                mTransformedView.setAlpha(0.0f);
+                mTransformedView.setVisibility(View.INVISIBLE);
+            }
             return false;
         } else {
-            CrossFadeHelper.fadeOut(mTransformedView, endRunnable);
+            CrossFadeHelper.fadeOut(mTransformedView, transformationAmount);
         }
-        animateViewTo(otherState, endRunnable);
+        transformViewFullyTo(otherState, transformationAmount);
         return true;
     }
 
-    public void animateViewTo(TransformState otherState, Runnable endRunnable) {
-        animateViewTo(otherState, endRunnable, ANIMATE_ALL);
+    public void transformViewFullyTo(TransformState otherState, float transformationAmount) {
+        transformViewTo(otherState, TRANSOFORM_ALL, null, transformationAmount);
     }
 
-    public void animateViewVerticalTo(TransformState otherState, Runnable endRunnable) {
-        animateViewTo(otherState, endRunnable, ANIMATE_Y);
+    public void transformViewVerticalTo(TransformState otherState,
+            ViewTransformationHelper.CustomTransformation customTransformation,
+            float transformationAmount) {
+        transformViewTo(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
     }
 
-    private void animateViewTo(TransformState otherState, final Runnable endRunnable,
-            int animationFlags) {
+    public void transformViewVerticalTo(TransformState otherState, float transformationAmount) {
+        transformViewTo(otherState, TRANSOFORM_Y, null, transformationAmount);
+    }
+
+    private void transformViewTo(TransformState otherState, int transformationFlags,
+            ViewTransformationHelper.CustomTransformation customTransformation,
+            float transformationAmount) {
         // lets animate the positions correctly
+
+        final View transformedView = mTransformedView;
+        boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
+        boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
+        boolean transformScale = transformScale();
+        // lets animate the positions correctly
+        if (transformationAmount == 0.0f) {
+            if (transformX) {
+                float transformationStartX = getTransformationStartX();
+                float start = transformationStartX != UNDEFINED ? transformationStartX
+                        : transformedView.getTranslationX();
+                setTransformationStartX(start);
+            }
+            if (transformY) {
+                float transformationStartY = getTransformationStartY();
+                float start = transformationStartY != UNDEFINED ? transformationStartY
+                        : transformedView.getTranslationY();
+                setTransformationStartY(start);
+            }
+            View otherView = otherState.getTransformedView();
+            if (transformScale && otherView.getWidth() != transformedView.getWidth()) {
+                setTransformationStartScaleX(transformedView.getScaleX());
+                transformedView.setPivotX(0);
+            } else {
+                setTransformationStartScaleX(UNDEFINED);
+            }
+            if (transformScale && otherView.getHeight() != transformedView.getHeight()) {
+                setTransformationStartScaleY(transformedView.getScaleY());
+                transformedView.setPivotY(0);
+            } else {
+                setTransformationStartScaleY(UNDEFINED);
+            }
+            setClippingDeactivated(transformedView, true);
+        }
+        float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                transformationAmount);
         int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
         int[] ownPosition = getLaidOutLocationOnScreen();
-        final View transformedView = mTransformedView;
-        if ((animationFlags & ANIMATE_X) != 0) {
-            transformedView.animate()
-                    .translationX(otherStablePosition[0] - ownPosition[0]);
+        if (transformX) {
+            float endX = otherStablePosition[0] - ownPosition[0];
+            if (customTransformation != null
+                    && customTransformation.customTransformTarget(this, otherState)) {
+                endX = mTransformationEndX;
+            }
+            transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
+                    endX,
+                    interpolatedValue));
         }
-        if ((animationFlags & ANIMATE_Y) != 0) {
-            transformedView.animate()
-                    .translationY(otherStablePosition[1] - ownPosition[1]);
+        if (transformY) {
+            float endY = otherStablePosition[1] - ownPosition[1];
+            if (customTransformation != null
+                    && customTransformation.customTransformTarget(this, otherState)) {
+                endY = mTransformationEndY;
+            }
+            transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
+                    endY,
+                    interpolatedValue));
         }
-        transformedView.animate()
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (endRunnable != null) {
-                            endRunnable.run();
-                        }
-                        setClippingDeactivated(transformedView, false);
-                    }
-                });
-        setClippingDeactivated(transformedView, true);
+        if (transformScale) {
+            View otherView = otherState.getTransformedView();
+            float transformationStartScaleX = getTransformationStartScaleX();
+            if (transformationStartScaleX != UNDEFINED) {
+                transformedView.setScaleX(
+                        NotificationUtils.interpolate(transformationStartScaleX,
+                                (otherView.getWidth() / (float) transformedView.getWidth()),
+                                interpolatedValue));
+            }
+            float transformationStartScaleY = getTransformationStartScaleY();
+            if (transformationStartScaleY != UNDEFINED) {
+                transformedView.setScaleY(
+                        NotificationUtils.interpolate(transformationStartScaleY,
+                                (otherView.getHeight() / (float) transformedView.getHeight()),
+                                interpolatedValue));
+            }
+        }
     }
 
     public static void setClippingDeactivated(final View transformedView, boolean deactivated) {
@@ -281,8 +392,54 @@
         }
     }
 
+    public void setTransformationEndY(float transformationEndY) {
+        mTransformationEndY = transformationEndY;
+    }
+
+    public void setTransformationEndX(float transformationEndX) {
+        mTransformationEndX = transformationEndX;
+    }
+
+    public float getTransformationStartX() {
+        Object tag = mTransformedView.getTag(TRANSFORMATION_START_X);
+        return tag == null ? UNDEFINED : (float) tag;
+    }
+
+    public float getTransformationStartY() {
+        Object tag = mTransformedView.getTag(TRANSFORMATION_START_Y);
+        return tag == null ? UNDEFINED : (float) tag;
+    }
+
+    public float getTransformationStartScaleX() {
+        Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_X);
+        return tag == null ? UNDEFINED : (float) tag;
+    }
+
+    public float getTransformationStartScaleY() {
+        Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_Y);
+        return tag == null ? UNDEFINED : (float) tag;
+    }
+
+    public void setTransformationStartX(float transformationStartX) {
+        mTransformedView.setTag(TRANSFORMATION_START_X, transformationStartX);
+    }
+
+    public void setTransformationStartY(float transformationStartY) {
+        mTransformedView.setTag(TRANSFORMATION_START_Y, transformationStartY);
+    }
+
+    private void setTransformationStartScaleX(float startScaleX) {
+        mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, startScaleX);
+    }
+
+    private void setTransformationStartScaleY(float startScaleY) {
+        mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, startScaleY);
+    }
+
     protected void reset() {
         mTransformedView = null;
+        mTransformationEndX = UNDEFINED;
+        mTransformationEndY = UNDEFINED;
     }
 
     public void setVisible(boolean visible) {
@@ -306,6 +463,15 @@
         mTransformedView.setTranslationY(0);
         mTransformedView.setScaleX(1.0f);
         mTransformedView.setScaleY(1.0f);
+        setClippingDeactivated(mTransformedView, false);
+        abortTransformation();
+    }
+
+    public void abortTransformation() {
+        mTransformedView.setTag(TRANSFORMATION_START_X, UNDEFINED);
+        mTransformedView.setTag(TRANSFORMATION_START_Y, UNDEFINED);
+        mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, UNDEFINED);
+        mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, UNDEFINED);
     }
 
     public static TransformState obtain() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index b742479..6d0fbb15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
+import com.android.systemui.statusbar.policy.NightModeController;
 
 /**
  * Manages which tiles should be automatically added to QS.
@@ -66,12 +67,33 @@
         if (!Prefs.getBoolean(context, Key.QS_WORK_ADDED, false)) {
             host.getManagedProfileController().addCallback(mProfileCallback);
         }
+        if (!Prefs.getBoolean(context, Key.QS_NIGHT_ADDED, false)) {
+            host.getNightModeController().addListener(mNightModeListener);
+        }
     }
 
     public void destroy() {
         // TODO: Remove any registered listeners.
     }
 
+    private final NightModeController.Listener mNightModeListener =
+            new NightModeController.Listener() {
+        @Override
+        public void onNightModeChanged() {
+            mHost.addTile("night");
+            Prefs.putBoolean(mContext, Key.QS_NIGHT_ADDED, true);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mHost.getNightModeController().removeListener(mNightModeListener);
+                }
+            });
+        }
+
+        @Override
+        public void onTwilightAutoChanged() { }
+    };
+
     private final ManagedProfileController.Callback mProfileCallback =
             new ManagedProfileController.Callback() {
                 @Override
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 d25e99b..9b87a8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1144,31 +1144,42 @@
 
         @Override
         public boolean onLongClick(View v) {
-            if (mRecents != null) {
-                int dockSide = WindowManagerProxy.getInstance().getDockSide();
-                if (dockSide == WindowManager.DOCKED_INVALID) {
-                    Point realSize = new Point();
-                    mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
-                            .getRealSize(realSize);
-                    Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
-                    boolean docked = mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
-                            ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
-                            initialBounds);
-                    if (docked) {
-                        MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS);
-                        return true;
-                    }
-                } else {
-                    EventBus.getDefault().send(new UndockingTaskEvent());
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
-                    return true;
-                }
-
+            if (mRecents == null) {
+                return false;
+            }
+            boolean initiallyDocked = WindowManagerProxy.getInstance().getDockSide()
+                    == WindowManager.DOCKED_INVALID;
+            boolean dockedAtEnd = toggleSplitScreenMode();
+            if (dockedAtEnd != initiallyDocked) {
+                int logAction = dockedAtEnd ? MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS
+                        : MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS;
+                MetricsLogger.action(mContext, logAction);
+                return true;
             }
             return false;
         }
     };
 
+    @Override
+    protected boolean toggleSplitScreenMode() {
+        if (mRecents == null) {
+            return false;
+        }
+        int dockSide = WindowManagerProxy.getInstance().getDockSide();
+        if (dockSide == WindowManager.DOCKED_INVALID) {
+            Point realSize = new Point();
+            mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+                    .getRealSize(realSize);
+            Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
+            return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+                    initialBounds);
+        } else {
+            EventBus.getDefault().send(new UndockingTaskEvent());
+            return false;
+        }
+    }
+
     private final View.OnLongClickListener mLongPressHomeListener
             = new View.OnLongClickListener() {
         @Override
@@ -1308,6 +1319,12 @@
                 if (DEBUG) {
                     Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
                 }
+            } else if (mNotificationData.getImportance(notification.getKey())
+                    < NotificationListenerService.Ranking.IMPORTANCE_MAX) {
+                if (DEBUG) {
+                    Log.d(TAG, "No Fullscreen intent: not important enough: "
+                            + notification.getKey());
+                }
             } else {
                 // Stop screensaver if the notification has a full-screen intent.
                 // (like an incoming phone call)
@@ -4089,7 +4106,7 @@
         ExpandableNotificationRow row = null;
         if (expandView instanceof ExpandableNotificationRow) {
             row = (ExpandableNotificationRow) expandView;
-            row.setUserExpanded(true);
+            row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
         }
         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 3bd68a9..ab34768 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -80,13 +80,13 @@
 
         // Original is slightly larger than the mirror, so make sure to use the center for the
         // positioning.
-        int originalX = mInt2Cache[0] + original.getWidth()/2;
-        int originalY = mInt2Cache[1];
+        int originalX = mInt2Cache[0] + original.getWidth() / 2;
+        int originalY = mInt2Cache[1] + original.getHeight() / 2;
         mBrightnessMirror.setTranslationX(0);
         mBrightnessMirror.setTranslationY(0);
         mBrightnessMirror.getLocationInWindow(mInt2Cache);
-        int mirrorX = mInt2Cache[0] + mBrightnessMirror.getWidth()/2;
-        int mirrorY = mInt2Cache[1];
+        int mirrorX = mInt2Cache[0] + mBrightnessMirror.getWidth() / 2;
+        int mirrorY = mInt2Cache[1] + mBrightnessMirror.getHeight() / 2;
         mBrightnessMirror.setTranslationX(originalX - mirrorX);
         mBrightnessMirror.setTranslationY(originalY - mirrorY);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 49aec42..030c8b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 import java.util.ArrayList;
@@ -60,6 +61,8 @@
     private ViewState mGroupOverFlowState;
     private int mRealHeight;
     private int mLayoutDirection = LAYOUT_DIRECTION_UNDEFINED;
+    private boolean mUserLocked;
+    private int mActualHeight;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -281,27 +284,45 @@
      */
     private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
         int intrinsicHeight = mNotificationHeaderHeight;
-        if (mChildrenExpanded) {
-            intrinsicHeight += mNotificatonTopPadding;
-        }
         int visibleChildren = 0;
         int childCount = mChildren.size();
+        boolean firstChild = true;
+        float expandFactor = 0;
+        if (mUserLocked) {
+            expandFactor = getChildExpandFraction();
+        }
         for (int i = 0; i < childCount; i++) {
             if (visibleChildren >= maxAllowedVisibleChildren) {
                 break;
             }
+            if (!firstChild) {
+                if (mUserLocked) {
+                    intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+                            expandFactor);
+                } else {
+                    intrinsicHeight += mChildrenExpanded ? mDividerHeight : mChildPadding;
+                }
+            } else {
+                if (mUserLocked) {
+                    intrinsicHeight += NotificationUtils.interpolate(
+                            0,
+                            mNotificatonTopPadding + mDividerHeight,
+                            expandFactor);
+                } else {
+                    intrinsicHeight += mChildrenExpanded
+                            ? mNotificatonTopPadding + mDividerHeight
+                            : 0;
+                }
+                firstChild = false;
+            }
             ExpandableNotificationRow child = mChildren.get(i);
             intrinsicHeight += child.getIntrinsicHeight();
             visibleChildren++;
         }
-        if (visibleChildren > 0) {
-            if (mChildrenExpanded) {
-                intrinsicHeight += visibleChildren * mDividerHeight;
-            } else {
-                intrinsicHeight += (visibleChildren - 1) * mChildPadding;
-            }
-        }
-        if (!mChildrenExpanded) {
+        if (mUserLocked) {
+            intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f,
+                    expandFactor);
+        } else if (!mChildrenExpanded) {
             intrinsicHeight += mCollapsedBottompadding;
         }
         return intrinsicHeight;
@@ -323,12 +344,28 @@
         int lastVisibleIndex = hasOverflow
                 ? maxAllowedVisibleChildren - 2
                 : maxAllowedVisibleChildren - 1;
+        float expandFactor = 0;
+        if (mUserLocked) {
+            expandFactor = getChildExpandFraction();
+        }
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (!firstChild) {
-                yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
+                if (mUserLocked) {
+                     yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+                             expandFactor);
+                } else {
+                    yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
+                }
             } else {
-                yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+                if (mUserLocked) {
+                    yPosition += NotificationUtils.interpolate(
+                            0,
+                            mNotificatonTopPadding + mDividerHeight,
+                            expandFactor);
+                } else {
+                    yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+                }
                 firstChild = false;
             }
             StackViewState childState = resultState.getViewStateForView(child);
@@ -375,6 +412,7 @@
     public void applyState(StackScrollState state) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
+        float expandFraction = getChildExpandFraction();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -384,7 +422,11 @@
             View divider = mDividers.get(i);
             tmpState.initFrom(divider);
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
-            tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            if (mUserLocked && viewState.alpha != 0) {
+                alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
+            }
+            tmpState.alpha = alpha;
             state.applyViewState(divider, tmpState);
         }
         if (mGroupOverflowContainer != null) {
@@ -407,6 +449,7 @@
             long baseDelay, long duration) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
+        float expandFraction = getChildExpandFraction();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -416,7 +459,11 @@
             View divider = mDividers.get(i);
             tmpState.initFrom(divider);
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
-            tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            if (mUserLocked && viewState.alpha != 0) {
+                alpha = NotificationUtils.interpolate(0, 0.5f, expandFraction);
+            }
+            tmpState.alpha = alpha;
             stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
         }
         if (mGroupOverflowContainer != null) {
@@ -449,7 +496,70 @@
     }
 
     public int getMaxContentHeight() {
-        return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+        int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
+        int visibleChildren = 0;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
+                break;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            float childHeight = child.isExpanded()
+                    ? child.getMaxExpandHeight()
+                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+            maxContentHeight += childHeight;
+            visibleChildren++;
+        }
+        if (visibleChildren > 0) {
+            maxContentHeight += visibleChildren * mDividerHeight;
+        }
+        return maxContentHeight;
+    }
+
+    public void setActualHeight(int actualHeight) {
+        if (!mUserLocked) {
+            return;
+        }
+        mActualHeight = actualHeight;
+        float fraction = getChildExpandFraction();
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            float childHeight = child.isExpanded()
+                    ? child.getMaxExpandHeight()
+                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+            float singleLineHeight = child.getShowingLayout().getMinHeight(
+                    false /* likeGroupExpanded */);
+            child.setActualHeight((int) NotificationUtils.interpolate(singleLineHeight, childHeight,
+                    fraction), false);
+        }
+    }
+
+    public float getChildExpandFraction() {
+        int allChildrenVisibleHeight = getChildrenExpandStartHeight();
+        int maxContentHeight = getMaxContentHeight();
+        float factor = (mActualHeight - allChildrenVisibleHeight)
+                / (float) (maxContentHeight - allChildrenVisibleHeight);
+        return Math.max(0.0f, Math.min(1.0f, factor));
+    }
+
+    private int getChildrenExpandStartHeight() {
+        int intrinsicHeight = mNotificationHeaderHeight;
+        int visibleChildren = 0;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            if (visibleChildren >= NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED) {
+                break;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            intrinsicHeight += child.getMinHeight();
+            visibleChildren++;
+        }
+        if (visibleChildren > 0) {
+            intrinsicHeight += (visibleChildren - 1) * mChildPadding;
+        }
+        intrinsicHeight += mCollapsedBottompadding;
+        return intrinsicHeight;
     }
 
     public int getMinHeight() {
@@ -477,4 +587,13 @@
             mDividers.set(i, divider);
         }
     }
+
+    public void setUserLocked(boolean userLocked) {
+        mUserLocked = userLocked;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            child.setUserLocked(userLocked);
+        }
+    }
 }
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 fe06c3a..340ebb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.StackScrollerDecorView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -538,7 +539,7 @@
             ExpandableView child = (ExpandableView) getChildAt(i);
             if (mChildrenToAddAnimated.contains(child)) {
                 int startingPosition = getPositionInLinearLayout(child);
-                int padding = child.needsIncreasedPadding()
+                int padding = child.getIncreasedPaddingAmount() == 1.0f
                         ? mIncreasedPaddingBetweenElements :
                         mPaddingBetweenElements;
                 int childHeight = getIntrinsicHeight(child) + padding;
@@ -1531,18 +1532,18 @@
 
     private void updateContentHeight() {
         int height = 0;
-        boolean previousNeedsIncreasedPaddings = false;
+        float previousIncreasedAmount = 0.0f;
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
             if (expandableView.getVisibility() != View.GONE) {
-                boolean needsIncreasedPaddings = expandableView.needsIncreasedPadding();
+                float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
                 if (height != 0) {
-                    int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
-                            ? mIncreasedPaddingBetweenElements
-                            : mPaddingBetweenElements;
-                    height += padding;
+                    height += (int) NotificationUtils.interpolate(
+                            mPaddingBetweenElements,
+                            mIncreasedPaddingBetweenElements,
+                            Math.max(previousIncreasedAmount, increasedPaddingAmount));
                 }
-                previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+                previousIncreasedAmount = increasedPaddingAmount;
                 height += expandableView.getIntrinsicHeight();
             }
         }
@@ -1813,8 +1814,12 @@
                 // it will be set once we reach the boundary
                 mMaxOverScroll = 0.0f;
             }
+            int minScrollY = Math.max(0, scrollRange);
+            if (mExpandedInThisMotion) {
+                minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
+            }
             mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
-                    Math.max(0, scrollRange), 0, Integer.MAX_VALUE / 2);
+                    minScrollY, 0, mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
 
             postInvalidateOnAnimation();
         }
@@ -2097,9 +2102,10 @@
      */
     private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
         int startingPosition = getPositionInLinearLayout(removedChild);
-        int padding = removedChild.needsIncreasedPadding()
-                ? mIncreasedPaddingBetweenElements :
-                mPaddingBetweenElements;
+        int padding = (int) NotificationUtils.interpolate(
+                mPaddingBetweenElements,
+                mIncreasedPaddingBetweenElements,
+                removedChild.getIncreasedPaddingAmount());
         int childHeight = getIntrinsicHeight(removedChild) + padding;
         int endPosition = startingPosition + childHeight;
         if (endPosition <= mOwnScrollY) {
@@ -2123,19 +2129,19 @@
 
     private int getPositionInLinearLayout(View requestedChild) {
         int position = 0;
-        boolean previousNeedsIncreasedPaddings = false;
+        float previousIncreasedAmount = 0.0f;
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView child = (ExpandableView) getChildAt(i);
             boolean notGone = child.getVisibility() != View.GONE;
             if (notGone) {
-                boolean needsIncreasedPaddings = child.needsIncreasedPadding();
+                float increasedPaddingAmount = child.getIncreasedPaddingAmount();
                 if (position != 0) {
-                    int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
-                            ? mIncreasedPaddingBetweenElements :
-                            mPaddingBetweenElements;
-                    position += padding;
+                    position += (int) NotificationUtils.interpolate(
+                            mPaddingBetweenElements,
+                            mIncreasedPaddingBetweenElements,
+                            Math.max(previousIncreasedAmount, increasedPaddingAmount));
                 }
-                previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+                previousIncreasedAmount = increasedPaddingAmount;
             }
             if (child == requestedChild) {
                 return position;
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 d78d626..eea923f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -25,9 +25,10 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -297,18 +298,22 @@
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
         state.visibleChildren.ensureCapacity(childCount);
-        state.increasedPaddingSet.clear();
+        state.increasedPaddingMap.clear();
         int notGoneIndex = 0;
         ExpandableView lastView = null;
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = (ExpandableView) hostView.getChildAt(i);
             if (v.getVisibility() != View.GONE) {
                 notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
-                boolean needsIncreasedPadding = v.needsIncreasedPadding();
-                if (needsIncreasedPadding) {
-                    state.increasedPaddingSet.add(v);
+                float increasedPadding = v.getIncreasedPaddingAmount();
+                if (increasedPadding != 0.0f) {
+                    state.increasedPaddingMap.put(v, increasedPadding);
                     if (lastView != null) {
-                        state.increasedPaddingSet.add(lastView);
+                        Float prevValue = state.increasedPaddingMap.get(lastView);
+                        float newValue = prevValue != null
+                                ? Math.max(prevValue, increasedPadding)
+                                : increasedPadding;
+                        state.increasedPaddingMap.put(lastView, newValue);
                     }
                 }
                 if (v instanceof ExpandableNotificationRow) {
@@ -423,9 +428,12 @@
 
     private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
             ExpandableView child) {
-        return algorithmState.increasedPaddingSet.contains(child)
-                ? mIncreasedPaddingBetweenElements
-                : mPaddingBetweenElements;
+        Float paddingValue = algorithmState.increasedPaddingMap.get(child);
+        return paddingValue == null
+                ? mPaddingBetweenElements
+                : (int) NotificationUtils.interpolate(mPaddingBetweenElements,
+                        mIncreasedPaddingBetweenElements,
+                        paddingValue);
     }
 
     private void updateHeadsUpStates(StackScrollState resultState,
@@ -765,9 +773,10 @@
         public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
 
         /**
-         * The children from the host that need an increased padding after them.
+         * The children from the host that need an increased padding after them. A value of 0 means
+         * no increased padding, a value of 1 means full padding.
          */
-        public final HashSet<ExpandableView> increasedPaddingSet = new HashSet<>();
+        public final HashMap<ExpandableView, Float> increasedPaddingMap = new HashMap<>();
     }
 
 }
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 110258c..0ed6ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -116,6 +116,11 @@
     }
 
     @Override
+    protected boolean toggleSplitScreenMode() {
+        return false;
+    }
+
+    @Override
     public void maybeEscalateHeadsUp() {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index ec49256..0925638 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -424,9 +424,7 @@
         }
         Intent intent = new Intent(mContext, PipMenuActivity.class);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchStackId(PINNED_STACK_ID);
-        mContext.startActivity(intent, options.toBundle());
+        mContext.startActivity(intent);
     }
 
     public void addListener(Listener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index 7e229d4..a392bec 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -33,33 +33,77 @@
     private final PipManager mPipManager = PipManager.getInstance();
     private MediaController mMediaController;
 
+    private View mFullButtonView;
+    private View mFullDescriptionView;
+    private View mPlayPauseButtonView;
+    private View mPlayPauseDescriptionView;
+    private View mCloseButtonView;
+    private View mCloseDescriptionView;
+
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_menu);
         mPipManager.addListener(this);
-        findViewById(R.id.full).setOnClickListener(new View.OnClickListener() {
+        mFullButtonView = findViewById(R.id.full);
+        mFullDescriptionView = findViewById(R.id.full_desc);
+        mFullButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 mPipManager.movePipToFullscreen();
+                finish();
             }
         });
-        findViewById(R.id.exit).setOnClickListener(new View.OnClickListener() {
+        mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+            }
+        });
+
+        mPlayPauseButtonView = findViewById(R.id.play_pause);
+        mPlayPauseDescriptionView = findViewById(R.id.play_pause_desc);
+        mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // TODO: Implement play/pause.
+            }
+        });
+        mPlayPauseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mPlayPauseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+            }
+        });
+
+        mCloseButtonView = findViewById(R.id.close);
+        mCloseDescriptionView = findViewById(R.id.close_desc);
+        mCloseButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 mPipManager.closePip();
                 finish();
             }
         });
-        findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
+        mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
-            public void onClick(View v) {
-                mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
-                finish();
+            public void onFocusChange(View v, boolean hasFocus) {
+                mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
             }
         });
     }
 
+    private void restorePipAndFinish() {
+        mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
+        finish();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        restorePipAndFinish();
+    }
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
@@ -70,8 +114,7 @@
 
     @Override
     public void onBackPressed() {
-        mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
-        finish();
+        restorePipAndFinish();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index 6f71c92..e5c07d2 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -17,9 +17,11 @@
 package com.android.systemui.tv.pip;
 
 import android.app.Activity;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup.LayoutParams;
 
 import com.android.systemui.R;
 
@@ -33,6 +35,8 @@
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_onboarding);
+        View pipOnboardingView = findViewById(R.id.pip_onboarding);
+        View pipOutlineView = findViewById(R.id.pip_outline);
         mPipManager.addListener(this);
         findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
             @Override
@@ -40,6 +44,20 @@
                 finish();
             }
         });
+
+        int pipOutlineSpace = getResources().getDimensionPixelSize(R.dimen.tv_pip_bounds_space);
+        int screenWidth = getResources().getDisplayMetrics().widthPixels;
+        Rect pipBounds = mPipManager.getPipBounds();
+        pipOnboardingView.setPadding(
+                pipBounds.left - pipOutlineSpace,
+                pipBounds.top - pipOutlineSpace,
+                screenWidth - pipBounds.right - pipOutlineSpace, 0);
+
+        // Set width and height for outline view to enclose the PIP.
+        LayoutParams lp = pipOutlineView.getLayoutParams();
+        lp.width = pipBounds.width() + pipOutlineSpace * 2;
+        lp.height = pipBounds.height() + pipOutlineSpace * 2;
+        pipOutlineView.setLayoutParams(lp);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 6f24691..cfeab6d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -19,8 +19,8 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
-
 import android.view.View;
+
 import com.android.systemui.R;
 
 /**
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 964688b..c9c5805 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -29,9 +29,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     $(call all-Iaidl-files-under, src) \
-    $(call all-java-files-under, ../src) \
-    $(call all-proto-files-under, ../src) \
-    src/com/android/systemui/EventLogTags.logtags
+    $(call all-java-files-under, ../src)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
     frameworks/support/v7/preference/res \
@@ -53,7 +51,8 @@
     android-support-v7-preference \
     android-support-v7-appcompat \
     android-support-v14-preference \
-    android-support-v17-leanback
+    android-support-v17-leanback \
+    SystemUI-proto-tags
 
 # sign this with platform cert, so this test is allowed to inject key events into
 # UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags
deleted file mode 120000
index 2f243d7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/EventLogTags.logtags
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../src/com/android/systemui/EventLogTags.logtags
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index cd026a8..471feef 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -441,5 +441,42 @@
 
     // Tuner: Change do not disturb volume buttons shortcut.
     ACTION_TUNER_DO_NOT_DISTURB_VOLUME_SHORTCUT = 315;
+
+    // Logs the action the user takes when an app crashed.
+    ACTION_APP_CRASH = 316;
+
+    // Logs the action the user takes when an app ANR'd.
+    ACTION_APP_ANR = 317;
+
+    // Logged when a user double taps the overview button to launch the previous task
+    OVERVIEW_LAUNCH_PREVIOUS_TASK = 318;
+
+    // Logged when we execute an app transition. This indicates the total delay from startActivity
+    // until the app transition is starting to animate, in milliseconds.
+    APP_TRANSITION_DELAY_MS = 319;
+
+    // Logged when we execute an app transition. This indicates the reason why the transition
+    // started. Must be one of ActivityManagerInternal.APP_TRANSITION_* reasons.
+    APP_TRANSITION_REASON = 320;
+
+    // Logged when we execute an app transition and we drew a starting window. This indicates the
+    // delay from startActivity until the starting window was drawn.
+    APP_TRANSITION_STARTING_WINDOW_DELAY_MS = 321;
+
+    // Logged when we execute an app transition and all windows of the app got drawn. This indicates
+    // the delay from startActivity until all windows have been drawn.
+    APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS = 322;
+
+    // Logged when we execute an app transition. This indicates the component name of the current
+    // transition.
+    APP_TRANSITION_COMPONENT_NAME = 323;
+
+    // Logged when we execute an app transition. This indicates whether the process was already
+    // running.
+    APP_TRANSITION_PROCESS_RUNNING = 324;
+
+    // Logged when we execute an app transition. This indicates the device uptime in seconds when
+    // the transition was executed.
+    APP_TRANSITION_DEVICE_UPTIME_SECONDS = 325;
   }
 }
diff --git a/services/accessibility/Android.mk b/services/accessibility/Android.mk
index d98fc28..ce89aa7 100644
--- a/services/accessibility/Android.mk
+++ b/services/accessibility/Android.mk
@@ -7,4 +7,6 @@
 LOCAL_SRC_FILES += \
       $(call all-java-files-under,java)
 
+LOCAL_JAVA_LIBRARIES := services.core
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0dbc5be..2b682c5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -94,6 +94,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.LocalServices;
 
+import com.android.server.statusbar.StatusBarManagerInternal;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.FileDescriptor;
@@ -2778,6 +2779,9 @@
                     case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
                         showGlobalActions();
                     } return true;
+                    case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
+                        toggleSplitScreen();
+                    } return true;
                 }
                 return false;
             } finally {
@@ -3249,6 +3253,10 @@
             mWindowManagerService.showGlobalActions();
         }
 
+        private void toggleSplitScreen() {
+            LocalServices.getService(StatusBarManagerInternal.class).toggleSplitScreen();
+        }
+
         private IAccessibilityInteractionConnection getConnectionLocked(int windowId) {
             if (DEBUG) {
                 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
@@ -3465,11 +3473,14 @@
                 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
                 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
                 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
-                case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
-                case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
+                case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
                     return AccessibilityWindowInfo.TYPE_SYSTEM;
                 }
 
+                case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: {
+                    return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER;
+                }
+
                 case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY: {
                     return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
                 }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e32d89c..f1a9c44 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -171,6 +171,10 @@
     static final boolean MORE_DEBUG = false;
     static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true;
 
+    // File containing backup-enabled state.  Contains a single byte;
+    // nonzero == enabled.  File missing or contains a zero byte == disabled.
+    static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
     // System-private key used for backing up an app's widget state.  Must
     // begin with U+FFxx by convention (we reserve all keys starting
     // with U+FF00 or higher for system use).
@@ -354,11 +358,30 @@
             if (userId == UserHandle.USER_SYSTEM) {
                 sInstance.initialize(userId);
 
-                ContentResolver r = sInstance.mContext.getContentResolver();
-                boolean areEnabled = Settings.Secure.getIntForUser(r,
-                        Settings.Secure.BACKUP_ENABLED, 0, userId) != 0;
+                // Migrate legacy setting
+                if (!backupSettingMigrated(userId)) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Backup enable apparently not migrated");
+                    }
+                    final ContentResolver r = sInstance.mContext.getContentResolver();
+                    final int enableState = Settings.Secure.getIntForUser(r,
+                            Settings.Secure.BACKUP_ENABLED, -1, userId);
+                    if (enableState >= 0) {
+                        if (DEBUG) {
+                            Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+                        }
+                        writeBackupEnableState(enableState != 0, userId);
+                        Settings.Secure.putStringForUser(r,
+                                Settings.Secure.BACKUP_ENABLED, null, userId);
+                    } else {
+                        if (DEBUG) {
+                            Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+                        }
+                    }
+                }
+
                 try {
-                    sInstance.setBackupEnabled(areEnabled);
+                    sInstance.setBackupEnabled(readBackupEnableState(userId));
                 } catch (RemoteException e) {
                     // can't happen; it's a local object
                 }
@@ -9314,6 +9337,58 @@
         }
     }
 
+    private static boolean backupSettingMigrated(int userId) {
+        File base = new File(Environment.getDataDirectory(), "backup");
+        File enableFile = new File(base, BACKUP_ENABLE_FILE);
+        return enableFile.exists();
+    }
+
+    private static boolean readBackupEnableState(int userId) {
+        File base = new File(Environment.getDataDirectory(), "backup");
+        File enableFile = new File(base, BACKUP_ENABLE_FILE);
+        if (enableFile.exists()) {
+            try (FileInputStream fin = new FileInputStream(enableFile)) {
+                int state = fin.read();
+                return state != 0;
+            } catch (IOException e) {
+                // can't read the file; fall through to assume disabled
+                Slog.e(TAG, "Cannot read enable state; assuming disabled");
+            }
+        } else {
+            if (DEBUG) {
+                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
+            }
+        }
+        return false;
+    }
+
+    private static void writeBackupEnableState(boolean enable, int userId) {
+        File base = new File(Environment.getDataDirectory(), "backup");
+        File enableFile = new File(base, BACKUP_ENABLE_FILE);
+        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
+        FileOutputStream fout = null;
+        try {
+            fout = new FileOutputStream(stage);
+            fout.write(enable ? 1 : 0);
+            fout.close();
+            stage.renameTo(enableFile);
+            // will be synced immediately by the try-with-resources call to close()
+        } catch (IOException|RuntimeException e) {
+            // Whoops; looks like we're doomed.  Roll everything out, disabled,
+            // including the legacy state.
+            Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
+                    + e.getMessage());
+
+            final ContentResolver r = sInstance.mContext.getContentResolver();
+            Settings.Secure.putStringForUser(r,
+                    Settings.Secure.BACKUP_ENABLED, null, userId);
+            enableFile.delete();
+            stage.delete();
+        } finally {
+            IoUtils.closeQuietly(fout);
+        }
+    }
+
     // Enable/disable backups
     public void setBackupEnabled(boolean enable) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -9325,8 +9400,7 @@
         try {
             boolean wasEnabled = mEnabled;
             synchronized (this) {
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
+                writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
                 mEnabled = enable;
             }
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index d14364d..caeb0c6 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -660,12 +660,7 @@
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
-            synchronized (accounts.cacheLock) {
-                if (!accountExistsCacheLocked(accounts, account)) {
-                    return null;
-                }
-                return readUserDataInternalLocked(accounts, account, key);
-            }
+            return readUserDataInternal(accounts, account, key);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -1717,58 +1712,44 @@
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
-            synchronized (accounts.cacheLock) {
-                if (!accountExistsCacheLocked(accounts, account)) {
-                    return;
-                }
-                setUserdataInternalLocked(accounts, account, key, value);
-            }
+            setUserdataInternal(accounts, account, key, value);
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
-        if (accounts.accountCache.containsKey(account.type)) {
-            for (Account acc : accounts.accountCache.get(account.type)) {
-                if (acc.name.equals(account.name)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
+    private void setUserdataInternal(UserAccounts accounts, Account account, String key,
             String value) {
         if (account == null || key == null) {
             return;
         }
-        final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
-            long accountId = getAccountIdLocked(db, account);
-            if (accountId < 0) {
-                return;
-            }
-            long extrasId = getExtrasIdLocked(db, accountId, key);
-            if (extrasId < 0) {
-                extrasId = insertExtraLocked(db, accountId, key, value);
-                if (extrasId < 0) {
+        synchronized (accounts.cacheLock) {
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            db.beginTransaction();
+            try {
+                long accountId = getAccountIdLocked(db, account);
+                if (accountId < 0) {
                     return;
                 }
-            } else {
-                ContentValues values = new ContentValues();
-                values.put(EXTRAS_VALUE, value);
-                if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
-                    return;
-                }
+                long extrasId = getExtrasIdLocked(db, accountId, key);
+                if (extrasId < 0 ) {
+                    extrasId = insertExtraLocked(db, accountId, key, value);
+                    if (extrasId < 0) {
+                        return;
+                    }
+                } else {
+                    ContentValues values = new ContentValues();
+                    values.put(EXTRAS_VALUE, value);
+                    if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+                        return;
+                    }
 
+                }
+                writeUserDataIntoCacheLocked(accounts, db, account, key, value);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
             }
-            writeUserDataIntoCacheLocked(accounts, db, account, key, value);
-            db.setTransactionSuccessful();
-        } finally {
-            db.endTransaction();
         }
     }
 
@@ -4803,16 +4784,17 @@
         }
     }
 
-    protected String readUserDataInternalLocked(
-            UserAccounts accounts, Account account, String key) {
-        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
-        if (userDataForAccount == null) {
-            // need to populate the cache for this account
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
-            accounts.userDataCache.put(account, userDataForAccount);
+    protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
+        synchronized (accounts.cacheLock) {
+            HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
+            if (userDataForAccount == null) {
+                // need to populate the cache for this account
+                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+                userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
+                accounts.userDataCache.put(account, userDataForAccount);
+            }
+            return userDataForAccount.get(key);
         }
-        return userDataForAccount.get(key);
     }
 
     protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e3a0b5c..b594ae8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1371,7 +1371,6 @@
     int mProcessLimitOverride = -1;
 
     WindowManagerService mWindowManager;
-
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -2487,7 +2486,6 @@
         mActivityStarter = new ActivityStarter(this, mStackSupervisor);
         mRecentTasks = new RecentTasks(this, mStackSupervisor);
 
-
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
@@ -13395,6 +13393,14 @@
     }
 
     @Override
+    public int getMemoryTrimLevel() {
+        enforceNotIsolatedCaller("getMyMemoryState");
+        synchronized (this) {
+            return mLastMemoryLevel;
+        }
+    }
+
+    @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
         (new ActivityManagerShellCommand(this, false)).exec(
@@ -20757,6 +20763,20 @@
                         voiceSession, voiceInteractor);
             }
         }
+
+        @Override
+        public void notifyStartingWindowDrawn() {
+            synchronized (ActivityManagerService.this) {
+                mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn();
+            }
+        }
+
+        @Override
+        public void notifyAppTransitionStarting(int reason) {
+            synchronized (ActivityManagerService.this) {
+                mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index ffb2fc4..0e6dd28 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -7,11 +7,13 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager.StackId;
 import android.content.Context;
 import android.os.SystemClock;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
 
 /**
  * Handles logging into Tron.
@@ -24,6 +26,8 @@
     private static final int WINDOW_STATE_FREEFORM = 2;
     private static final int WINDOW_STATE_INVALID = -1;
 
+    private static final long INVALID_START_TIME = -1;
+
     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
     // time we log.
     private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
@@ -34,6 +38,11 @@
     private final ActivityStackSupervisor mSupervisor;
     private final Context mContext;
 
+    private long mCurrentTransitionStartTime = INVALID_START_TIME;
+    private boolean mLoggedWindowsDrawn;
+    private boolean mLoggedStartingWindowDrawn;
+    private boolean mLoggedTransitionStarting;
+
     ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) {
         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
         mSupervisor = supervisor;
@@ -73,4 +82,101 @@
             throw new IllegalStateException("Unknown stack=" + stack);
         }
     }
+
+    /**
+     * Notifies the tracker at the earliest possible point when we are starting to launch an
+     * activity.
+     */
+    void notifyActivityLaunching() {
+        mCurrentTransitionStartTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Notifies the tracker the the activity is actually launching.
+     *
+     * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
+     *                   launch
+     * @param componentName the component name of the activity being launched
+     * @param processRunning whether the process that will contains the activity is already running
+     */
+    void notifyActivityLaunched(int resultCode, @Nullable String componentName,
+            boolean processRunning) {
+
+        if (resultCode < 0 || componentName == null) {
+
+            // Failed to launch, don't track anything.
+            reset();
+            return;
+        }
+
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME,
+                componentName);
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING,
+                processRunning);
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+                (int) (SystemClock.uptimeMillis() / 1000));
+    }
+
+    /**
+     * Notifies the tracker that all windows of the app have been drawn.
+     */
+    void notifyWindowsDrawn() {
+        if (!isTransitionActive() || mLoggedWindowsDrawn) {
+            return;
+        }
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS,
+                calculateCurrentDelay());
+        mLoggedWindowsDrawn = true;
+        if (mLoggedTransitionStarting) {
+            reset();
+        }
+    }
+
+    /**
+     * Notifies the tracker that the starting window was drawn.
+     */
+    void notifyStartingWindowDrawn() {
+        if (!isTransitionActive() || mLoggedStartingWindowDrawn) {
+            return;
+        }
+        mLoggedStartingWindowDrawn = true;
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+                calculateCurrentDelay());
+    }
+
+    /**
+     * Notifies the tracker that the app transition is starting.
+     *
+     * @param reason The reason why we started it. Must be on of
+     *               ActivityManagerInternal.APP_TRANSITION_* reasons.
+     */
+    void notifyTransitionStarting(int reason) {
+        if (!isTransitionActive() || mLoggedTransitionStarting) {
+            return;
+        }
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason);
+        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS,
+                calculateCurrentDelay());
+        mLoggedTransitionStarting = true;
+        if (mLoggedWindowsDrawn) {
+            reset();
+        }
+    }
+
+    private boolean isTransitionActive() {
+        return mCurrentTransitionStartTime != INVALID_START_TIME;
+    }
+
+    private void reset() {
+        mCurrentTransitionStartTime = INVALID_START_TIME;
+        mLoggedWindowsDrawn = false;
+        mLoggedTransitionStarting = false;
+        mLoggedStartingWindowDrawn = false;
+    }
+
+    private int calculateCurrentDelay() {
+
+        // Shouldn't take more than 25 days to launch an app, so int is fine here.
+        return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 90a7da9..e2ea1f1 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -321,6 +321,7 @@
                 pw.print(" visible="); pw.print(visible);
                 pw.print(" sleeping="); pw.print(sleeping);
                 pw.print(" idle="); pw.println(idle);
+                pw.print(" mStartingWindowShown="); pw.println(mStartingWindowShown);
         pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
                 pw.print(" noDisplay="); pw.print(noDisplay);
                 pw.print(" immersive="); pw.print(immersive);
@@ -1166,6 +1167,7 @@
     }
 
     void windowsDrawnLocked() {
+        mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn();
         if (displayStartTime != 0) {
             reportLaunchTimeLocked(SystemClock.uptimeMillis());
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index acde10f..ec37667 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2263,6 +2263,7 @@
                             mService.compatibilityInfoForPackageLocked(next.info.applicationInfo),
                             next.nonLocalizedLabel, next.labelRes, next.icon, next.logo,
                             next.windowFlags, null, true);
+                    next.mStartingWindowShown = true;
                 }
                 mStackSupervisor.startSpecificActivityLocked(next, true, false);
                 if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2295,6 +2296,7 @@
                             next.nonLocalizedLabel,
                             next.labelRes, next.icon, next.logo, next.windowFlags,
                             null, true);
+                    next.mStartingWindowShown = true;
                 }
                 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index af5f6b3..20f8285 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -406,7 +406,7 @@
     // Whether tasks have moved and we need to rank the tasks before next OOM scoring
     private boolean mTaskLayersChanged = true;
 
-    private final ActivityMetricsLogger mActivityMetricsLogger;
+    final ActivityMetricsLogger mActivityMetricsLogger;
 
     private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 9c7eb95..0181640 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -593,6 +593,7 @@
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
+        mSupervisor.mActivityMetricsLogger.notifyActivityLaunching();
         boolean componentSpecified = intent.getComponent() != null;
 
         // Save a copy in case ephemeral needs it
@@ -722,11 +723,13 @@
                 }
             }
 
+            final ActivityRecord[] outRecord = new ActivityRecord[1];
             int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                     aInfo, rInfo, voiceSession, voiceInteractor,
                     resultTo, resultWho, requestCode, callingPid,
                     callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                    options, ignoreTargetSecurity, componentSpecified, null, container, inTask);
+                    options, ignoreTargetSecurity, componentSpecified, outRecord, container,
+                    inTask);
 
             Binder.restoreCallingIdentity(origId);
 
@@ -773,6 +776,13 @@
                 }
             }
 
+            final String componentName = outRecord[0] != null ? outRecord[0].shortComponentName
+                    : null;
+            final boolean processRunning = outRecord[0] != null &&
+                    mService.mProcessNames.get(outRecord[0].processName,
+                            outRecord[0].appInfo.uid) != null;
+            mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, componentName,
+                    processRunning);
             return res;
         }
     }
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index b746a4b..86cdbcc 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -37,6 +37,7 @@
 import static com.android.server.am.ActivityManagerService.IS_USER_BUILD;
 
 final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
+
     private final ActivityManagerService mService;
     private final AppErrorResult mResult;
     private final ProcessRecord mProc;
@@ -44,12 +45,17 @@
 
     private CharSequence mName;
 
+    static int CANT_SHOW = -1;
+    static int BACKGROUND_USER = -2;
+    static int ALREADY_SHOWING = -3;
+
     // Event 'what' codes
     static final int FORCE_QUIT = 1;
     static final int FORCE_QUIT_AND_REPORT = 2;
     static final int RESTART = 3;
     static final int RESET = 4;
     static final int MUTE = 5;
+    static final int TIMEOUT = 6;
 
     // 5-minute timeout, then we automatically dismiss the crash dialog
     static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -89,7 +95,7 @@
 
         // After the timeout, pretend the user clicked the quit button
         mHandler.sendMessageDelayed(
-                mHandler.obtainMessage(FORCE_QUIT),
+                mHandler.obtainMessage(TIMEOUT),
                 DISMISS_TIMEOUT);
     }
 
@@ -132,7 +138,7 @@
             mResult.set(result);
 
             // Make sure we don't have time timeout still hanging around.
-            removeMessages(FORCE_QUIT);
+            removeMessages(TIMEOUT);
 
             dismiss();
         }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 190e9e1..055935d 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -17,6 +17,8 @@
 package com.android.server.am;
 
 import com.android.internal.app.ProcessMap;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.server.Watchdog;
 
@@ -403,6 +405,10 @@
         Intent appErrorIntent = null;
         final long ident = Binder.clearCallingIdentity();
         try {
+            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
+            if (res == AppErrorDialog.TIMEOUT) {
+                res = AppErrorDialog.FORCE_QUIT;
+            }
             if (res == AppErrorDialog.RESET) {
                 String[] packageList = r.getPackageList();
                 if (packageList != null) {
@@ -697,7 +703,7 @@
             if (proc != null && proc.crashDialog != null) {
                 Slog.e(TAG, "App already has crash dialog: " + proc);
                 if (res != null) {
-                    res.set(0);
+                    res.set(AppErrorDialog.ALREADY_SHOWING);
                 }
                 return;
             }
@@ -710,7 +716,7 @@
             if (isBackground && !showBackground) {
                 Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
                 if (res != null) {
-                    res.set(0);
+                    res.set(AppErrorDialog.BACKGROUND_USER);
                 }
                 return;
             }
@@ -724,7 +730,7 @@
                 // The device is asleep, so just pretend that the user
                 // saw a crash dialog and hit "force quit".
                 if (res != null) {
-                    res.set(0);
+                    res.set(AppErrorDialog.CANT_SHOW);
                 }
             }
         }
@@ -920,6 +926,8 @@
             ProcessRecord proc = (ProcessRecord)data.get("app");
             if (proc != null && proc.anrDialog != null) {
                 Slog.e(TAG, "App already has anr dialog: " + proc);
+                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
+                        AppNotRespondingDialog.ALREADY_SHOWING);
                 return;
             }
 
@@ -939,6 +947,8 @@
                 d.show();
                 proc.anrDialog = d;
             } else {
+                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
+                        AppNotRespondingDialog.CANT_SHOW);
                 // Just kill the app if there is no dialog to be shown.
                 mService.killAppAtUsersRequest(proc, null);
             }
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 9875887..6d1d9f3 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -16,6 +16,9 @@
 
 package com.android.server.am;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
+
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -41,6 +44,9 @@
     static final int WAIT = 2;
     static final int WAIT_AND_REPORT = 3;
 
+    public static final int CANT_SHOW = -1;
+    public static final int ALREADY_SHOWING = -2;
+
     private final ActivityManagerService mService;
     private final ProcessRecord mProc;
 
@@ -132,6 +138,10 @@
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             Intent appErrorIntent = null;
+
+            MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_APP_ANR,
+                    msg.what);
+
             switch (msg.what) {
                 case FORCE_CLOSE:
                     // Kill the application.
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index e9d684a..5d81dae 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -505,7 +505,7 @@
     }
 
     public boolean hasEnrolledFingerprints(int userId) {
-        if (userId != Binder.getCallingUid()) {
+        if (userId != UserHandle.getCallingUserId()) {
             checkPermission(INTERACT_ACROSS_USERS);
         }
         return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index f1d7b36..bd888d8 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -19,6 +19,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
@@ -52,8 +53,11 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
-import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.app.ProcessStats;
 import com.android.server.DeviceIdleController;
 import com.android.server.LocalServices;
 import com.android.server.job.JobStore.JobStatusFunctor;
@@ -66,6 +70,8 @@
 import com.android.server.job.controllers.StateController;
 import com.android.server.job.controllers.TimeController;
 
+import libcore.util.EmptyArray;
+
 /**
  * Responsible for taking jobs representing work to be performed by a client app, and determining
  * based on the criteria specified when that job should be run against the client application's
@@ -83,9 +89,8 @@
     static final String TAG = "JobSchedulerService";
     public static final boolean DEBUG = false;
 
-    /** The number of concurrent jobs we run at one time. */
-    private static final int MAX_JOB_CONTEXTS_COUNT
-            = ActivityManager.isLowRamDeviceStatic() ? 3 : 6;
+    /** The maximum number of concurrent jobs we run at one time. */
+    private static final int MAX_JOB_CONTEXTS_COUNT = 8;
     /** Enforce a per-app limit on scheduled jobs? */
     private static final boolean ENFORCE_MAX_JOBS = false;
     /** The maximum number of jobs that we allow an unprivileged app to schedule */
@@ -142,7 +147,7 @@
      */
     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
 
-    final ArrayList<Integer> mStartedUsers = new ArrayList<>();
+    int[] mStartedUsers = EmptyArray.INT;
 
     final JobHandler mHandler;
     final JobSchedulerStub mJobSchedulerStub;
@@ -167,9 +172,33 @@
     boolean mReportedActive;
 
     /**
+     * Current limit on the number of concurrent JobServiceContext entries we want to
+     * keep actively running a job.
+     */
+    int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+
+    /**
      * Which uids are currently in the foreground.
      */
-    final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+    final SparseIntArray mUidPriorityOverride = new SparseIntArray();
+
+    // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
+
+    /**
+     * This array essentially stores the state of mActiveServices array.
+     * The ith index stores the job present on the ith JobServiceContext.
+     * We manipulate this array until we arrive at what jobs should be running on
+     * what JobServiceContext.
+     */
+    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+    /**
+     * Indicates whether we need to act on this jobContext id
+     */
+    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+    /**
+     * The uid whose jobs we would like to assign to a context.
+     */
+    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
 
     /**
      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
@@ -228,14 +257,20 @@
 
     @Override
     public void onStartUser(int userHandle) {
-        mStartedUsers.add(userHandle);
+        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
+        // Let's kick any outstanding jobs for this user.
+        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+    }
+
+    @Override
+    public void onUnlockUser(int userHandle) {
         // Let's kick any outstanding jobs for this user.
         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
     }
 
     @Override
     public void onStopUser(int userHandle) {
-        mStartedUsers.remove(Integer.valueOf(userHandle));
+        mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
     }
 
     /**
@@ -366,19 +401,15 @@
 
     void updateUidState(int uid, int procState) {
         synchronized (mLock) {
-            boolean foreground = procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-            boolean changed = false;
-            if (foreground) {
-                if (!mForegroundUids.get(uid)) {
-                    changed = true;
-                    mForegroundUids.put(uid, true);
-                }
+            if (procState == ActivityManager.PROCESS_STATE_TOP) {
+                // Only use this if we are exactly the top app.  All others can live
+                // with just the foreground priority.  This means that persistent processes
+                // can never be the top app priority...  that is fine.
+                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
+            } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
             } else {
-                int index = mForegroundUids.indexOfKey(uid);
-                if (index >= 0) {
-                    mForegroundUids.removeAt(index);
-                    changed = true;
-                }
+                mUidPriorityOverride.delete(uid);
             }
         }
     }
@@ -936,18 +967,31 @@
          *      - It's not pending.
          *      - It's not already running on a JSC.
          *      - The user that requested the job is running.
+         *      - The component is enabled and runnable.
          */
         private boolean isReadyToBeExecutedLocked(JobStatus job) {
             final boolean jobReady = job.isReady();
             final boolean jobPending = mPendingJobs.contains(job);
             final boolean jobActive = isCurrentlyActiveLocked(job);
-            final boolean userRunning = mStartedUsers.contains(job.getUserId());
+
+            final int userId = job.getUserId();
+            final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
+            final boolean componentPresent;
+            try {
+                componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
+                        job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                        userId) != null);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+
             if (DEBUG) {
                 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
                         + " ready=" + jobReady + " pending=" + jobPending
-                        + " active=" + jobActive + " userRunning=" + userRunning);
+                        + " active=" + jobActive + " userStarted=" + userStarted
+                        + " componentPresent=" + componentPresent);
             }
-            return userRunning && jobReady && !jobPending && !jobActive;
+            return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
         }
 
         /**
@@ -984,8 +1028,9 @@
         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
             return priority;
         }
-        if (mForegroundUids.get(job.getSourceUid())) {
-            return JobInfo.PRIORITY_FOREGROUND_APP;
+        int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
+        if (override != 0) {
+            return override;
         }
         return priority;
     }
@@ -1001,24 +1046,44 @@
             Slog.d(TAG, printPendingQueue());
         }
 
-        // This array essentially stores the state of mActiveServices array.
-        // ith index stores the job present on the ith JobServiceContext.
-        // We manipulate this array until we arrive at what jobs should be running on
-        // what JobServiceContext.
-        JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
-        // Indicates whether we need to act on this jobContext id
-        boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT];
-        int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
-        for (int i=0; i<mActiveServices.size(); i++) {
-            contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob();
-            preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid();
+        int memLevel;
+        try {
+            memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
+        } catch (RemoteException e) {
+            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+        }
+        switch (memLevel) {
+            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+                mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - 2) * 2) / 3;
+                break;
+            case ProcessStats.ADJ_MEM_FACTOR_LOW:
+                mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - 2) / 3;
+                break;
+            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+                mMaxActiveJobs = 1;
+                break;
+            default:
+                mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+                break;
+        }
+
+        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
+        boolean[] act = mTmpAssignAct;
+        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
+        int numActive = 0;
+        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
+            final JobServiceContext js = mActiveServices.get(i);
+            if ((contextIdToJobMap[i] = js.getRunningJob()) != null) {
+                numActive++;
+            }
+            act[i] = false;
+            preferredUidForContext[i] = js.getPreferredUid();
         }
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
         }
-        Iterator<JobStatus> it = mPendingJobs.iterator();
-        while (it.hasNext()) {
-            JobStatus nextPending = it.next();
+        for (int i=0; i<mPendingJobs.size(); i++) {
+            JobStatus nextPending = mPendingJobs.get(i);
 
             // If job is already running, go to next job.
             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -1026,24 +1091,30 @@
                 continue;
             }
 
-            nextPending.lastEvaluatedPriority = evaluateJobPriorityLocked(nextPending);
+            final int priority = evaluateJobPriorityLocked(nextPending);
+            nextPending.lastEvaluatedPriority = priority;
 
             // Find a context for nextPending. The context should be available OR
             // it should have lowest priority among all running jobs
             // (sharing the same Uid as nextPending)
             int minPriority = Integer.MAX_VALUE;
             int minPriorityContextId = -1;
-            for (int i=0; i<mActiveServices.size(); i++) {
-                JobStatus job = contextIdToJobMap[i];
-                int preferredUid = preferredUidForContext[i];
-                if (job == null && (preferredUid == nextPending.getUid() ||
-                        preferredUid == JobServiceContext.NO_PREFERRED_UID) ) {
-                    minPriorityContextId = i;
-                    break;
-                }
+            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
+                JobStatus job = contextIdToJobMap[j];
+                int preferredUid = preferredUidForContext[j];
                 if (job == null) {
+                    if ((numActive < mMaxActiveJobs || priority >= JobInfo.PRIORITY_TOP_APP) &&
+                            (preferredUid == nextPending.getUid() ||
+                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+                        // This slot is free, and we haven't yet hit the limit on
+                        // concurrent jobs...  we can just throw the job in to here.
+                        minPriorityContextId = j;
+                        numActive++;
+                        break;
+                    }
                     // No job on this context, but nextPending can't run here because
-                    // the context has a preferred Uid.
+                    // the context has a preferred Uid or we have reached the limit on
+                    // concurrent jobs.
                     continue;
                 }
                 if (job.getUid() != nextPending.getUid()) {
@@ -1054,7 +1125,7 @@
                 }
                 if (minPriority > nextPending.lastEvaluatedPriority) {
                     minPriority = nextPending.lastEvaluatedPriority;
-                    minPriorityContextId = i;
+                    minPriorityContextId = j;
                 }
             }
             if (minPriorityContextId != -1) {
@@ -1065,7 +1136,7 @@
         if (DEBUG) {
             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
         }
-        for (int i=0; i<mActiveServices.size(); i++) {
+        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
             boolean preservePreferredUid = false;
             if (act[i]) {
                 JobStatus js = mActiveServices.get(i).getRunningJob();
@@ -1123,7 +1194,8 @@
             final ComponentName service = job.getService();
             try {
                 ServiceInfo si = pm.getServiceInfo(service,
-                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(uid));
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+                        UserHandle.getUserId(uid));
                 if (si == null) {
                     throw new IllegalArgumentException("No such service " + service);
                 }
@@ -1295,11 +1367,7 @@
     void dumpInternal(final PrintWriter pw) {
         final long now = SystemClock.elapsedRealtime();
         synchronized (mLock) {
-            pw.print("Started users: ");
-            for (int i=0; i<mStartedUsers.size(); i++) {
-                pw.print("u" + mStartedUsers.get(i) + " ");
-            }
-            pw.println();
+            pw.println("Started users: " + Arrays.toString(mStartedUsers));
             pw.println("Registered jobs:");
             if (mJobs.size() > 0) {
                 mJobs.forEachJob(new JobStatusFunctor() {
@@ -1309,7 +1377,7 @@
                     public void process(JobStatus job) {
                         pw.print("  Job #"); pw.print(index++); pw.print(": ");
                         pw.println(job.toShortString());
-                        job.dump(pw, "    ");
+                        job.dump(pw, "    ", true);
                         pw.print("    Ready: ");
                         pw.print(mHandler.isReadyToBeExecutedLocked(job));
                         pw.print(" (job=");
@@ -1319,7 +1387,7 @@
                         pw.print(" active=");
                         pw.print(isCurrentlyActiveLocked(job));
                         pw.print(" user=");
-                        pw.print(mStartedUsers.contains(job.getUserId()));
+                        pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
                         pw.println(")");
                     }
                 });
@@ -1331,9 +1399,10 @@
                 mControllers.get(i).dumpControllerStateLocked(pw);
             }
             pw.println();
-            pw.println("Foreground uids:");
-            for (int i=0; i<mForegroundUids.size(); i++) {
-                pw.print("  "); pw.println(UserHandle.formatUid(mForegroundUids.keyAt(i)));
+            pw.println("Uid priority overrides:");
+            for (int i=0; i< mUidPriorityOverride.size(); i++) {
+                pw.print("  "); pw.print(UserHandle.formatUid(mUidPriorityOverride.keyAt(i)));
+                pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
             }
             pw.println();
             pw.println("Pending queue:");
@@ -1341,6 +1410,7 @@
                 JobStatus job = mPendingJobs.get(i);
                 pw.print("  Pending #"); pw.print(i); pw.print(": ");
                 pw.println(job.toShortString());
+                job.dump(pw, "    ", false);
                 int priority = evaluateJobPriorityLocked(job);
                 if (priority != JobInfo.PRIORITY_DEFAULT) {
                     pw.print("    Evaluated priority: "); pw.println(priority);
@@ -1351,20 +1421,21 @@
             pw.println("Active jobs:");
             for (int i=0; i<mActiveServices.size(); i++) {
                 JobServiceContext jsc = mActiveServices.get(i);
+                pw.print("  Slot #"); pw.print(i); pw.print(": ");
                 if (jsc.getRunningJob() == null) {
+                    pw.println("inactive");
                     continue;
                 } else {
-                    final long timeout = jsc.getTimeoutElapsed();
-                    pw.print("Running for: ");
-                    pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
-                    pw.print("s timeout=");
-                    pw.print(timeout);
-                    pw.print(" fromnow=");
-                    pw.println(timeout-now);
-                    jsc.getRunningJob().dump(pw, "  ");
+                    pw.println(jsc.getRunningJob().toShortString());
+                    pw.print("    Running for: ");
+                    TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
+                    pw.print(", timeout at: ");
+                    TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
+                    pw.println();
+                    jsc.getRunningJob().dump(pw, "    ", false);
                     int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
                     if (priority != JobInfo.PRIORITY_DEFAULT) {
-                        pw.print("  Evaluated priority: "); pw.println(priority);
+                        pw.print("    Evaluated priority: "); pw.println(priority);
                     }
                 }
             }
@@ -1372,6 +1443,7 @@
             pw.print("mReadyToRock="); pw.println(mReadyToRock);
             pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
             pw.print("mReportedActive="); pw.println(mReportedActive);
+            pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
         }
         pw.println();
     }
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e6fbc39..98bf8a9 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -134,6 +134,10 @@
             sb.append(job.getService().getPackageName());
             sb.append('/');
             sb.append(this.sourceTag);
+            if (sourcePackageName != null) {
+                sb.append('/');
+                sb.append(this.sourcePackageName);
+            }
             this.batteryName = sb.toString();
         } else {
             this.batteryName = job.getService().flattenToShortString();
@@ -433,10 +437,10 @@
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(" jId=");
         sb.append(job.getId());
-        sb.append(" uid=");
+        sb.append(' ');
         UserHandle.formatUid(sb, callingUid);
         sb.append(' ');
-        sb.append(job.getService().flattenToShortString());
+        sb.append(batteryName);
         return sb.toString();
     }
 
@@ -468,75 +472,71 @@
     }
 
     // Dumpsys infrastructure
-    public void dump(PrintWriter pw, String prefix) {
+    public void dump(PrintWriter pw, String prefix, boolean full) {
         pw.print(prefix); UserHandle.formatUid(pw, callingUid);
         pw.print(" tag="); pw.println(tag);
         pw.print(prefix);
         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
         pw.print(" user="); pw.print(getSourceUserId());
         pw.print(" pkg="); pw.println(getSourcePackageName());
-        pw.print(prefix); pw.println("JobInfo:");
-        pw.print(prefix); pw.print("  Service: ");
-        pw.println(job.getService().flattenToShortString());
-        if (job.isPeriodic()) {
-            pw.print(prefix); pw.print("  PERIODIC: interval=");
-            TimeUtils.formatDuration(job.getIntervalMillis(), pw);
-            pw.print(" flex=");
-            TimeUtils.formatDuration(job.getFlexMillis(), pw);
-            pw.println();
-        }
-        if (job.isPersisted()) {
-            pw.print(prefix); pw.println("  PERSISTED");
-        }
-        if (job.getPriority() != 0) {
-            pw.print(prefix); pw.print("  Priority: ");
-            pw.println(job.getPriority());
-        }
-        pw.print(prefix); pw.print("  Requires: charging=");
-        pw.print(job.isRequireCharging());
-        pw.print(" deviceIdle=");
-        pw.println(job.isRequireDeviceIdle());
-        if (job.getTriggerContentUris() != null) {
-            pw.print(prefix); pw.println("  Trigger content URIs:");
-            for (int i=0; i<job.getTriggerContentUris().length; i++) {
-                JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
-                pw.print(prefix); pw.print("    ");
-                pw.print(Integer.toHexString(trig.getFlags()));
-                pw.print(' ' );
-                pw.println(trig.getUri());
+        if (full) {
+            pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix);
+            pw.print("  Service: "); pw.println(job.getService().flattenToShortString());
+            if (job.isPeriodic()) {
+                pw.print(prefix); pw.print("  PERIODIC: interval=");
+                TimeUtils.formatDuration(job.getIntervalMillis(), pw);
+                pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
+                pw.println();
             }
-        }
-        if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
-            pw.print(prefix); pw.print("  Network type: ");
-            pw.println(job.getNetworkType());
-        }
-        if (job.getMinLatencyMillis() != 0) {
-            pw.print(prefix); pw.print("  Minimum latency: ");
-            TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
+            if (job.isPersisted()) {
+                pw.print(prefix); pw.println("  PERSISTED");
+            }
+            if (job.getPriority() != 0) {
+                pw.print(prefix); pw.print("  Priority: "); pw.println(job.getPriority());
+            }
+            pw.print(prefix); pw.print("  Requires: charging=");
+            pw.print(job.isRequireCharging()); pw.print(" deviceIdle=");
+            pw.println(job.isRequireDeviceIdle());
+            if (job.getTriggerContentUris() != null) {
+                pw.print(prefix); pw.println("  Trigger content URIs:");
+                for (int i = 0; i < job.getTriggerContentUris().length; i++) {
+                    JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
+                    pw.print(prefix); pw.print("    ");
+                    pw.print(Integer.toHexString(trig.getFlags()));
+                    pw.print(' '); pw.println(trig.getUri());
+                }
+            }
+            if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
+                pw.print(prefix); pw.print("  Network type: "); pw.println(job.getNetworkType());
+            }
+            if (job.getMinLatencyMillis() != 0) {
+                pw.print(prefix); pw.print("  Minimum latency: ");
+                TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
+                pw.println();
+            }
+            if (job.getMaxExecutionDelayMillis() != 0) {
+                pw.print(prefix); pw.print("  Max execution delay: ");
+                TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
+                pw.println();
+            }
+            pw.print(prefix); pw.print("  Backoff: policy="); pw.print(job.getBackoffPolicy());
+            pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
             pw.println();
-        }
-        if (job.getMaxExecutionDelayMillis() != 0) {
-            pw.print(prefix); pw.print("  Max execution delay: ");
-            TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
-            pw.println();
-        }
-        pw.print(prefix); pw.print("  Backoff: policy=");
-        pw.print(job.getBackoffPolicy());
-        pw.print(" initial=");
-        TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
-        pw.println();
-        if (job.hasEarlyConstraint()) {
-            pw.print(prefix); pw.println("  Has early constraint");
-        }
-        if (job.hasLateConstraint()) {
-            pw.print(prefix); pw.println("  Has late constraint");
+            if (job.hasEarlyConstraint()) {
+                pw.print(prefix); pw.println("  Has early constraint");
+            }
+            if (job.hasLateConstraint()) {
+                pw.print(prefix); pw.println("  Has late constraint");
+            }
         }
         pw.print(prefix); pw.print("Required constraints:");
         dumpConstraints(pw, requiredConstraints);
         pw.println();
-        pw.print(prefix); pw.print("Satisfied constraints:");
-        dumpConstraints(pw, satisfiedConstraints);
-        pw.println();
+        if (full) {
+            pw.print(prefix); pw.print("Satisfied constraints:");
+            dumpConstraints(pw, satisfiedConstraints);
+            pw.println();
+        }
         if (changedAuthorities != null) {
             pw.print(prefix); pw.println("Changed authorities:");
             for (int i=0; i<changedAuthorities.size(); i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c337c573..692e610 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -36,7 +36,7 @@
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -2524,7 +2524,7 @@
         final Notification notification = record.sbn.getNotification();
 
         // Should this notification make noise, vibe, or use the LED?
-        final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_HIGH;
+        final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
         final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
         if (DBG || record.isIntercepted())
             Slog.v(TAG,
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 6c5685d..fd893fa 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MIN;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
@@ -33,6 +34,8 @@
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.EventLogTags;
@@ -55,6 +58,8 @@
  * {@hide}
  */
 public final class NotificationRecord {
+    static final String TAG = "NotificationRecord";
+    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     final StatusBarNotification sbn;
     final int mOriginalFlags;
     private final Context mContext;
@@ -123,6 +128,8 @@
 
         switch (n.priority) {
             case Notification.PRIORITY_MIN:
+                importance = IMPORTANCE_MIN;
+                break;
             case Notification.PRIORITY_LOW:
                 importance = IMPORTANCE_LOW;
                 break;
@@ -143,25 +150,15 @@
                 || n.sound != null
                 || n.vibrate != null;
         stats.isNoisy = isNoisy;
-        if (!isNoisy && importance > IMPORTANCE_DEFAULT) {
-            importance = IMPORTANCE_DEFAULT;
+
+        if (!isNoisy && importance > IMPORTANCE_LOW) {
+            importance = IMPORTANCE_LOW;
         }
 
-        try {
-            final ApplicationInfo applicationInfo =
-                    mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
-                            0, sbn.getUser().getIdentifier());
-            if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.N) {
-                if (isNoisy) {
-                    if (importance >= IMPORTANCE_HIGH) {
-                        importance = IMPORTANCE_MAX;
-                    } else {
-                        importance = IMPORTANCE_HIGH;
-                    }
-                }
+        if (isNoisy) {
+            if (importance < IMPORTANCE_DEFAULT) {
+                importance = IMPORTANCE_DEFAULT;
             }
-        } catch (NameNotFoundException e) {
-            // oh well.
         }
 
         if (n.fullScreenIntent != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 0272850..538f951 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -597,8 +597,9 @@
 
     private static class ImportanceHistogram {
         // TODO define these somewhere else
-        private static final int NUM_IMPORTANCES = 5;
-        private static final String[] IMPORTANCE_NAMES = {"none", "low", "default", "high", "max"};
+        private static final int NUM_IMPORTANCES = 6;
+        private static final String[] IMPORTANCE_NAMES =
+                {"none", "min", "low", "default", "high", "max"};
         private final Context mContext;
         private final String[] mCounterNames;
         private final String mPrefix;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0f67a05..3f06c78 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10668,14 +10668,14 @@
                     pkgSetting.setInstalled(true, userId);
                     pkgSetting.setHidden(false, userId);
                     mSettings.writePackageRestrictionsLPr(userId);
-                    if (pkgSetting.pkg != null) {
-                        prepareAppDataAfterInstall(pkgSetting.pkg);
-                    }
                     installed = true;
                 }
             }
 
             if (installed) {
+                if (pkgSetting.pkg != null) {
+                    prepareAppDataAfterInstall(pkgSetting.pkg);
+                }
                 sendPackageAddedForUser(packageName, pkgSetting, userId);
             }
         } finally {
@@ -18047,6 +18047,8 @@
      * correct for all installed apps. If there is an ownership mismatch, it
      * will try recovering system apps by wiping data; third-party app data is
      * left intact.
+     * <p>
+     * <em>Note: To avoid a deadlock, do not call this method with {@code mPackages} lock held</em>
      */
     private void prepareAppDataAfterInstall(PackageParser.Package pkg) {
         prepareAppDataAfterInstallInternal(pkg);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dd13809..1652185 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2387,10 +2387,7 @@
             if (DEBUG_KERNEL) Slog.d(TAG, "Dropping mapping " + name);
 
             mKernelMapping.remove(name);
-
-            final File dir = new File(mKernelMappingFilename, name);
-            FileUtils.deleteContents(dir);
-            dir.delete();
+            new File(mKernelMappingFilename, name).delete();
         }
     }
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index cbbcdae..6bda4ed 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -33,4 +33,5 @@
     void topAppWindowChanged(boolean menuVisible);
     void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
             Rect fullscreenBounds, Rect dockedBounds, String cause);
+    void toggleSplitScreen();
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6eab8d4..d24e1af 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -204,6 +204,16 @@
             StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
                     dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
         }
+
+        @Override
+        public void toggleSplitScreen() {
+            enforceStatusBarService();
+            if (mBar != null) {
+                try {
+                    mBar.toggleSplitScreen();
+                } catch (RemoteException ex) {}
+            }
+        }
     };
 
     // ================================================================================
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 77db275..ba0d340 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -231,11 +231,16 @@
                             }
                             if (lockWallpaperChanged
                                     || (wallpaper.whichPending & FLAG_SET_LOCK) != 0) {
-                                // either a lock-only wallpaper commit or a system+lock event,
-                                // so tell keyguard about it
                                 if (DEBUG) {
-                                    Slog.i(TAG, "Lock-relevant wallpaper changed; telling listener");
+                                    Slog.i(TAG, "Lock-relevant wallpaper changed");
                                 }
+                                // either a lock-only wallpaper commit or a system+lock event.
+                                // if it's system-plus-lock we need to wipe the lock bookkeeping;
+                                // we're falling back to displaying the system wallpaper there.
+                                if (!lockWallpaperChanged) {
+                                    mLockWallpaperMap.remove(wallpaper.userId);
+                                }
+                                // and in any case, tell keyguard about it
                                 final IWallpaperManagerCallback cb = mKeyguardListener;
                                 if (cb != null) {
                                     try {
@@ -245,7 +250,7 @@
                                     }
                                 }
                             }
-                            saveSettingsLocked(wallpaper);
+                            saveSettingsLocked(wallpaper.userId);
                         }
                     }
                 }
@@ -479,7 +484,7 @@
                     // when we have an engine, but I'm not sure about
                     // locking there and anyway we always need to be able to
                     // recover if there is something wrong.
-                    saveSettingsLocked(mWallpaper);
+                    saveSettingsLocked(mWallpaper.userId);
                 }
             }
         }
@@ -995,7 +1000,7 @@
             if (width != wallpaper.width || height != wallpaper.height) {
                 wallpaper.width = width;
                 wallpaper.height = height;
-                saveSettingsLocked(wallpaper);
+                saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
                     if (wallpaper.connection.mEngine != null) {
@@ -1052,7 +1057,7 @@
 
             if (!padding.equals(wallpaper.padding)) {
                 wallpaper.padding.set(padding);
-                saveSettingsLocked(wallpaper);
+                saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
                     if (wallpaper.connection.mEngine != null) {
@@ -1488,50 +1493,33 @@
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
-    private void saveSettingsLocked(WallpaperData wallpaper) {
-        JournaledFile journal = makeJournaledFile(wallpaper.userId);
-        FileOutputStream stream = null;
+    private void saveSettingsLocked(int userId) {
+        JournaledFile journal = makeJournaledFile(userId);
+        FileOutputStream fstream = null;
+        BufferedOutputStream stream = null;
         try {
-            stream = new FileOutputStream(journal.chooseForWrite(), false);
             XmlSerializer out = new FastXmlSerializer();
+            fstream = new FileOutputStream(journal.chooseForWrite(), false);
+            stream = new BufferedOutputStream(fstream);
             out.setOutput(stream, StandardCharsets.UTF_8.name());
             out.startDocument(null, true);
 
-            out.startTag(null, "wp");
-            out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
-            out.attribute(null, "width", Integer.toString(wallpaper.width));
-            out.attribute(null, "height", Integer.toString(wallpaper.height));
+            WallpaperData wallpaper;
 
-            out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
-            out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
-            out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
-            out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
-
-            if (wallpaper.padding.left != 0) {
-                out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+            wallpaper = mWallpaperMap.get(userId);
+            if (wallpaper != null) {
+                writeWallpaperAttributes(out, "wp", wallpaper);
             }
-            if (wallpaper.padding.top != 0) {
-                out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+            wallpaper = mLockWallpaperMap.get(userId);
+            if (wallpaper != null) {
+                writeWallpaperAttributes(out, "kwp", wallpaper);
             }
-            if (wallpaper.padding.right != 0) {
-                out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
-            }
-            if (wallpaper.padding.bottom != 0) {
-                out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
-            }
-
-            out.attribute(null, "name", wallpaper.name);
-            if (wallpaper.wallpaperComponent != null
-                    && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
-                out.attribute(null, "component",
-                        wallpaper.wallpaperComponent.flattenToShortString());
-            }
-            out.endTag(null, "wp");
 
             out.endDocument();
-            stream.flush();
-            FileUtils.sync(stream);
-            stream.close();
+
+            stream.flush(); // also flushes fstream
+            FileUtils.sync(fstream);
+            stream.close(); // also closes fstream
             journal.commit();
         } catch (IOException e) {
             IoUtils.closeQuietly(stream);
@@ -1539,6 +1527,40 @@
         }
     }
 
+    private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        out.startTag(null, tag);
+        out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
+        out.attribute(null, "width", Integer.toString(wallpaper.width));
+        out.attribute(null, "height", Integer.toString(wallpaper.height));
+
+        out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
+        out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
+        out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
+        out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
+
+        if (wallpaper.padding.left != 0) {
+            out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+        }
+        if (wallpaper.padding.top != 0) {
+            out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+        }
+        if (wallpaper.padding.right != 0) {
+            out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+        }
+        if (wallpaper.padding.bottom != 0) {
+            out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+        }
+
+        out.attribute(null, "name", wallpaper.name);
+        if (wallpaper.wallpaperComponent != null
+                && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
+            out.attribute(null, "component",
+                    wallpaper.wallpaperComponent.flattenToShortString());
+        }
+        out.endTag(null, tag);
+    }
+
     private void migrateFromOld() {
         File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
         File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
@@ -1753,8 +1775,8 @@
         WallpaperData wallpaper = null;
         boolean success = false;
         synchronized (mLock) {
-            loadSettingsLocked(0);
-            wallpaper = mWallpaperMap.get(0);
+            loadSettingsLocked(UserHandle.USER_SYSTEM);
+            wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
             wallpaper.wallpaperId = makeWallpaperIdLocked();    // always bump id at restore
             if (wallpaper.nextWallpaperComponent != null
                     && !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
@@ -1788,11 +1810,11 @@
         if (!success) {
             Slog.e(TAG, "Failed to restore wallpaper: '" + wallpaper.name + "'");
             wallpaper.name = "";
-            getWallpaperDir(0).delete();
+            getWallpaperDir(UserHandle.USER_SYSTEM).delete();
         }
 
         synchronized (mLock) {
-            saveSettingsLocked(wallpaper);
+            saveSettingsLocked(UserHandle.USER_SYSTEM);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 12c62bd..f9e258d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -159,6 +160,25 @@
         }
     }
 
+    void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
+        firstWindowDrawn = true;
+
+        // We now have a good window to show, remove dead placeholders
+        removeAllDeadWindows();
+
+        if (startingData != null) {
+            if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
+                    + win.mToken + ": first real window is shown, no animation");
+            // If this initial window is animating, stop it -- we will do an animation to reveal
+            // it from behind the starting window, so there is no need for it to also be doing its
+            // own stuff.
+            winAnimator.clearAnimation();
+            winAnimator.mService.mFinishedStarting.add(this);
+            winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+        }
+        updateReportedVisibilityLocked();
+    }
+
     void updateReportedVisibilityLocked() {
         if (appToken == null) {
             return;
@@ -357,6 +377,9 @@
     void notifyAppStopped() {
         mAppStopped = true;
         destroySurfaces();
+
+        // Remove any starting window that was added for this app if they are still around.
+        mTask.mService.scheduleRemoveStartingWindowLocked(this);
     }
 
     /**
@@ -594,6 +617,9 @@
         if (paused) {
             pw.print(prefix); pw.print("paused="); pw.println(paused);
         }
+        if (mAppStopped) {
+            pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
+        }
         if (numInterestingWindows != 0 || numDrawnWindows != 0
                 || allDrawn || mAppAnimator.allDrawn) {
             pw.print(prefix); pw.print("numInterestingWindows=");
@@ -619,7 +645,7 @@
             pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
                     pw.print(" startingView="); pw.print(startingView);
                     pw.print(" startingDisplayed="); pw.print(startingDisplayed);
-                    pw.print(" startingMoved"); pw.println(startingMoved);
+                    pw.print(" startingMoved="); pw.println(startingMoved);
         }
         if (!mFrozenBounds.isEmpty()) {
             pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index daeecfc..72621c2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -20,6 +20,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
@@ -362,6 +363,7 @@
     final WindowManagerPolicy mPolicy = new PhoneWindowManager();
 
     final IActivityManager mActivityManager;
+    final ActivityManagerInternal mAmInternal;
 
     final AppOpsManager mAppOps;
 
@@ -902,6 +904,7 @@
         mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
 
         mActivityManager = ActivityManagerNative.getDefault();
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
         AppOpsManager.OnOpChangedInternalListener opListener =
                 new AppOpsManager.OnOpChangedInternalListener() {
@@ -7606,6 +7609,9 @@
 
         public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
 
+        public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
+        public static final int NOTIFY_STARTING_WINDOW_DRAWN = 48;
+
         /**
          * Used to denote that an integer field in a message will not be used.
          */
@@ -8203,6 +8209,13 @@
                         token.clearTimedoutReplacesLocked();
                     }
                 }
+                case NOTIFY_APP_TRANSITION_STARTING: {
+                    mAmInternal.notifyAppTransitionStarting(msg.arg1);
+                }
+                break;
+                case NOTIFY_STARTING_WINDOW_DRAWN: {
+                    mAmInternal.notifyStartingWindowDrawn();
+                }
                 break;
             }
             if (DEBUG_WINDOW_TRACE) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 44ed7e2..40b6b50 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2347,7 +2347,8 @@
         pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
                 pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
                 pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
-                pw.print(" hasSavedSurface()="); pw.println(hasSavedSurface());
+                pw.print(" hasSavedSurface()="); pw.print(hasSavedSurface());
+                pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
         if (dumpAll) {
             pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
                     pw.print(" last="); mLastFrame.printShortString(pw);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2d8a4c9..c623047 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1522,23 +1522,7 @@
             }
 
             if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
-                mWin.mAppToken.firstWindowDrawn = true;
-
-                // We now have a good window to show, remove dead placeholders
-                mWin.mAppToken.removeAllDeadWindows();
-
-                if (mWin.mAppToken.startingData != null) {
-                    if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
-                            + mWin.mToken + ": first real window is shown, no animation");
-                    // If this initial window is animating, stop it -- we
-                    // will do an animation to reveal it from behind the
-                    // starting window, so there is no need for it to also
-                    // be doing its own stuff.
-                    clearAnimation();
-                    mService.mFinishedStarting.add(mWin.mAppToken);
-                    mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
-                }
-                mWin.mAppToken.updateReportedVisibilityLocked();
+                mWin.mAppToken.onFirstWindowDrawn(mWin, this);
             }
 
             return true;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 0db6f3a..2972a24 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -62,7 +62,9 @@
     // However, we need to somehow handle the situation where the cropping would completely hide
     // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
     private boolean mHiddenForCrop = false;
-    private boolean mHiddenForOtherReasons = false;
+
+    // Initially a surface is hidden after just being created.
+    private boolean mHiddenForOtherReasons = true;
     private final String title;
 
     public WindowSurfaceController(SurfaceSession s,
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index f705df8..856d30a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1,5 +1,9 @@
 package com.android.server.wm;
 
+import static android.app.ActivityManagerInternal.APP_TRANSITION_SAVED_SURFACE;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_STARTING_WINDOW;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -42,6 +46,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Debug;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -782,6 +787,7 @@
                                 }
                             }
                         } else if (w.isDrawnLw()) {
+                            mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
                             atoken.startingDisplayed = true;
                         }
                     }
@@ -1262,6 +1268,7 @@
                 "Checking " + appsCount + " opening apps (frozen="
                         + mService.mDisplayFrozen + " timeout="
                         + mService.mAppTransition.isTimeout() + ")...");
+        int reason = APP_TRANSITION_TIMEOUT;
         if (!mService.mAppTransition.isTimeout()) {
             for (int i = 0; i < appsCount; i++) {
                 AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -1271,11 +1278,18 @@
                         + wtoken.startingDisplayed + " startingMoved="
                         + wtoken.startingMoved);
 
+                final boolean drawnBeforeRestoring = wtoken.allDrawn;
                 wtoken.restoreSavedSurfaces();
 
                 if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
                     return false;
                 }
+                if (wtoken.allDrawn) {
+                    reason = drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
+                            : APP_TRANSITION_SAVED_SURFACE;
+                } else {
+                    reason = APP_TRANSITION_STARTING_WINDOW;
+                }
             }
 
             // We also need to wait for the specs to be fetched, if needed.
@@ -1285,9 +1299,15 @@
             }
 
             // If the wallpaper is visible, we need to check it's ready too.
-            return !mWallpaperControllerLocked.isWallpaperVisible() ||
+            boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
                     mWallpaperControllerLocked.wallpaperTransitionReady();
+            if (wallpaperReady) {
+                mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
+                return true;
+            }
+            return false;
         }
+        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
         return true;
     }
 
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e39445a..c97323c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -29,10 +29,11 @@
 #include "android_runtime/Log.h"
 
 #include <arpa/inet.h>
-#include <string.h>
-#include <pthread.h>
+#include <limits>
 #include <linux/in.h>
 #include <linux/in6.h>
+#include <pthread.h>
+#include <string.h>
 
 static jobject mCallbacksObj = NULL;
 
@@ -1090,11 +1091,37 @@
         if (flags & (flag)) object.callSetter("set" # setter, (value))
 
 static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+    static uint32_t discontinuity_count_to_handle_old_lock_type = 0;
     JavaObject object(env, "android/location/GnssClock");
     GpsClockFlags flags = clock->flags;
 
     SET_IF(GNSS_CLOCK_HAS_LEAP_SECOND, LeapSecond, clock->leap_second);
-    SET(Type, clock->type);
+
+    // GnssClock only supports the more effective HW_CLOCK type, so type
+    // handling and documentation complexity has been removed.  To convert the
+    // old GPS_CLOCK types (active only in a limited number of older devices),
+    // the GPS time information is handled as an always discontinuous HW clock,
+    // with the GPS time information put into the full_bias_ns instead - so that
+    // time_ns + full_bias_ns = local estimate of GPS time (as remains true, in
+    // the new GnssClock struct.)
+    switch (clock->type) {
+      case GPS_CLOCK_TYPE_UNKNOWN:
+        // Clock type unsupported.
+        ALOGE("Unknown clock type provided.");
+        break;
+      case GPS_CLOCK_TYPE_LOCAL_HW_TIME:
+        // Already local hardware time. No need to do anything.
+        break;
+      case GPS_CLOCK_TYPE_GPS_TIME:
+        // GPS time, need to convert.
+        flags |= GNSS_CLOCK_HAS_FULL_BIAS;
+        clock->full_bias_ns = clock->time_ns;
+        clock->time_ns = 0;
+        SET(HardwareClockDiscontinuityCount,
+            discontinuity_count_to_handle_old_lock_type++);
+        break;
+    }
+
     SET(TimeInNs, clock->time_ns);
     SET_IF(GNSS_CLOCK_HAS_TIME_UNCERTAINTY,
            TimeUncertaintyInNs,
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index f1fe346..32501ad 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,12 +15,6 @@
  */
 package com.android.server.notification;
 
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
-
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3ad7d34..1278c07 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -733,9 +733,28 @@
      * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
      * @return A {@link PersistableBundle} containing the config for the given subId, or default
      *         values for an invalid subId.
+     *
+     * @deprecated use getConfig.
      */
     @Nullable
     public PersistableBundle getConfigForSubId(int subId) {
+        return getConfig(subId);
+    }
+
+    /**
+     * Gets the configuration values for a particular subscription, which is associated with a
+     * specific SIM card. If an invalid subId is used, the returned config will contain default
+     * values.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     * @return A {@link PersistableBundle} containing the config for the given subId, or default
+     *         values for an invalid subId.
+     */
+    @Nullable
+    public PersistableBundle getConfig(int subId) {
         try {
             ICarrierConfigLoader loader = getICarrierConfigLoader();
             if (loader == null) {
@@ -757,11 +776,32 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *
-     * @see #getConfigForSubId
+     * @return A {@link PersistableBundle} containing the config for the default subscription.
      */
     @Nullable
     public PersistableBundle getConfig() {
-        return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
+        return getConfig(SubscriptionManager.getDefaultSubscriptionId());
+    }
+
+    /**
+     * Calling this method triggers telephony services to fetch the current carrier configuration.
+     * <p>
+     * Normally this does not need to be called because the platform reloads config on its own.
+     * This should be called by a carrier service app if it wants to update config at an arbitrary
+     * moment.
+     * </p>
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
+     * <p>
+     * This method returns before the reload has completed, and
+     * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
+     * arbitrary thread.
+     * </p>
+     *
+     * @deprecated use notifyConfigChanged.
+     */
+    public void notifyConfigChangedForSubId(int subId) {
+        notifyConfigChanged(subId);
     }
 
     /**
@@ -779,7 +819,7 @@
      * arbitrary thread.
      * </p>
      */
-    public void notifyConfigChangedForSubId(int subId) {
+    public void notifyConfigChanged(int subId) {
         try {
             ICarrierConfigLoader loader = getICarrierConfigLoader();
             if (loader == null) {
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 4aa5b98..558c1dc 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -241,6 +241,16 @@
     public static final int CODE_ANSWERED_ELSEWHERE = 1014;
 
     /**
+     * Call pull request failure from the network.
+     */
+    public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+    /**
+     * Call ended due to being pulled onto another device.
+     */
+    public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
+
+    /**
      * Supplementary services (HOLD/RESUME) failure error codes.
      * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
      */
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 69259d0..04cb1f2 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -18,6 +18,8 @@
 
 import com.android.ims.ImsReasonInfo;
 
+import android.net.Uri;
+
 /**
  * A listener type for receiving notifications about the changes to
  * the IMS connection(registration).
@@ -100,4 +102,9 @@
      * @param count The number of waiting voice messages.
      */
     void voiceMessageCountUpdate(int count);
+
+    /**
+     * Notifies the application when the list of URIs associated with IMS client is updated.
+     */
+    void registrationAssociatedUriChanged(in Uri[] uris);
 }
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 8b8d604..4bed941 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -60,6 +60,8 @@
     // optional parameter: comma separated list of required account types before proceeding
     // with the app launch
     private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
+    private static final String WEARABLE_ACTION_GOOGLE =
+            "com.google.android.wearable.action.GOOGLE";
     private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
     private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
     private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps
@@ -183,6 +185,13 @@
         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
         List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
+        resolveLoop(ris, intentToResolve, pm);
+        intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
+        ris = pm.queryIntentActivities(intentToResolve, 0);
+        resolveLoop(ris, intentToResolve, pm);
+    }
+
+    private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) {
         if (ris == null || ris.isEmpty()) {
             Log.i(TAG, "Could not find any apps");
         } else {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 9ac4dbf..2a3f143 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -86,6 +86,122 @@
     }
 
     private Test[] mTests = new Test[] {
+            new Test("Min priority") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Min priority")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setFullScreenIntent(makeIntent2(), false)
+                            .build();
+                    mNM.notify(7000, n);
+                }
+            },
+            new Test("Min priority, high pri flag") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Min priority, high pri flag")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setFullScreenIntent(makeIntent2(), true)
+                            .build();
+                    mNM.notify(7001, n);
+                }
+            },
+            new Test("Low priority") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Low priority")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_LOW)
+                            .setFullScreenIntent(makeIntent2(), false)
+                            .build();
+                    mNM.notify(7002, n);
+                }
+            },
+            new Test("Default priority") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Default priority")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setFullScreenIntent(makeIntent2(), false)
+                            .build();
+                    mNM.notify(7004, n);
+                }
+            },
+            new Test("High priority") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("High priority")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_HIGH)
+                            .setFullScreenIntent(makeIntent2(), false)
+                            .build();
+                    mNM.notify(7006, n);
+                }
+            },
+            new Test("Max priority") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Max priority")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_MAX)
+                            .setFullScreenIntent(makeIntent2(), false)
+                            .build();
+                    mNM.notify(7008, n);
+                }
+            },
+            new Test("Max priority with delay") {
+                public void run()
+                {
+                    try {
+                        Thread.sleep(5000);
+                    } catch (InterruptedException e) {
+                    }
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Max priority")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"))
+                            .setPriority(Notification.PRIORITY_MAX)
+                            .setFullScreenIntent(makeIntent2(), false)
+                            .build();
+                    mNM.notify(7008, n);
+                }
+            },
         new Test("Off") {
             public void run() {
                 PowerManager pm = (PowerManager)NotificationTestList.this.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/UiBench/res/layout/activity_transition.xml b/tests/UiBench/res/layout/activity_transition.xml
index d4c6610..4556b02 100644
--- a/tests/UiBench/res/layout/activity_transition.xml
+++ b/tests/UiBench/res/layout/activity_transition.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License
   -->
 <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/transition_grid_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="true"
@@ -25,8 +26,6 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
-        android:layout_column="0"
-        android:layout_row="0"
         android:src="@drawable/ducky"
         android:onClick="clicked"
         android:transitionName="ducky"/>
@@ -36,8 +35,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/woot"
-        android:layout_column="1"
-        android:layout_row="0"
         android:onClick="clicked"
         android:transitionName="woot"/>
     <ImageView
@@ -46,8 +43,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/ball"
-        android:layout_column="0"
-        android:layout_row="1"
         android:onClick="clicked"
         android:transitionName="ball"/>
     <ImageView
@@ -56,8 +51,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/block"
-        android:layout_column="1"
-        android:layout_row="1"
         android:onClick="clicked"
         android:transitionName="block"/>
     <ImageView
@@ -66,8 +59,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/jellies"
-        android:layout_column="0"
-        android:layout_row="2"
         android:onClick="clicked"
         android:transitionName="jellies"/>
     <ImageView
@@ -76,8 +67,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/mug"
-        android:layout_column="1"
-        android:layout_row="2"
         android:onClick="clicked"
         android:transitionName="mug"/>
     <ImageView
@@ -86,8 +75,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/pencil"
-        android:layout_column="0"
-        android:layout_row="3"
         android:onClick="clicked"
         android:transitionName="pencil"/>
     <ImageView
@@ -96,8 +83,6 @@
         android:layout_width="wrap_content"
         android:scaleType="centerCrop"
         android:src="@drawable/scissors"
-        android:layout_column="1"
-        android:layout_row="3"
         android:onClick="clicked"
         android:transitionName="scissors"/>
 </GridLayout>
\ No newline at end of file
diff --git a/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java b/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
index 1106a13..0a069c2 100644
--- a/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
+++ b/tests/UiBench/src/com/android/test/uibench/ActivityTransition.java
@@ -18,11 +18,13 @@
 import android.app.ActivityOptions;
 import android.app.SharedElementCallback;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
+import android.widget.GridLayout;
 import android.widget.ImageView;
 
 import java.util.List;
@@ -90,6 +92,13 @@
         getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
         setContentView(R.layout.activity_transition);
         setupHero();
+
+        // Ensure that all images are visible regardless of orientation.
+        GridLayout gridLayout = (GridLayout) findViewById(R.id.transition_grid_layout);
+        boolean isPortrait =
+                getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+        gridLayout.setRowCount(isPortrait ? 4 : 2);
+        gridLayout.setColumnCount(isPortrait ? 2 : 4);
     }
 
     private void setupHero() {
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index ed12bdf..a46aaec 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -160,21 +160,6 @@
         }
     }
 
-    /** @hide */
-    public static final int ENABLED                                          = 0;
-    /** @hide */
-    public static final int AUTO_ROAM_DISABLED                               = 16;
-    /** @hide */
-    public static final int AUTO_JOIN_DISABLED                               = 32;
-    /** @hide */
-    public static final int AUTHENTICATION_ERROR                              = 128;
-
-    /**
-     * Status: indicating join status
-     * @hide
-     */
-    public int autoJoinStatus;
-
     /**
      * num IP configuration failures
      * @hide
@@ -187,17 +172,6 @@
      */
     public long blackListTimestamp;
 
-    /** @hide **/
-    public void setAutoJoinStatus(int status) {
-        if (status < 0) status = 0;
-        if (status == 0) {
-            blackListTimestamp = 0;
-        }  else if (status > autoJoinStatus) {
-            blackListTimestamp = System.currentTimeMillis();
-        }
-        autoJoinStatus = status;
-    }
-
     /**
      * Status: indicating the scan result is not a result
      * that is part of user's saved configurations
@@ -462,7 +436,6 @@
             distanceCm = source.distanceCm;
             distanceSdCm = source.distanceSdCm;
             seen = source.seen;
-            autoJoinStatus = source.autoJoinStatus;
             untrusted = source.untrusted;
             numConnection = source.numConnection;
             numUsage = source.numUsage;
@@ -506,9 +479,6 @@
 
         sb.append(", passpoint: ");
         sb.append(((flags & FLAG_PASSPOINT_NETWORK) != 0) ? "yes" : "no");
-        if (autoJoinStatus != 0) {
-            sb.append(", status: ").append(autoJoinStatus);
-        }
         sb.append(", ChannelBandwidth: ").append(channelWidth);
         sb.append(", centerFreq0: ").append(centerFreq0);
         sb.append(", centerFreq1: ").append(centerFreq1);
@@ -544,7 +514,6 @@
         dest.writeInt(centerFreq0);
         dest.writeInt(centerFreq1);
         dest.writeLong(seen);
-        dest.writeInt(autoJoinStatus);
         dest.writeInt(untrusted ? 1 : 0);
         dest.writeInt(numConnection);
         dest.writeInt(numUsage);
@@ -615,7 +584,6 @@
                 );
 
                 sr.seen = in.readLong();
-                sr.autoJoinStatus = in.readInt();
                 sr.untrusted = in.readInt() != 0;
                 sr.numConnection = in.readInt();
                 sr.numUsage = in.readInt();