Merge "Fix issue #28931042: wtf in system server" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 56390c2..70ea1c5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5048,7 +5048,7 @@
 
   public static class Notification.BigPictureStyle extends android.app.Notification.Style {
     ctor public Notification.BigPictureStyle();
-    ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.BigPictureStyle(android.app.Notification.Builder);
     method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
     method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
@@ -5058,7 +5058,7 @@
 
   public static class Notification.BigTextStyle extends android.app.Notification.Style {
     ctor public Notification.BigTextStyle();
-    ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.BigTextStyle(android.app.Notification.Builder);
     method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
     method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence);
     method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
@@ -5159,12 +5159,10 @@
 
   public static class Notification.DecoratedCustomViewStyle extends android.app.Notification.Style {
     ctor public Notification.DecoratedCustomViewStyle();
-    ctor public Notification.DecoratedCustomViewStyle(android.app.Notification.Builder);
   }
 
   public static class Notification.DecoratedMediaCustomViewStyle extends android.app.Notification.MediaStyle {
     ctor public Notification.DecoratedMediaCustomViewStyle();
-    ctor public Notification.DecoratedMediaCustomViewStyle(android.app.Notification.Builder);
   }
 
   public static abstract interface Notification.Extender {
@@ -5173,7 +5171,7 @@
 
   public static class Notification.InboxStyle extends android.app.Notification.Style {
     ctor public Notification.InboxStyle();
-    ctor public Notification.InboxStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.InboxStyle(android.app.Notification.Builder);
     method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence);
     method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence);
     method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence);
@@ -5181,7 +5179,7 @@
 
   public static class Notification.MediaStyle extends android.app.Notification.Style {
     ctor public Notification.MediaStyle();
-    ctor public Notification.MediaStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.MediaStyle(android.app.Notification.Builder);
     method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
     method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
   }
@@ -19585,7 +19583,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+    method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -19731,7 +19729,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
+    method public void onRecordingConfigChanged(java.util.List<android.media.AudioRecordingConfiguration>);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19778,6 +19776,7 @@
     method public void stop() throws java.lang.IllegalStateException;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int READ_BLOCKING = 0; // 0x0
     field public static final int READ_NON_BLOCKING = 1; // 0x1
@@ -19900,6 +19899,7 @@
     method public int write(java.nio.ByteBuffer, int, int, long);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
@@ -20557,6 +20557,7 @@
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelHP = 4; // 0x4
     field public static final int MPEG2LevelLL = 0; // 0x0
     field public static final int MPEG2LevelML = 1; // 0x1
     field public static final int MPEG2Profile422 = 2; // 0x2
@@ -20570,9 +20571,11 @@
     field public static final int MPEG4Level1 = 4; // 0x4
     field public static final int MPEG4Level2 = 8; // 0x8
     field public static final int MPEG4Level3 = 16; // 0x10
+    field public static final int MPEG4Level3b = 24; // 0x18
     field public static final int MPEG4Level4 = 32; // 0x20
     field public static final int MPEG4Level4a = 64; // 0x40
     field public static final int MPEG4Level5 = 128; // 0x80
+    field public static final int MPEG4Level6 = 256; // 0x100
     field public static final int MPEG4ProfileAdvancedCoding = 4096; // 0x1000
     field public static final int MPEG4ProfileAdvancedCore = 8192; // 0x2000
     field public static final int MPEG4ProfileAdvancedRealTime = 1024; // 0x400
@@ -34633,8 +34636,8 @@
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
-    method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
-    method public final void requestUnbind() throws android.os.RemoteException;
+    method public static void requestRebind(android.content.ComponentName);
+    method public final void requestUnbind();
     method public final void setNotificationsShown(java.lang.String[]);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 27188f0..a6f612d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5195,7 +5195,7 @@
 
   public static class Notification.BigPictureStyle extends android.app.Notification.Style {
     ctor public Notification.BigPictureStyle();
-    ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.BigPictureStyle(android.app.Notification.Builder);
     method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
     method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
@@ -5205,7 +5205,7 @@
 
   public static class Notification.BigTextStyle extends android.app.Notification.Style {
     ctor public Notification.BigTextStyle();
-    ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.BigTextStyle(android.app.Notification.Builder);
     method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
     method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence);
     method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
@@ -5306,12 +5306,10 @@
 
   public static class Notification.DecoratedCustomViewStyle extends android.app.Notification.Style {
     ctor public Notification.DecoratedCustomViewStyle();
-    ctor public Notification.DecoratedCustomViewStyle(android.app.Notification.Builder);
   }
 
   public static class Notification.DecoratedMediaCustomViewStyle extends android.app.Notification.MediaStyle {
     ctor public Notification.DecoratedMediaCustomViewStyle();
-    ctor public Notification.DecoratedMediaCustomViewStyle(android.app.Notification.Builder);
   }
 
   public static abstract interface Notification.Extender {
@@ -5320,7 +5318,7 @@
 
   public static class Notification.InboxStyle extends android.app.Notification.Style {
     ctor public Notification.InboxStyle();
-    ctor public Notification.InboxStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.InboxStyle(android.app.Notification.Builder);
     method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence);
     method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence);
     method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence);
@@ -5328,7 +5326,7 @@
 
   public static class Notification.MediaStyle extends android.app.Notification.Style {
     ctor public Notification.MediaStyle();
-    ctor public Notification.MediaStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.MediaStyle(android.app.Notification.Builder);
     method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
     method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
   }
@@ -21092,7 +21090,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+    method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -21246,7 +21244,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
+    method public void onRecordingConfigChanged(java.util.List<android.media.AudioRecordingConfiguration>);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21294,6 +21292,7 @@
     method public void stop() throws java.lang.IllegalStateException;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int READ_BLOCKING = 0; // 0x0
     field public static final int READ_NON_BLOCKING = 1; // 0x1
@@ -21418,6 +21417,7 @@
     method public int write(java.nio.ByteBuffer, int, int, long);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
@@ -22075,6 +22075,7 @@
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelHP = 4; // 0x4
     field public static final int MPEG2LevelLL = 0; // 0x0
     field public static final int MPEG2LevelML = 1; // 0x1
     field public static final int MPEG2Profile422 = 2; // 0x2
@@ -22088,9 +22089,11 @@
     field public static final int MPEG4Level1 = 4; // 0x4
     field public static final int MPEG4Level2 = 8; // 0x8
     field public static final int MPEG4Level3 = 16; // 0x10
+    field public static final int MPEG4Level3b = 24; // 0x18
     field public static final int MPEG4Level4 = 32; // 0x20
     field public static final int MPEG4Level4a = 64; // 0x40
     field public static final int MPEG4Level5 = 128; // 0x80
+    field public static final int MPEG4Level6 = 256; // 0x100
     field public static final int MPEG4ProfileAdvancedCoding = 4096; // 0x1000
     field public static final int MPEG4ProfileAdvancedCore = 8192; // 0x2000
     field public static final int MPEG4ProfileAdvancedRealTime = 1024; // 0x400
@@ -37359,8 +37362,8 @@
     method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
-    method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
-    method public final void requestUnbind() throws android.os.RemoteException;
+    method public static void requestRebind(android.content.ComponentName);
+    method public final void requestUnbind();
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void setOnNotificationPostedTrim(int);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 15a2d30..37bc7af 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5049,7 +5049,7 @@
 
   public static class Notification.BigPictureStyle extends android.app.Notification.Style {
     ctor public Notification.BigPictureStyle();
-    ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.BigPictureStyle(android.app.Notification.Builder);
     method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap);
     method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap);
@@ -5059,7 +5059,7 @@
 
   public static class Notification.BigTextStyle extends android.app.Notification.Style {
     ctor public Notification.BigTextStyle();
-    ctor public Notification.BigTextStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.BigTextStyle(android.app.Notification.Builder);
     method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence);
     method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence);
     method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
@@ -5160,12 +5160,10 @@
 
   public static class Notification.DecoratedCustomViewStyle extends android.app.Notification.Style {
     ctor public Notification.DecoratedCustomViewStyle();
-    ctor public Notification.DecoratedCustomViewStyle(android.app.Notification.Builder);
   }
 
   public static class Notification.DecoratedMediaCustomViewStyle extends android.app.Notification.MediaStyle {
     ctor public Notification.DecoratedMediaCustomViewStyle();
-    ctor public Notification.DecoratedMediaCustomViewStyle(android.app.Notification.Builder);
   }
 
   public static abstract interface Notification.Extender {
@@ -5174,7 +5172,7 @@
 
   public static class Notification.InboxStyle extends android.app.Notification.Style {
     ctor public Notification.InboxStyle();
-    ctor public Notification.InboxStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.InboxStyle(android.app.Notification.Builder);
     method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence);
     method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence);
     method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence);
@@ -5182,7 +5180,7 @@
 
   public static class Notification.MediaStyle extends android.app.Notification.Style {
     ctor public Notification.MediaStyle();
-    ctor public Notification.MediaStyle(android.app.Notification.Builder);
+    ctor public deprecated Notification.MediaStyle(android.app.Notification.Builder);
     method public android.app.Notification.MediaStyle setMediaSession(android.media.session.MediaSession.Token);
     method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
   }
@@ -19654,7 +19652,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+    method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -19800,7 +19798,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
+    method public void onRecordingConfigChanged(java.util.List<android.media.AudioRecordingConfiguration>);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19847,6 +19845,7 @@
     method public void stop() throws java.lang.IllegalStateException;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int READ_BLOCKING = 0; // 0x0
     field public static final int READ_NON_BLOCKING = 1; // 0x1
@@ -19969,6 +19968,7 @@
     method public int write(java.nio.ByteBuffer, int, int, long);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
@@ -20626,6 +20626,7 @@
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelHP = 4; // 0x4
     field public static final int MPEG2LevelLL = 0; // 0x0
     field public static final int MPEG2LevelML = 1; // 0x1
     field public static final int MPEG2Profile422 = 2; // 0x2
@@ -20639,9 +20640,11 @@
     field public static final int MPEG4Level1 = 4; // 0x4
     field public static final int MPEG4Level2 = 8; // 0x8
     field public static final int MPEG4Level3 = 16; // 0x10
+    field public static final int MPEG4Level3b = 24; // 0x18
     field public static final int MPEG4Level4 = 32; // 0x20
     field public static final int MPEG4Level4a = 64; // 0x40
     field public static final int MPEG4Level5 = 128; // 0x80
+    field public static final int MPEG4Level6 = 256; // 0x100
     field public static final int MPEG4ProfileAdvancedCoding = 4096; // 0x1000
     field public static final int MPEG4ProfileAdvancedCore = 8192; // 0x2000
     field public static final int MPEG4ProfileAdvancedRealTime = 1024; // 0x400
@@ -34710,8 +34713,8 @@
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
-    method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
-    method public final void requestUnbind() throws android.os.RemoteException;
+    method public static void requestRebind(android.content.ComponentName);
+    method public final void requestUnbind();
     method public final void setNotificationsShown(java.lang.String[]);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/cmds/uiautomator/api/current.txt b/cmds/uiautomator/api/current.txt
index 7eeecf5..634ca4d 100644
--- a/cmds/uiautomator/api/current.txt
+++ b/cmds/uiautomator/api/current.txt
@@ -1,6 +1,6 @@
 package com.android.uiautomator.core {
 
-  public final class Configurator {
+  public final deprecated class Configurator {
     method public long getActionAcknowledgmentTimeout();
     method public static com.android.uiautomator.core.Configurator getInstance();
     method public long getKeyInjectionDelay();
@@ -14,7 +14,7 @@
     method public com.android.uiautomator.core.Configurator setWaitForSelectorTimeout(long);
   }
 
-  public class UiCollection extends com.android.uiautomator.core.UiObject {
+  public deprecated class UiCollection extends com.android.uiautomator.core.UiObject {
     ctor public UiCollection(com.android.uiautomator.core.UiSelector);
     method public com.android.uiautomator.core.UiObject getChildByDescription(com.android.uiautomator.core.UiSelector, java.lang.String) throws com.android.uiautomator.core.UiObjectNotFoundException;
     method public com.android.uiautomator.core.UiObject getChildByInstance(com.android.uiautomator.core.UiSelector, int) throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -22,7 +22,7 @@
     method public int getChildCount(com.android.uiautomator.core.UiSelector);
   }
 
-  public class UiDevice {
+  public deprecated class UiDevice {
     method public void clearLastTraversedText();
     method public boolean click(int, int);
     method public boolean drag(int, int, int, int, int);
@@ -77,7 +77,7 @@
     method public void wakeUp() throws android.os.RemoteException;
   }
 
-  public class UiObject {
+  public deprecated class UiObject {
     ctor public UiObject(com.android.uiautomator.core.UiSelector);
     method public void clearTextField() throws com.android.uiautomator.core.UiObjectNotFoundException;
     method public boolean click() throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -130,13 +130,13 @@
     field protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500L; // 0x157cL
   }
 
-  public class UiObjectNotFoundException extends java.lang.Exception {
+  public deprecated class UiObjectNotFoundException extends java.lang.Exception {
     ctor public UiObjectNotFoundException(java.lang.String);
     ctor public UiObjectNotFoundException(java.lang.String, java.lang.Throwable);
     ctor public UiObjectNotFoundException(java.lang.Throwable);
   }
 
-  public class UiScrollable extends com.android.uiautomator.core.UiCollection {
+  public deprecated class UiScrollable extends com.android.uiautomator.core.UiCollection {
     ctor public UiScrollable(com.android.uiautomator.core.UiSelector);
     method protected boolean exists(com.android.uiautomator.core.UiSelector);
     method public boolean flingBackward() throws com.android.uiautomator.core.UiObjectNotFoundException;
@@ -165,7 +165,7 @@
     method public com.android.uiautomator.core.UiScrollable setSwipeDeadZonePercentage(double);
   }
 
-  public class UiSelector {
+  public deprecated class UiSelector {
     ctor public UiSelector();
     method public com.android.uiautomator.core.UiSelector checkable(boolean);
     method public com.android.uiautomator.core.UiSelector checked(boolean);
@@ -198,7 +198,7 @@
     method public com.android.uiautomator.core.UiSelector textStartsWith(java.lang.String);
   }
 
-  public abstract interface UiWatcher {
+  public abstract deprecated interface UiWatcher {
     method public abstract boolean checkForCondition();
   }
 
@@ -206,11 +206,11 @@
 
 package com.android.uiautomator.testrunner {
 
-  public abstract interface IAutomationSupport {
+  public abstract deprecated interface IAutomationSupport {
     method public abstract void sendStatus(int, android.os.Bundle);
   }
 
-  public class UiAutomatorTestCase extends junit.framework.TestCase {
+  public deprecated class UiAutomatorTestCase extends junit.framework.TestCase {
     ctor public UiAutomatorTestCase();
     method public com.android.uiautomator.testrunner.IAutomationSupport getAutomationSupport();
     method public android.os.Bundle getParams();
diff --git a/cmds/uiautomator/cmds/uiautomator/uiautomator b/cmds/uiautomator/cmds/uiautomator/uiautomator
index fe2c735..86a1dba 100755
--- a/cmds/uiautomator/cmds/uiautomator/uiautomator
+++ b/cmds/uiautomator/cmds/uiautomator/uiautomator
@@ -67,6 +67,11 @@
 
 # special case pre-processing for 'runtest' command
 if [ "${cmd}" == "runtest" ]; then
+  # Print deprecation warning
+  echo "Warning: This version of UI Automator is deprecated. New tests should be written using"
+  echo "UI Automator 2.0 which is available as part of the Android Testing Support Library."
+  echo "See https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html"
+  echo "for more details."
   # first parse the jar paths
   while [ true ]; do
     if [ -z "${1}" ] && [ -z "${jars}" ]; then
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
index f0c60d2..6d75844 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -22,7 +22,10 @@
  * Provides auxiliary support for running test cases
  *
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public interface IAutomationSupport {
 
     /**
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
index ae763f2..af42653 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorInstrumentationTestRunner.java
@@ -28,7 +28,10 @@
 /**
  * Test runner for {@link UiAutomatorTestCase}s. Such tests are executed
  * on the device and have access to an applications context.
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiAutomatorInstrumentationTestRunner extends InstrumentationTestRunner {
 
     @Override
diff --git a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index b5f21c9..7c9aede 100644
--- a/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/instrumentation/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -26,7 +26,10 @@
 
 /**
  * UI Automator test case that is executed on the device.
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiAutomatorTestCase extends InstrumentationTestCase {
 
     private Bundle mParams;
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 62fb0e4..ea77fbc 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -67,7 +67,6 @@
 # Make sure to run droiddoc first to generate the stub source files.
 $(full_classes_compiled_jar) : $(uiautomator_stubs_stamp)
 $(built_dex_intermediate) : $(uiautomator_stubs_stamp)
-uiautomator_stubs_jar := $(full_classes_compiled_jar)
 
 ###############################################
 # API check
@@ -92,7 +91,7 @@
     $(uiautomator_internal_removed_api_file), \
     $(checkapi_last_error_level_flags), \
     cat $(LOCAL_PATH)/apicheck_msg_last.txt, \
-    $(uiautomator_stubs_jar), \
+    uiautomator.core, \
     $(uiautomator_stubs_stamp)))
 
 checkapi_current_error_level_flags := \
@@ -111,7 +110,7 @@
     $(uiautomator_internal_removed_api_file), \
     $(checkapi_current_error_level_flags), \
     cat $(LOCAL_PATH)/apicheck_msg_current.txt, \
-    $(uiautomator_stubs_jar), \
+    uiautomator.core, \
     $(uiautomator_stubs_stamp)))
 
 .PHONY: update-uiautomator-api
@@ -128,7 +127,6 @@
 uiautomator.core_java_libraries :=
 uiautomator_stubs_stamp :=
 uiautomator_internal_api_file :=
-uiautomator_stubs_jar :=
 uiautomator_api_dir :=
 checkapi_last_error_level_flags :=
 checkapi_current_error_level_flags :=
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
index 249f404..255c49a 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/Configurator.java
@@ -25,7 +25,10 @@
  * tests with the modified parameters, make sure to also restore
  * the original parameter values, otherwise this will impact other tests cases.
  * @since API Level 18
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public final class Configurator {
     private long mWaitForIdleTimeout = 10 * 1000;
     private long mWaitForSelector = 10 * 1000;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
index e15beb2..06bacb0 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiCollection.java
@@ -19,7 +19,10 @@
  * Used to enumerate a container's UI elements for the purpose of counting,
  * or targeting a sub elements by a child's text or description.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiCollection extends UiObject {
 
     /**
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
index a930eb4..b1b432b 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java
@@ -42,7 +42,10 @@
  * You can also use this class to simulate user actions on the device,
  * such as pressing the d-pad or pressing the Home and Menu buttons.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiDevice {
     private static final String LOG_TAG = UiDevice.class.getSimpleName();
 
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
index 2118da8..751bbe8 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
@@ -31,7 +31,10 @@
  * its constructor. Once you create an instance of a UiObject, it can
  * be reused for different views that match the selector criteria.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiObject {
     private static final String LOG_TAG = UiObject.class.getSimpleName();
     /**
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
index fc0891b..f3e2698 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObjectNotFoundException.java
@@ -20,7 +20,10 @@
  * Generated in test runs when a {@link UiSelector} selector could not be matched
  * to any UI element displayed.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiObjectNotFoundException extends Exception {
 
     private static final long serialVersionUID = 1L;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
index a8d20c3..6673dac 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiScrollable.java
@@ -24,7 +24,10 @@
  * for items in scrollable layout elements. This class can be used with
  * horizontally or vertically scrollable controls.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiScrollable extends UiCollection {
     private static final String LOG_TAG = UiScrollable.class.getSimpleName();
 
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
index 482a74d..9ba425e 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiSelector.java
@@ -27,7 +27,10 @@
  * information. You can also target an element by its location in a layout
  * hierarchy.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiSelector {
     static final int SELECTOR_NIL = 0;
     static final int SELECTOR_TEXT = 1;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
index 5403e30..030fb0a 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiWatcher.java
@@ -22,7 +22,10 @@
  * to locate its selector yet. Only during this time, the watchers are invoked to check if there is
  * something else unexpected on the screen.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public interface UiWatcher {
 
     /**
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
index f0c60d2..6d75844 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/IAutomationSupport.java
@@ -22,7 +22,10 @@
  * Provides auxiliary support for running test cases
  *
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public interface IAutomationSupport {
 
     /**
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
index e7d961b..3d5476d 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/testrunner/UiAutomatorTestCase.java
@@ -36,7 +36,10 @@
  * {@link UiDevice} instance
  * {@link Bundle} for command line parameters.
  * @since API Level 16
+ * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
+ * Android Testing Support Library.
  */
+@Deprecated
 public class UiAutomatorTestCase extends TestCase {
 
     private static final String DISABLE_IME = "disable_ime";
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6ba6b0f..cd9a05b 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -7015,6 +7015,7 @@
             data.writeInt(0);
         }
         mRemote.transact(SEND_INTENT_SENDER_TRANSACTION, data, reply, 0);
+        reply.readException();
         final int res = reply.readInt();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dff0769..30753c1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,6 +62,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
@@ -130,6 +131,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TimeZone;
@@ -4684,6 +4686,8 @@
                     + config);
 
             mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+            updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+                    mResourcesManager.getConfiguration().getLocales());
 
             if (mConfiguration == null) {
                 mConfiguration = new Configuration();
@@ -4989,6 +4993,24 @@
         return insInfo.nativeLibraryDir;
     }
 
+    /**
+     * The LocaleList set for the app's resources may have been shuffled so that the preferred
+     * Locale is at position 0. We must find the index of this preferred Locale in the
+     * original LocaleList.
+     */
+    private void updateLocaleListFromAppContext(Context context, LocaleList newLocaleList) {
+        final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0);
+        final int newLocaleListSize = newLocaleList.size();
+        for (int i = 0; i < newLocaleListSize; i++) {
+            if (bestLocale.equals(newLocaleList.get(i))) {
+                LocaleList.setDefault(newLocaleList, i);
+                return;
+            }
+        }
+        throw new AssertionError("chosen locale " + bestLocale + " must be present in LocaleList: "
+                + newLocaleList.toLanguageTags());
+    }
+
     private void handleBindApplication(AppBindData data) {
         // Register the UI Thread as a sensitive thread to the runtime.
         VMRuntime.registerSensitiveThread();
@@ -5047,6 +5069,24 @@
          */
         TimeZone.setDefault(null);
 
+        /*
+         * Set the LocaleList. This may change once we create the App Context.
+         */
+        LocaleList.setDefault(data.config.getLocales());
+
+        synchronized (mResourcesManager) {
+            /*
+             * Update the system configuration since its preloaded and might not
+             * reflect configuration changes. The configuration object passed
+             * in AppBindData can be safely assumed to be up to date
+             */
+            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+            mCurDefaultDisplayDpi = data.config.densityDpi;
+
+            // This calls mResourcesManager so keep it within the synchronized block.
+            applyCompatConfiguration(mCurDefaultDisplayDpi);
+        }
+
         data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
         /**
@@ -5174,25 +5214,8 @@
         }
 
         final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
-        synchronized (mResourcesManager) {
-            /*
-             * Initialize the default locales in this process for the reasons we set the time zone.
-             *
-             * We do this through ResourcesManager, since we need to do locale negotiation.
-             */
-            mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
-
-            /*
-             * Update the system configuration since its preloaded and might not
-             * reflect configuration changes. The configuration object passed
-             * in AppBindData can be safely assumed to be up to date
-             */
-            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
-            mCurDefaultDisplayDpi = data.config.densityDpi;
-
-            // This calls mResourcesManager so keep it within the synchronized block.
-            applyCompatConfiguration(mCurDefaultDisplayDpi);
-        }
+        updateLocaleListFromAppContext(appContext,
+                mResourcesManager.getConfiguration().getLocales());
 
         if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
             // This cache location probably points at credential-encrypted
@@ -5895,6 +5918,9 @@
                     // immediately, because upon returning the view
                     // hierarchy will be informed about it.
                     if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
+                        updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+                                mResourcesManager.getConfiguration().getLocales());
+
                         // This actually changed the resources!  Tell
                         // everyone about it.
                         if (mPendingConfiguration == null ||
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bd94a17..0cc44e7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -58,6 +58,7 @@
 import android.view.Gravity;
 import android.view.NotificationHeaderView;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
 
@@ -3106,6 +3107,11 @@
         }
 
         private Drawable getProfileBadgeDrawable() {
+            if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
+                // This user can never be a badged profile,
+                // and also includes USER_ALL system notifications.
+                return null;
+            }
             // Note: This assumes that the current user can read the profile badge of the
             // originating user.
             return mContext.getPackageManager().getUserBadgeForDensityNoBackground(
@@ -3197,11 +3203,14 @@
 
             bindNotificationHeader(contentView);
             bindLargeIcon(contentView);
+            boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
             if (title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, title);
+                contentView.setViewLayoutWidth(R.id.title, showProgress
+                        ? ViewGroup.LayoutParams.WRAP_CONTENT
+                        : ViewGroup.LayoutParams.MATCH_PARENT);
             }
-            boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
             if (text != null) {
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
@@ -4082,6 +4091,10 @@
         public BigPictureStyle() {
         }
 
+        /**
+         * @deprecated use {@code BigPictureStyle()}.
+         */
+        @Deprecated
         public BigPictureStyle(Builder builder) {
             setBuilder(builder);
         }
@@ -4238,6 +4251,10 @@
         public BigTextStyle() {
         }
 
+        /**
+         * @deprecated use {@code BigTextStyle()}.
+         */
+        @Deprecated
         public BigTextStyle(Builder builder) {
             setBuilder(builder);
         }
@@ -4780,6 +4797,10 @@
         public InboxStyle() {
         }
 
+        /**
+         * @deprecated use {@code InboxStyle()}.
+         */
+        @Deprecated
         public InboxStyle(Builder builder) {
             setBuilder(builder);
         }
@@ -4952,6 +4973,10 @@
         public MediaStyle() {
         }
 
+        /**
+         * @deprecated use {@code MediaStyle()}.
+         */
+        @Deprecated
         public MediaStyle(Builder builder) {
             setBuilder(builder);
         }
@@ -5159,10 +5184,6 @@
         public DecoratedCustomViewStyle() {
         }
 
-        public DecoratedCustomViewStyle(Builder builder) {
-            setBuilder(builder);
-        }
-
         /**
          * @hide
          */
@@ -5279,10 +5300,6 @@
         public DecoratedMediaCustomViewStyle() {
         }
 
-        public DecoratedMediaCustomViewStyle(Builder builder) {
-            setBuilder(builder);
-        }
-
         /**
          * @hide
          */
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b4e9db8..25a8b66 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -67,10 +67,6 @@
                 }
             };
 
-    private String[] mSystemLocales = null;
-    private final HashSet<String> mNonSystemLocales = new HashSet<>();
-    private boolean mHasNonSystemLocales = false;
-
     /**
      * The global compatibility settings.
      */
@@ -479,12 +475,7 @@
      */
     private Resources getOrCreateResources(@Nullable IBinder activityToken,
             @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
-        final boolean findSystemLocales;
-        final boolean hasNonSystemLocales;
         synchronized (this) {
-            findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
-            hasNonSystemLocales = mHasNonSystemLocales;
-
             if (DEBUG) {
                 Throwable here = new Throwable();
                 here.fillInStackTrace();
@@ -538,24 +529,7 @@
         // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
         ResourcesImpl resourcesImpl = createResourcesImpl(key);
 
-        final String[] systemLocales = findSystemLocales
-                ? AssetManager.getSystem().getLocales() : null;
-        final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
-
-        // Avoid checking for non-pseudo-locales if we already know there were some from a previous
-        // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
-        // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
-        // able to affect mHasNonSystemLocales.
-        final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
-                LocaleList.isPseudoLocalesOnly(nonSystemLocales);
-
         synchronized (this) {
-            if (mSystemLocales == null || mSystemLocales.length == 0) {
-                mSystemLocales = systemLocales;
-            }
-            mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
-            mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
-
             ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
             if (existingResourcesImpl != null) {
                 if (DEBUG) {
@@ -745,23 +719,6 @@
         }
     }
 
-    /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
-        if (mSystemLocales == null) {
-            throw new RuntimeException("ResourcesManager is not ready to negotiate locales.");
-        }
-        final int bestLocale;
-        if (mHasNonSystemLocales) {
-            bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales);
-        } else {
-            // We fallback to system locales if there was no locale specifically supported by the
-            // assets. This is to properly support apps that only rely on the shared system assets
-            // and don't need assets of their own.
-            bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mSystemLocales);
-        }
-        // set it for Java, this also affects newly created Resources
-        LocaleList.setDefault(locales, bestLocale);
-    }
-
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                              @Nullable CompatibilityInfo compat) {
         try {
@@ -786,30 +743,7 @@
                         | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
             }
 
-            Configuration localeAdjustedConfig = config;
-            final LocaleList configLocales = config.getLocales();
-            if (!configLocales.isEmpty()) {
-                setDefaultLocalesLocked(configLocales);
-                final LocaleList adjustedLocales = LocaleList.getAdjustedDefault();
-                if (adjustedLocales
-                        != configLocales) { // has the same result as .equals() in this case
-                    // The first locale in the list was not chosen. So we create a modified
-                    // configuration with the adjusted locales (which moves the chosen locale to the
-                    // front).
-                    localeAdjustedConfig = new Configuration();
-                    localeAdjustedConfig.setTo(config);
-                    localeAdjustedConfig.setLocales(adjustedLocales);
-                    // Also adjust the locale list in mResConfiguration, so that the Resources
-                    // created later would have the same locale list.
-                    if (!mResConfiguration.getLocales().equals(adjustedLocales)) {
-                        mResConfiguration.setLocales(adjustedLocales);
-                        changes |= ActivityInfo.CONFIG_LOCALE;
-                    }
-                }
-            }
-
-            Resources.updateSystemConfiguration(localeAdjustedConfig, defaultDisplayMetrics,
-                    compat);
+            Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
 
             ApplicationPackageManager.configurationChanged();
             //Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -821,7 +755,7 @@
                 ResourcesImpl r = mResourceImpls.valueAt(i).get();
                 if (r != null) {
                     if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
-                            + r + " config to: " + localeAdjustedConfig);
+                            + r + " config to: " + config);
                     int displayId = key.mDisplayId;
                     boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
                     DisplayMetrics dm = defaultDisplayMetrics;
@@ -830,7 +764,7 @@
                         if (tmpConfig == null) {
                             tmpConfig = new Configuration();
                         }
-                        tmpConfig.setTo(localeAdjustedConfig);
+                        tmpConfig.setTo(config);
                         if (!isDefaultDisplay) {
                             dm = getDisplayMetrics(displayId);
                             applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
@@ -840,7 +774,7 @@
                         }
                         r.updateConfiguration(tmpConfig, dm, compat);
                     } else {
-                        r.updateConfiguration(localeAdjustedConfig, dm, compat);
+                        r.updateConfiguration(config, dm, compat);
                     }
                     //Slog.i(TAG, "Updated app resources " + v.getKey()
                     //        + " " + r + ": " + r.getConfiguration());
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index cd8f126..bb5f7a1 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
@@ -150,7 +151,7 @@
             // We add padding to the AppWidgetHostView if necessary
             Rect padding = getDefaultPaddingForWidget(mContext, info.provider, null);
             setPadding(padding.left, padding.top, padding.right, padding.bottom);
-            setContentDescription(info.label);
+            updateContentDescription(info);
         }
     }
 
@@ -459,6 +460,7 @@
         }
 
         applyContent(content, recycled, exception);
+        updateContentDescription(mInfo);
     }
 
     private void applyContent(View content, boolean recycled, Exception exception) {
@@ -493,6 +495,22 @@
         }
     }
 
+    private void updateContentDescription(AppWidgetProviderInfo info) {
+        if (info != null) {
+            LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class);
+            ApplicationInfo appInfo = launcherApps.getApplicationInfo(
+                    info.provider.getPackageName(), 0, info.getProfile());
+            if (appInfo != null &&
+                    (appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0) {
+                setContentDescription(
+                        Resources.getSystem().getString(
+                        com.android.internal.R.string.suspended_widget_accessibility, info.label));
+            } else {
+                setContentDescription(info.label);
+            }
+        }
+    }
+
     private void inflateAsync(RemoteViews remoteViews) {
         // Prepare a local reference to the remote Context so we're ready to
         // inflate any requested LayoutParams.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d036d96..4c86578 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -57,6 +57,7 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
@@ -483,6 +484,8 @@
 
     private final IBluetoothManager mManagerService;
     private IBluetooth mService;
+    private final ReentrantReadWriteLock mServiceLock =
+        new ReentrantReadWriteLock();
 
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
@@ -517,8 +520,13 @@
             throw new IllegalArgumentException("bluetooth manager service is null");
         }
         try {
+            mServiceLock.writeLock().lock();
             mService = managerService.registerAdapter(mManagerCallback);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.writeLock().unlock();
+        }
         mManagerService = managerService;
         mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
         mToken = new Binder();
@@ -605,10 +613,14 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isEnabled() {
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.isEnabled();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.isEnabled();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+
         return false;
     }
 
@@ -639,12 +651,12 @@
      * or OFF if BT is in BLE_ON state
      */
     private void notifyUserAction(boolean enable) {
-        if (mService == null) {
-            Log.e(TAG, "mService is null");
-            return;
-        }
-
         try {
+            mServiceLock.readLock().lock();
+            if (mService == null) {
+                Log.e(TAG, "mService is null");
+                return;
+            }
             if (enable) {
                 mService.onLeServiceUp(); //NA:TODO implementation pending
             } else {
@@ -652,6 +664,8 @@
             }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
     }
 
@@ -783,26 +797,28 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @AdapterState
     public int getState() {
+        int state = BluetoothAdapter.STATE_OFF;
+
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null)
-                {
-                    int state=  mService.getState();
-                    if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
-                    //consider all internal states as OFF
-                    if (state == BluetoothAdapter.STATE_BLE_ON
-                        || state == BluetoothAdapter.STATE_BLE_TURNING_ON
-                        || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-                        if (VDBG) Log.d(TAG, "Consider internal state as OFF");
-                        state = BluetoothAdapter.STATE_OFF;
-                    }
-                    return state;
-                }
-                // TODO(BT) there might be a small gap during STATE_TURNING_ON that
-                //          mService is null, handle that case
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                state = mService.getState();
             }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
-        return STATE_OFF;
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+
+        // Consider all internal states as OFF
+        if (state == BluetoothAdapter.STATE_BLE_ON
+            || state == BluetoothAdapter.STATE_BLE_TURNING_ON
+            || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
+            if (VDBG) Log.d(TAG, "Consider internal state as OFF");
+            state = BluetoothAdapter.STATE_OFF;
+        }
+        if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+        return state;
     }
 
     /**
@@ -825,19 +841,21 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @AdapterState
     public int getLeState() {
+        int state = BluetoothAdapter.STATE_OFF;
+
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null)
-                {
-                    int state=  mService.getState();
-                    if (VDBG) Log.d(TAG,"getLeState() returning " + state);
-                    return state;
-                }
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                state = mService.getState();
             }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
-        return BluetoothAdapter.STATE_OFF;
+
+        if (VDBG) Log.d(TAG,"getLeState() returning " + state);
+        return state;
     }
 
     boolean getLeAccess() {
@@ -879,16 +897,21 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enable() {
-        int state = STATE_OFF;
-        if (isEnabled() == true){
+        int state = BluetoothAdapter.STATE_OFF;
+        if (isEnabled() == true) {
             if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
             return true;
         }
-        //Use service interface to get the exact state
-        if (mService != null) {
-            try {
-               state = mService.getState();
-            } catch (RemoteException e) {Log.e(TAG, "", e);}
+        // Use service interface to get the exact state
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                state = mService.getState();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
 
         if (state == BluetoothAdapter.STATE_BLE_ON) {
@@ -993,10 +1016,13 @@
      */
     public boolean configHciSnoopLog(boolean enable) {
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.configHciSnoopLog(enable);
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.configHciSnoopLog(enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1012,12 +1038,16 @@
      */
     public boolean factoryReset() {
         try {
+            mServiceLock.readLock().lock();
             if (mService != null) {
                 return mService.factoryReset();
-            } else {
-                SystemProperties.set("persist.bluetooth.factoryreset", "true");
             }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            SystemProperties.set("persist.bluetooth.factoryreset", "true");
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1032,10 +1062,13 @@
     public ParcelUuid[] getUuids() {
         if (getState() != STATE_ON) return null;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.getUuids();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getUuids();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return null;
     }
 
@@ -1058,10 +1091,13 @@
     public boolean setName(String name) {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.setName(name);
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.setName(name);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1086,10 +1122,13 @@
     public int getScanMode() {
         if (getState() != STATE_ON) return SCAN_MODE_NONE;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.getScanMode();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getScanMode();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return SCAN_MODE_NONE;
     }
 
@@ -1124,10 +1163,13 @@
     public boolean setScanMode(@ScanMode int mode, int duration) {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.setScanMode(mode, duration);
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.setScanMode(mode, duration);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1142,10 +1184,13 @@
     public int getDiscoverableTimeout() {
         if (getState() != STATE_ON) return -1;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.getDiscoverableTimeout();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getDiscoverableTimeout();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return -1;
     }
 
@@ -1153,10 +1198,13 @@
     public void setDiscoverableTimeout(int timeout) {
         if (getState() != STATE_ON) return;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) mService.setDiscoverableTimeout(timeout);
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) mService.setDiscoverableTimeout(timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
     }
 
     /**
@@ -1193,10 +1241,13 @@
     public boolean startDiscovery() {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.startDiscovery();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.startDiscovery();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1221,10 +1272,13 @@
     public boolean cancelDiscovery() {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.cancelDiscovery();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.cancelDiscovery();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1251,10 +1305,13 @@
     public boolean isDiscovering() {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null ) return mService.isDiscovering();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.isDiscovering();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return false;
     }
 
@@ -1266,9 +1323,12 @@
     public boolean isMultipleAdvertisementSupported() {
         if (getState() != STATE_ON) return false;
         try {
-            return mService.isMultiAdvertisementSupported();
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.isMultiAdvertisementSupported();
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
         return false;
     }
@@ -1301,9 +1361,12 @@
     public boolean isPeripheralModeSupported() {
         if (getState() != STATE_ON) return false;
         try {
-            return mService.isPeripheralModeSupported();
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.isPeripheralModeSupported();
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get peripheral mode capability: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
         return false;
     }
@@ -1316,9 +1379,12 @@
     public boolean isOffloadedFilteringSupported() {
         if (!getLeAccess()) return false;
         try {
-            return mService.isOffloadedFilteringSupported();
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.isOffloadedFilteringSupported();
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
         return false;
     }
@@ -1331,9 +1397,12 @@
     public boolean isOffloadedScanBatchingSupported() {
         if (!getLeAccess()) return false;
         try {
-            return mService.isOffloadedScanBatchingSupported();
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.isOffloadedScanBatchingSupported();
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
         return false;
     }
@@ -1399,15 +1468,15 @@
      */
     public void requestControllerActivityEnergyInfo(ResultReceiver result) {
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) {
-                    mService.requestActivityInfo(result);
-                    result = null;
-                }
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                mService.requestActivityInfo(result);
+                result = null;
             }
         } catch (RemoteException e) {
             Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e);
         } finally {
+            mServiceLock.readLock().unlock();
             if (result != null) {
                 // Only send an immediate result if we failed.
                 result.send(0, null);
@@ -1432,11 +1501,14 @@
             return toDeviceSet(new BluetoothDevice[0]);
         }
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return toDeviceSet(mService.getBondedDevices());
-            }
+            mServiceLock.readLock().lock();
+            if (mService != null) return toDeviceSet(mService.getBondedDevices());
             return toDeviceSet(new BluetoothDevice[0]);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return null;
     }
 
@@ -1456,10 +1528,13 @@
     public int getConnectionState() {
         if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.getAdapterConnectionState();
-            }
-        } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);}
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getAdapterConnectionState();
+        } catch (RemoteException e) {
+            Log.e(TAG, "getConnectionState:", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
         return BluetoothAdapter.STATE_DISCONNECTED;
     }
 
@@ -1482,11 +1557,12 @@
     public int getProfileConnectionState(int profile) {
         if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
         try {
-            synchronized(mManagerCallback) {
-                if (mService != null) return mService.getProfileConnectionState(profile);
-            }
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getProfileConnectionState(profile);
         } catch (RemoteException e) {
             Log.e(TAG, "getProfileConnectionState:", e);
+        } finally {
+            mServiceLock.readLock().unlock();
         }
         return BluetoothProfile.STATE_DISCONNECTED;
     }
@@ -1790,7 +1866,9 @@
             byte[] hash;
             byte[] randomizer;
 
-            byte[] ret = mService.readOutOfBandData();
+            byte[] ret = null;
+            mServiceLock.readLock().lock();
+            if (mService != null) mService.readOutOfBandData();
 
             if (ret  == null || ret.length != 32) return null;
 
@@ -1803,7 +1881,12 @@
             }
             return new Pair<byte[], byte[]>(hash, randomizer);
 
-        } catch (RemoteException e) {Log.e(TAG, "", e);}*/
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        */
         return null;
     }
 
@@ -1939,17 +2022,21 @@
         new IBluetoothManagerCallback.Stub() {
             public void onBluetoothServiceUp(IBluetooth bluetoothService) {
                 if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
-                synchronized (mManagerCallback) {
-                    mService = bluetoothService;
-                    synchronized (mProxyServiceStateCallbacks) {
-                        for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceUp(bluetoothService);
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
-                                }
-                            } catch (Exception e)  { Log.e(TAG,"",e);}
+
+                mServiceLock.writeLock().lock();
+                mService = bluetoothService;
+                mServiceLock.writeLock().unlock();
+
+                synchronized (mProxyServiceStateCallbacks) {
+                    for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ) {
+                        try {
+                            if (cb != null) {
+                                cb.onBluetoothServiceUp(bluetoothService);
+                            } else {
+                                Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
+                            }
+                        } catch (Exception e) {
+                            Log.e(TAG,"",e);
                         }
                     }
                 }
@@ -1957,20 +2044,24 @@
 
             public void onBluetoothServiceDown() {
                 if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
-                synchronized (mManagerCallback) {
-                    mService = null;
-                    if (mLeScanClients != null) mLeScanClients.clear();
-                    if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
-                    if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
-                    synchronized (mProxyServiceStateCallbacks) {
-                        for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceDown();
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
-                                }
-                            } catch (Exception e)  { Log.e(TAG,"",e);}
+
+                mServiceLock.writeLock().lock();
+                mService = null;
+                if (mLeScanClients != null) mLeScanClients.clear();
+                if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
+                if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
+                mServiceLock.writeLock().unlock();
+
+                synchronized (mProxyServiceStateCallbacks) {
+                    for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
+                        try {
+                            if (cb != null) {
+                                cb.onBluetoothServiceDown();
+                            } else {
+                                Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
+                            }
+                        } catch (Exception e) {
+                            Log.e(TAG,"",e);
                         }
                     }
                 }
@@ -2033,11 +2124,17 @@
         //TODO(BT)
         /*
         try {
-            return mService.changeApplicationBluetoothState(on, new
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.changeApplicationBluetoothState(on, new
                     StateChangeCallbackWrapper(callback), new Binder());
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "changeBluetoothState", e);
-        }*/
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        */
         return false;
     }
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index c1aac85..f6445e6 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1446,7 +1446,7 @@
      *
      * @return The locale list.
      */
-    public LocaleList getLocales() {
+    public @NonNull LocaleList getLocales() {
         fixUpLocaleList();
         return mLocaleList;
     }
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index dada612..32a2795 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -330,18 +330,43 @@
                 // doing the conversion here.  This impl should be okay because
                 // we make sure to return a compatible display in the places
                 // where there are public APIs to retrieve the display...  but
-                // it would be cleaner and more maintainble to just be
+                // it would be cleaner and more maintainable to just be
                 // consistently dealing with a compatible display everywhere in
                 // the framework.
                 mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
 
                 final @Config int configChanges = calcConfigChanges(config);
 
+                // If even after the update there are no Locales set, grab the default locales.
                 LocaleList locales = mConfiguration.getLocales();
                 if (locales.isEmpty()) {
-                    locales = LocaleList.getAdjustedDefault();
+                    locales = LocaleList.getDefault();
                     mConfiguration.setLocales(locales);
                 }
+
+                if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+                    if (locales.size() > 1) {
+                        // The LocaleList has changed. We must query the AssetManager's available
+                        // Locales and figure out the best matching Locale in the new LocaleList.
+                        String[] availableLocales = mAssets.getNonSystemLocales();
+                        if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+                            // No app defined locales, so grab the system locales.
+                            availableLocales = mAssets.getLocales();
+                            if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
+                                availableLocales = null;
+                            }
+                        }
+
+                        if (availableLocales != null) {
+                            final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+                                    availableLocales);
+                            if (bestLocale != null && bestLocale != locales.get(0)) {
+                                mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+                            }
+                        }
+                    }
+                }
+
                 if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
                     mMetrics.densityDpi = mConfiguration.densityDpi;
                     mMetrics.density =
@@ -370,7 +395,7 @@
                 }
 
                 mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
-                        adjustLanguageTag(locales.get(0).toLanguageTag()),
+                        adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
                         mConfiguration.orientation,
                         mConfiguration.touchscreen,
                         mConfiguration.densityDpi, mConfiguration.keyboard,
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 8136796..60b618a 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -409,6 +409,14 @@
     }
 
     /**
+     * {@hide}
+     */
+    public int getFirstMatchIndex(String[] supportedLocales) {
+        return computeFirstMatchIndex(Arrays.asList(supportedLocales),
+                false /* assume English is not supported */);
+    }
+
+    /**
      * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
      * {@hide}
      */
@@ -437,7 +445,11 @@
      * Assumes that there is no repetition in the input.
      * {@hide}
      */
-    public static boolean isPseudoLocalesOnly(String[] supportedLocales) {
+    public static boolean isPseudoLocalesOnly(@Nullable String[] supportedLocales) {
+        if (supportedLocales == null) {
+            return true;
+        }
+
         if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
             // This is for optimization. Since there's no repetition in the input, if we have more
             // than the number of pseudo-locales plus one for the empty string, it's guaranteed
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 403e06c..4abbf0e 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -25,8 +25,10 @@
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
@@ -45,6 +47,8 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+import com.android.internal.logging.MetricsLogger;
+
 import sun.security.pkcs.PKCS7;
 import sun.security.pkcs.SignerInfo;
 
@@ -69,6 +73,7 @@
     /** Used to communicate with recovery.  See bootable/recovery/recovery.cpp. */
     private static final File RECOVERY_DIR = new File("/cache/recovery");
     private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
+    private static final File LAST_INSTALL_FILE = new File(RECOVERY_DIR, "last_install");
     private static final String LAST_PREFIX = "last_";
 
     /**
@@ -682,13 +687,64 @@
         }
     }
 
+    // Read last_install; then report time for update and I/O to tron.
+    // Only report on the reboots immediately after an OTA update.
+    private static void parseLastInstallLog(Context context) {
+        try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {
+            String line = null;
+            int bytesWritten = -1, bytesStashed = -1;
+            int timeTotal = -1;
+            while ((line = in.readLine()) != null) {
+                // Here is an example of lines in last_install:
+                // ...
+                // time_total: 101
+                // bytes_written_vendor: 51074
+                // bytes_stashed_vendor: 200
+                int numIndex = line.indexOf(':');
+                if (numIndex == -1 || numIndex + 1 >= line.length()) {
+                    continue;
+                }
+                String numString = line.substring(numIndex + 1).trim();
+                int parsedNum;
+                try {
+                    parsedNum = Integer.parseInt(numString);
+                } catch (NumberFormatException ignored) {
+                    Log.e(TAG, "Failed to parse numbers in " + line);
+                    continue;
+                }
+
+                if (line.startsWith("time")) {
+                    timeTotal = parsedNum;
+                } else if (line.startsWith("bytes_written")) {
+                    bytesWritten = (bytesWritten == -1) ? parsedNum : bytesWritten + parsedNum;
+                } else if (line.startsWith("bytes_stashed")) {
+                    bytesStashed = (bytesStashed == -1) ? parsedNum : bytesStashed + parsedNum;
+                }
+            }
+
+            // Don't report data to tron if corresponding entry isn't found in last_install.
+            if (timeTotal != -1) {
+                MetricsLogger.histogram(context, "ota_time_total", timeTotal);
+            }
+            if (bytesWritten != -1) {
+                MetricsLogger.histogram(context, "ota_bytes_written", bytesWritten);
+            }
+            if (bytesStashed != -1) {
+                MetricsLogger.histogram(context, "ota_bytes_stashed", bytesStashed);
+            }
+
+        } catch (IOException ignored) {
+            Log.e(TAG, "Failed to read lines in last_install", ignored);
+        }
+    }
+
     /**
      * Called after booting to process and remove recovery-related files.
      * @return the log file from recovery, or null if none was found.
      *
      * @hide
      */
-    public static String handleAftermath() {
+    public static String handleAftermath(Context context) {
         // Record the tail of the LOG_FILE
         String log = null;
         try {
@@ -699,6 +755,10 @@
             Log.e(TAG, "Error reading recovery log", e);
         }
 
+        if (log != null) {
+            parseLastInstallLog(context);
+        }
+
         // Only remove the OTA package if it's partially processed (uncrypt'd).
         boolean reservePackage = BLOCK_MAP_FILE.exists();
         if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 25fe4ff..1557a27 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -790,11 +790,14 @@
      * <p>This method will fail for listeners that have
      * not been granted the permission by the user.
      */
-    public static void requestRebind(ComponentName componentName)
-            throws RemoteException {
+    public static void requestRebind(ComponentName componentName) {
         INotificationManager noMan = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        noMan.requestBindListener(componentName);
+        try {
+            noMan.requestBindListener(componentName);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -807,12 +810,16 @@
      * <p>The service should wait for the {@link #onListenerConnected()} event
      * before performing this operation. I know it's tempting, but you must wait.
      */
-    public final void requestUnbind() throws RemoteException {
+    public final void requestUnbind() {
         if (mWrapper != null) {
             INotificationManager noMan = getNotificationInterface();
-            noMan.requestUnbindListener(mWrapper);
-            // Disable future messages.
-            isConnected = false;
+            try {
+                noMan.requestUnbindListener(mWrapper);
+                // Disable future messages.
+                isConnected = false;
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
+            }
         }
     }
 
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 37da869..6e6baea 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -18,7 +18,11 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -47,6 +51,18 @@
     private int mOriginalNotificationColor;
     private boolean mExpanded;
     private boolean mShowWorkBadgeAtEnd;
+    private Drawable mBackground;
+    private int mHeaderBackgroundHeight;
+
+    ViewOutlineProvider mProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            if (mBackground != null) {
+                outline.setRect(0, 0, getWidth(), mHeaderBackgroundHeight);
+                outline.setAlpha(1f);
+            }
+        }
+    };
 
     public NotificationHeaderView(Context context) {
         this(context, null);
@@ -66,6 +82,8 @@
                 com.android.internal.R.dimen.notification_header_shrink_min_width);
         mContentEndMargin = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_end);
+        mHeaderBackgroundHeight = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_header_background_height);
     }
 
     @Override
@@ -165,6 +183,43 @@
         return new ViewGroup.MarginLayoutParams(getContext(), attrs);
     }
 
+    /**
+     * Set a {@link Drawable} to be displayed as a background on the header.
+     */
+    public void setHeaderBackgroundDrawable(Drawable drawable) {
+        if (drawable != null) {
+            setWillNotDraw(false);
+            mBackground = drawable;
+            mBackground.setCallback(this);
+            setOutlineProvider(mProvider);
+        } else {
+            setWillNotDraw(true);
+            mBackground = null;
+            setOutlineProvider(null);
+        }
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mBackground != null) {
+            mBackground.setBounds(0, 0, getWidth(), mHeaderBackgroundHeight);
+            mBackground.draw(canvas);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mBackground;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        if (mBackground != null && mBackground.isStateful()) {
+            mBackground.setState(getDrawableState());
+        }
+    }
+
     private void updateTouchListener() {
         if (mExpandClickListener != null) {
             mTouchListener.bindTouchRects();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 5db0f16..2d1e0bd 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -142,13 +142,17 @@
     public static int loadWebViewNativeLibraryFromPackage(String packageName,
                                                           ClassLoader clazzLoader) {
         int ret = waitForProviderAndSetPackageInfo();
-        if (ret != LIBLOAD_SUCCESS) {
+        if (ret != LIBLOAD_SUCCESS && ret != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
             return ret;
         }
         if (!sPackageInfo.packageName.equals(packageName))
             return LIBLOAD_WRONG_PACKAGE_NAME;
 
-        return loadNativeLibrary(clazzLoader);
+        int loadNativeRet = loadNativeLibrary(clazzLoader);
+        // If we failed waiting for relro we want to return that fact even if we successfully load
+        // the relro file.
+        if (loadNativeRet == LIBLOAD_SUCCESS) return ret;
+        return loadNativeRet;
     }
 
     static WebViewFactoryProvider getProvider() {
@@ -240,7 +244,8 @@
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
-            if (response.status != LIBLOAD_SUCCESS) {
+            if (response.status != LIBLOAD_SUCCESS
+                    && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
                 throw new MissingWebViewPackageException("Failed to load WebView provider: "
                         + getWebViewPreparationErrorReason(response.status));
             }
@@ -599,8 +604,10 @@
         try {
             response =
                 getUpdateService().waitForAndGetProvider();
-            if (response.status == WebViewFactory.LIBLOAD_SUCCESS)
+            if (response.status == LIBLOAD_SUCCESS
+                    || response.status == LIBLOAD_FAILED_WAITING_FOR_RELRO) {
                 sPackageInfo = response.packageInfo;
+            }
         } catch (RemoteException e) {
             Log.e(LOGTAG, "error waiting for relro creation", e);
             return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index fb2337e..35b53c2 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -43,6 +43,8 @@
 import dalvik.system.VMRuntime;
 import libcore.util.EmptyArray;
 
+import java.io.BufferedReader;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
@@ -51,6 +53,8 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
 
 public final class ProcessStats implements Parcelable {
     public static final String TAG = "ProcessStats";
@@ -151,7 +155,7 @@
     };
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 20;
+    private static final int PARCEL_VERSION = 21;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -188,6 +192,12 @@
     // For reading parcels.
     ArrayList<String> mIndexToCommonString;
 
+    private static final Pattern sPageTypeRegex = Pattern.compile(
+            "^Node\\s+(\\d+),.*. type\\s+(\\w+)\\s+([\\s\\d]+?)\\s*$");
+    private final ArrayList<Integer> mPageTypeZones = new ArrayList<Integer>();
+    private final ArrayList<String> mPageTypeLabels = new ArrayList<String>();
+    private final ArrayList<int[]> mPageTypeSizes = new ArrayList<int[]>();
+
     public ProcessStats(boolean running) {
         mRunning = running;
         reset();
@@ -493,6 +503,7 @@
         mReadError = null;
         mFlags = 0;
         evaluateSystemProperties(true);
+        updateFragmentation();
     }
 
     public boolean evaluateSystemProperties(boolean update) {
@@ -515,6 +526,101 @@
 
     static final int[] BAD_TABLE = new int[0];
 
+
+    /**
+     * Load the system's memory fragmentation info.
+     */
+    public void updateFragmentation() {
+        // Parse /proc/pagetypeinfo and store the values.
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader("/proc/pagetypeinfo"));
+            final Matcher matcher = sPageTypeRegex.matcher("");
+            mPageTypeZones.clear();
+            mPageTypeLabels.clear();
+            mPageTypeSizes.clear();
+            while (true) {
+                final String line = reader.readLine();
+                if (line == null) {
+                    break;
+                }
+                matcher.reset(line);
+                if (matcher.matches()) {
+                    final Integer zone = Integer.valueOf(matcher.group(1), 10);
+                    if (zone == null) {
+                        continue;
+                    }
+                    mPageTypeZones.add(zone);
+                    mPageTypeLabels.add(matcher.group(2));
+                    mPageTypeSizes.add(splitAndParseNumbers(matcher.group(3)));
+                }
+            }
+        } catch (IOException ex) {
+            mPageTypeZones.clear();
+            mPageTypeLabels.clear();
+            mPageTypeSizes.clear();
+            return;
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException allHopeIsLost) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Split the string of digits separaed by spaces.  There must be no
+     * leading or trailing spaces.  The format is ensured by the regex
+     * above.
+     */
+    private static int[] splitAndParseNumbers(String s) {
+        // These are always positive and the numbers can't be so big that we'll overflow
+        // so just do the parsing inline.
+        boolean digit = false;
+        int count = 0;
+        final int N = s.length();
+        // Count the numbers
+        for (int i=0; i<N; i++) {
+            final char c = s.charAt(i);
+            if (c >= '0' && c <= '9') {
+                if (!digit) {
+                    digit = true;
+                    count++;
+                }
+            } else {
+                digit = false;
+            }
+        }
+        // Parse the numbers
+        final int[] result = new int[count];
+        int p = 0;
+        int val = 0;
+        for (int i=0; i<N; i++) {
+            final char c = s.charAt(i);
+            if (c >= '0' && c <= '9') {
+                if (!digit) {
+                    digit = true;
+                    val = c - '0';
+                } else {
+                    val *= 10;
+                    val += c - '0';
+                }
+            } else {
+                if (digit) {
+                    digit = false;
+                    result[p++] = val;
+                }
+            }
+        }
+        if (count > 0) {
+            result[count-1] = val;
+        }
+        return result;
+    }
+
+
     private void writeCompactedLongArray(Parcel out, long[] array, int num) {
         for (int i=0; i<num; i++) {
             long val = array[i];
@@ -717,6 +823,15 @@
             }
         }
 
+        // Fragmentation info (/proc/pagetypeinfo)
+        final int NPAGETYPES = mPageTypeLabels.size();
+        out.writeInt(NPAGETYPES);
+        for (int i=0; i<NPAGETYPES; i++) {
+            out.writeInt(mPageTypeZones.get(i));
+            out.writeString(mPageTypeLabels.get(i));
+            out.writeIntArray(mPageTypeSizes.get(i));
+        }
+
         mCommonStringToIndex = null;
     }
 
@@ -981,6 +1096,20 @@
             }
         }
 
+        // Fragmentation info
+        final int NPAGETYPES = in.readInt();
+        mPageTypeZones.clear();
+        mPageTypeZones.ensureCapacity(NPAGETYPES);
+        mPageTypeLabels.clear();
+        mPageTypeLabels.ensureCapacity(NPAGETYPES);
+        mPageTypeSizes.clear();
+        mPageTypeSizes.ensureCapacity(NPAGETYPES);
+        for (int i=0; i<NPAGETYPES; i++) {
+            mPageTypeZones.add(in.readInt());
+            mPageTypeLabels.add(in.readString());
+            mPageTypeSizes.add(in.createIntArray());
+        }
+
         mIndexToCommonString = null;
 
         if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!");
@@ -1271,6 +1400,8 @@
             */
             pw.print("  mRunning="); pw.println(mRunning);
         }
+
+        dumpFragmentationLocked(pw);
     }
 
     public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) {
@@ -1282,6 +1413,21 @@
         dumpTotalsLocked(pw, now);
     }
 
+    private void dumpFragmentationLocked(PrintWriter pw) {
+        pw.println();
+        pw.println("Available pages by page size:");
+        final int NPAGETYPES = mPageTypeLabels.size();
+        for (int i=0; i<NPAGETYPES; i++) {
+            pw.format("Zone %3d  %14s ", mPageTypeZones.get(i), mPageTypeLabels.get(i));
+            final int[] sizes = mPageTypeSizes.get(i);
+            final int N = sizes == null ? 0 : sizes.length;
+            for (int j=0; j<N; j++) {
+                pw.format("%6d", sizes[j]);
+            }
+            pw.println();
+        }
+    }
+
     long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
             long totalTime, long curTotalMem, int samples) {
         if (memWeight != 0) {
@@ -1540,6 +1686,24 @@
             pw.print(totalMem.processStateSamples[i]);
         }
         pw.println();
+
+        final int NPAGETYPES = mPageTypeLabels.size();
+        for (int i=0; i<NPAGETYPES; i++) {
+            pw.print("availablepages,");
+            pw.print(mPageTypeLabels.get(i));
+            pw.print(",");
+            pw.print(mPageTypeZones.get(i));
+            pw.print(",");
+            final int[] sizes = mPageTypeSizes.get(i);
+            final int N = sizes == null ? 0 : sizes.length;
+            for (int j=0; j<N; j++) {
+                if (j != 0) {
+                    pw.print(",");
+                }
+                pw.print(sizes[j]);
+            }
+            pw.println();
+        }
     }
 
 
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 6d6c162..fbc51cd 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -127,7 +127,7 @@
             .append("\n").toString();
         final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
 
-        String recovery = RecoverySystem.handleAftermath();
+        String recovery = RecoverySystem.handleAftermath(ctx);
         if (recovery != null && db != null) {
             db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
         }
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 1bc4285..1fc08ed 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -479,17 +479,13 @@
 
 static inline
 jint interpretReadSizeError(ssize_t readSize) {
-    ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
-    switch (readSize) {
-    case WOULD_BLOCK:
+    if (readSize == WOULD_BLOCK) {
         return (jint)0;
-    case BAD_VALUE:
-        return (jint)AUDIO_JAVA_BAD_VALUE;
-    default:
-        // may be possible for other errors such as
-        // NO_INIT to happen if restoreRecord_l fails.
-    case INVALID_OPERATION:
-        return (jint)AUDIO_JAVA_INVALID_OPERATION;
+    } else if (readSize == NO_INIT) {
+        return AUDIO_JAVA_DEAD_OBJECT;
+    } else {
+        ALOGE("Error %zd during AudioRecord native read", readSize);
+        return nativeToJavaStatus(readSize);
     }
 }
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 024c21d..982a1f8 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -606,6 +606,18 @@
     env->ReleaseFloatArrayElements(array, elems, mode);
 }
 
+static inline
+jint interpretWriteSizeError(ssize_t writeSize) {
+    if (writeSize == WOULD_BLOCK) {
+        return (jint)0;
+    } else if (writeSize == NO_INIT) {
+        return AUDIO_JAVA_DEAD_OBJECT;
+    } else {
+        ALOGE("Error %zd during AudioTrack native read", writeSize);
+        return nativeToJavaStatus(writeSize);
+    }
+}
+
 // ----------------------------------------------------------------------------
 template <typename T>
 static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
@@ -628,11 +640,10 @@
         memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
         written = sizeInBytes;
     }
-    if (written > 0) {
+    if (written >= 0) {
         return written / sizeof(T);
     }
-    // for compatibility, error codes pass through unchanged
-    return written;
+    return interpretWriteSizeError(written);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 3c47705..39e1bbb 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -19,7 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="#F3374248"
-    android:clickable="true" >
+    android:importantForAccessibility="noHideDescendants"
+    android:clickable="true">
 
     <ImageView android:id="@+id/work_widget_app_icon"
         android:layout_width="wrap_content"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3ba6ce1..37fb816 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -24,6 +24,8 @@
     <dimen name="thumbnail_height">192dp</dimen>
     <!-- The amount to scale a fullscreen screenshot thumbnail. -->
     <item name="thumbnail_fullscreen_scale" type="fraction">60%</item>
+    <!-- The width used to calculate scale for full screen thumbnail on TV -->
+    <integer name="thumbnail_width_tv">240</integer>
     <!-- The standard size (both width and height) of an application icon that
          will be displayed in the app launcher and elsewhere. -->
     <dimen name="app_icon_size">48dip</dimen>
@@ -161,6 +163,9 @@
     <!-- height of the content margin on the bottom -->
     <dimen name="notification_content_margin_bottom">16dp</dimen>
 
+	<!-- The height of the background for a notification header on a group -->
+    <dimen name="notification_header_background_height">45.5dp</dimen>
+
     <!-- Height of a small notification in the status bar -->
     <dimen name="notification_min_height">92dp</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dffab2c..45ee778 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4317,4 +4317,7 @@
     <!-- Description of notification shown when device has been forced to safe mode after a security compromise. -->
     <string name="audit_safemode_notification_details">Touch to learn more.</string>
 
+    <!-- Accessibilty string added to a widget that has been suspended [CHAR LIMIT=20] -->
+    <string name="suspended_widget_accessibility">Suspended <xliff:g id="label" example="Calendar">%1$s</xliff:g></string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3f1de6d..f2c3c42 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1496,6 +1496,7 @@
   <java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
   <java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
   <java-symbol type="fraction" name="thumbnail_fullscreen_scale" />
+  <java-symbol type="integer" name="thumbnail_width_tv" />
   <java-symbol type="dimen" name="resize_shadow_size" />
   <java-symbol type="color" name="resize_shadow_start_color" />
   <java-symbol type="color" name="resize_shadow_end_color" />
@@ -2435,6 +2436,7 @@
   <java-symbol type="dimen" name="notification_content_picture_margin" />
   <java-symbol type="dimen" name="notification_content_margin_top" />
   <java-symbol type="dimen" name="notification_content_margin_bottom" />
+  <java-symbol type="dimen" name="notification_header_background_height" />
   <java-symbol type="string" name="importance_from_user" />
   <java-symbol type="string" name="importance_from_person" />
 
@@ -2594,4 +2596,5 @@
   <!-- Pinner Service -->
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
 
+  <java-symbol type="string" name="suspended_widget_accessibility" />
 </resources>
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
new file mode 100644
index 0000000..9cb13dd
--- /dev/null
+++ b/core/tests/coretests/apks/locales/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := locales
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/locales/AndroidManifest.xml b/core/tests/coretests/apks/locales/AndroidManifest.xml
new file mode 100644
index 0000000..8a1f575
--- /dev/null
+++ b/core/tests/coretests/apks/locales/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.locales">
+
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/core/tests/coretests/apks/locales/res/values-pl/values.xml b/core/tests/coretests/apks/locales/res/values-pl/values.xml
new file mode 100644
index 0000000..22237fa8
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values-pl/values.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<resources>
+    <string name="text">Text (pl)</string>
+</resources>
diff --git a/core/tests/coretests/apks/locales/res/values/values.xml b/core/tests/coretests/apks/locales/res/values/values.xml
new file mode 100644
index 0000000..cb990d2
--- /dev/null
+++ b/core/tests/coretests/apks/locales/res/values/values.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<resources>
+    <string name="text">Text</string>
+</resources>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
new file mode 100644
index 0000000..e85666e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 android.content.res;
+
+import android.os.FileUtils;
+import android.os.LocaleList;
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.view.Display;
+
+import com.android.frameworks.coretests.R;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleTest extends AndroidTestCase {
+
+    private String extractApkAndGetPath(int id) throws Exception {
+        final Resources resources = getContext().getResources();
+        try (InputStream is = resources.openRawResource(id)) {
+            File path = new File(getContext().getFilesDir(), resources.getResourceEntryName(id));
+            FileUtils.copyToFileOrThrow(is, path);
+            return path.getAbsolutePath();
+        }
+    }
+
+    private Resources createResourcesWithApk(int rawApkId) throws Exception {
+        final AssetManager assets = new AssetManager();
+        assertTrue(assets.addAssetPath(extractApkAndGetPath(rawApkId)) != 0);
+
+        final DisplayMetrics dm = new DisplayMetrics();
+        dm.setToDefaults();
+        return new Resources(assets, dm, new Configuration());
+    }
+
+    private static void ensureNoLanguage(Resources resources, String language) {
+        final String[] supportedLocales = resources.getAssets().getNonSystemLocales();
+        for (String languageTag : supportedLocales) {
+            if ("en-XA".equals(languageTag)) {
+                continue;
+            }
+            assertFalse(
+                    "supported locales: " + Arrays.toString(supportedLocales),
+                    language.equals(Locale.forLanguageTag(languageTag).getLanguage()));
+        }
+    }
+
+    @SmallTest
+    public void testEnglishIsAlwaysConsideredSupported() throws Exception {
+        final Resources resources = createResourcesWithApk(R.raw.locales);
+        ensureNoLanguage(resources, "en");
+
+        final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US,pl-PL");
+        final Configuration config = new Configuration();
+        config.setLocales(preferredLocales);
+
+        resources.updateConfiguration(config, null);
+
+        // The APK we loaded has default and Polish languages. If English is first in the list,
+        // always take it the default (assumed to be English).
+        assertEquals(Locale.forLanguageTag("en-US"),
+                resources.getConfiguration().getLocales().get(0));
+    }
+
+    @SmallTest
+    public void testSelectFirstSupportedLanguage() throws Exception {
+        final Resources resources = createResourcesWithApk(R.raw.locales);
+        ensureNoLanguage(resources, "fr");
+
+        final LocaleList preferredLocales = LocaleList.forLanguageTags("fr-FR,pl-PL");
+        final Configuration config = new Configuration();
+        config.setLocales(preferredLocales);
+
+        resources.updateConfiguration(config, null);
+
+        // The APK we loaded has default and Polish languages. We expect the Polish language to
+        // therefore be chosen.
+        assertEquals(Locale.forLanguageTag("pl-PL"),
+                resources.getConfiguration().getLocales().get(0));
+    }
+}
diff --git a/docs/html-intl/intl/es/preview/setup-sdk.jd b/docs/html-intl/intl/es/preview/setup-sdk.jd
index 01c715b..769121d 100644
--- a/docs/html-intl/intl/es/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/es/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
 y <a href="{@docRoot}preview/api-overview.html">API y funciones de Android N
 </a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/in/preview/setup-sdk.jd b/docs/html-intl/intl/in/preview/setup-sdk.jd
index a842931..c6e43f1 100644
--- a/docs/html-intl/intl/in/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/in/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
 dan <a href="{@docRoot}preview/api-overview.html">Android N API
 dan Fiturnya</a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ja/preview/setup-sdk.jd b/docs/html-intl/intl/ja/preview/setup-sdk.jd
index fd5faba..4331641 100644
--- a/docs/html-intl/intl/ja/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ja/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
 
 </li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ko/preview/setup-sdk.jd b/docs/html-intl/intl/ko/preview/setup-sdk.jd
index adb9edf..91f17c1 100644
--- a/docs/html-intl/intl/ko/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ko/preview/setup-sdk.jd
@@ -136,7 +136,7 @@
 
 <h2 id="java8">Java 8 JDK 및 JRE 가져오기</h2>
 
-<p>Android N 플랫폼에 대해 앱을 컴파일하려면 
+<p>Android N 플랫폼에 대해 앱을 컴파일하려면
 Java 8 개발자 키트(JDK 8)를 사용해야 하며, Android
 Studio 2.1의 일부 도구를 사용하려면 Java 8 런타임 환경(JRE 8)을 설치해야
 합니다. 따라서 각각 최신 버전이 없는 경우, 지금 JDK 8 및 JRE 8를
@@ -221,4 +221,4 @@
 및 기능</a>에서 Android N 플랫폼에
 대해 자세히 알아봅니다.</li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
index 4159c25..9035e50 100644
--- a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd
@@ -79,7 +79,7 @@
 
 <h2 id="get-sdk">Obter o N Preview SDK</h2>
 
-<p>Para começar a desenvolver com as APIs do Android N, instale o 
+<p>Para começar a desenvolver com as APIs do Android N, instale o
 Android N Preview SDK no Android Studio da seguinte maneira:</p>
 
 <ol>
@@ -112,7 +112,7 @@
 <p>
   Informações detalhadas sobre as APIs do Android N são disponibilizadas na documentação de referência do N Preview
 , que pode ser baixada pela tabela a seguir.
- Este pacote contém uma versão off-line resumida do site de desenvolvedores do Android 
+ Este pacote contém uma versão off-line resumida do site de desenvolvedores do Android
  e inclui uma referência atualizada para as APIs do Android N, além de um relatório
  das diferenças entre as APIs.
 </p>
@@ -168,7 +168,7 @@
   Para usar as APIs do Android N, seu projeto deve ser configurado da maneira apropriada.
 </p>
 
-<p>Se planeja usar os recursos de linguagem do Java 8, consulte 
+<p>Se planeja usar os recursos de linguagem do Java 8, consulte
 <a href="{@docRoot}preview/j8-jack.html">Recursos de linguagem do Java 8</a>
 para saber mais sobre os recursos do Java 8 com suporte e
 como configurar seu projeto com o compilador Jack.</p>
@@ -216,9 +216,9 @@
 
 <ul>
   <li>Siga o guia para <a href="{@docRoot}preview/download.html">Testar em um dispositivo Android N</a>.</li>
-  <li>Saiba mais sobre a plataforma Android N com 
+  <li>Saiba mais sobre a plataforma Android N com
 <a href="{@docRoot}preview/behavior-changes.html">Mudanças de comportamento</a>
 e <a href="{@docRoot}preview/api-overview.html">Recursos de APIs do
 Android N</a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/ru/preview/setup-sdk.jd b/docs/html-intl/intl/ru/preview/setup-sdk.jd
index 3838700..582fe9f 100644
--- a/docs/html-intl/intl/ru/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/ru/preview/setup-sdk.jd
@@ -79,7 +79,7 @@
 
 <h2 id="get-sdk">Установка N Preview SDK</h2>
 
-<p>Чтобы начать разработку с использованием API-интерфейсов Android N, необходимо установить 
+<p>Чтобы начать разработку с использованием API-интерфейсов Android N, необходимо установить
 Android N Preview SDK в Android Studio, выполнив следующие шаги.</p>
 
 <ol>
@@ -136,7 +136,7 @@
 
 <h2 id="java8">Установка Java 8 JDK и JRE</h2>
 
-<p>Чтобы скомпилировать ваше приложение для платформы Android N, необходимо использовать 
+<p>Чтобы скомпилировать ваше приложение для платформы Android N, необходимо использовать
 Java 8 Developer Kit (JDK 8), а для использования некоторых инструментов с Android
 Studio 2.1 необходимо установить Java 8 Runtime Environment (JRE 8). Поэтому если у вас еще
 нет последних версий каждого из этих пакетов, загрузите JDK 8 и JRE 8
@@ -221,4 +221,4 @@
 и в описании <a href="{@docRoot}preview/api-overview.html">API-интерфейсов и возможностей Android N</a>.
 </li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/vi/preview/setup-sdk.jd b/docs/html-intl/intl/vi/preview/setup-sdk.jd
index 2e3a18b..6f6cd20 100644
--- a/docs/html-intl/intl/vi/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/vi/preview/setup-sdk.jd
@@ -98,7 +98,7 @@
 
   <li>Nhấp vào tab <strong>SDK Tools</strong>, rồi chọn
     <strong>Android SDK Build Tools</strong>, <strong>Android SDK
-    Platform-Tools</strong>, và các hộp kiểm <strong>Android SDK Tools</strong> 
+    Platform-Tools</strong>, và các hộp kiểm <strong>Android SDK Tools</strong>
 .
   </li>
 
@@ -221,4 +221,4 @@
 và<a href="{@docRoot}preview/api-overview.html">API Android N
 và Các tính năng</a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
index 216d464..eaef7a1 100644
--- a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
 
 </li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
index d2a9d9e..7a46aca 100644
--- a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
+++ b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd
@@ -221,4 +221,4 @@
 
 </li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/docs/html/images/testing/test_framework.png b/docs/html/images/testing/test_framework.png
deleted file mode 100644
index fbc5fc2..0000000
--- a/docs/html/images/testing/test_framework.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index c85ef7e..3aab5bd 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -237,7 +237,7 @@
     "title":"Test Your App",
     "summary":"This document describes key concepts related to Android app testing. It assumes you have a basic knowledge of the JUnit testing framework. Android testing is based on JUnit. In general, a JUnit test is a method whose statements test a part of the application\u2026",
     "url":"studio/test/index.html",
-    "image":"images/testing/test_framework.png",
+    "image":"studio/images/test/test-framework.png",
     "type":"studio",
     "keywords": [],
     "tags": [],
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 28fc406..ebeae74 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -189,4 +189,4 @@
 and <a href="{@docRoot}preview/api-overview.html">Android N APIs
 and Features</a>.</li>
 </ul>
-
+<div class="end-content-title"></div>
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 5561b22..b601cde 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -34,6 +34,7 @@
     private static final int NUM_SATELLITES = 255;
     private static final int GLONASS_SVID_OFFSET = 64;
     private static final int BEIDOU_SVID_OFFSET = 200;
+    private static final int SBAS_SVID_OFFSET = -87;
 
     /* These package private values are modified by the LocationManager class */
     private int mTimeToFirstFix;
@@ -166,9 +167,10 @@
                 prn += GLONASS_SVID_OFFSET;
             } else if (constellationType == GnssStatus.CONSTELLATION_BEIDOU) {
                 prn += BEIDOU_SVID_OFFSET;
+            } else if (constellationType == GnssStatus.CONSTELLATION_SBAS) {
+                prn += SBAS_SVID_OFFSET;
             } else if ((constellationType != GnssStatus.CONSTELLATION_GPS) &&
-                    (constellationType != GnssStatus.CONSTELLATION_QZSS) &&
-                    (constellationType != GnssStatus.CONSTELLATION_SBAS)) {
+                    (constellationType != GnssStatus.CONSTELLATION_QZSS)) {
                 continue;
             }
             if (prn > 0 && prn <= NUM_SATELLITES) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 23ae691..4bdc70e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2748,10 +2748,10 @@
     public static abstract class AudioRecordingCallback {
         /**
          * Called whenever the device recording configuration has changed.
-         * @param configs array containing the results of
+         * @param configs list containing the results of
          *      {@link AudioManager#getActiveRecordingConfigurations()}.
          */
-        public void onRecordingConfigChanged(AudioRecordingConfiguration[] configs) {}
+        public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {}
     }
 
     private static class AudioRecordingCallbackInfo {
@@ -2765,10 +2765,10 @@
 
     private final static class RecordConfigChangeCallbackData {
         final AudioRecordingCallback mCb;
-        final AudioRecordingConfiguration[] mConfigs;
+        final List<AudioRecordingConfiguration> mConfigs;
 
         RecordConfigChangeCallbackData(AudioRecordingCallback cb,
-                AudioRecordingConfiguration[] configs) {
+                List<AudioRecordingConfiguration> configs) {
             mCb = cb;
             mConfigs = configs;
         }
@@ -2848,10 +2848,10 @@
 
     /**
      * Returns the current active audio recording configurations of the device.
-     * @return a non-null array of recording configurations. An array of length 0 indicates there is
+     * @return a non-null list of recording configurations. An empty list indicates there is
      *     no recording active when queried.
      */
-    public @NonNull AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+    public @NonNull List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
         final IAudioService service = getService();
         try {
             return service.getActiveRecordingConfigurations();
@@ -2909,7 +2909,7 @@
 
     private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
 
-        public void dispatchRecordingConfigChange(AudioRecordingConfiguration[] configs) {
+        public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
             synchronized(mRecordCallbackLock) {
                 if (mRecordCallbackList != null) {
                     for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4504e69..8efd599 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -94,6 +94,11 @@
      * Denotes a failure due to the improper use of a method.
      */
     public  static final int ERROR_INVALID_OPERATION               = AudioSystem.INVALID_OPERATION;
+    /**
+     * An error code indicating that the object reporting it is no longer valid and needs to
+     * be recreated.
+     */
+    public  static final int ERROR_DEAD_OBJECT                     = AudioSystem.DEAD_OBJECT;
 
     // Error codes:
     // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp
@@ -1046,10 +1051,16 @@
      * @param audioData the array to which the recorded audio data is written.
      * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
      * @param sizeInBytes the number of requested bytes.
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will not exceed sizeInBytes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
         return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);
@@ -1070,11 +1081,17 @@
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will be a multiple of the frame size in bytes
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will be a multiple of the frame size in bytes
      *    not to exceed sizeInBytes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
             @ReadMode int readMode) {
@@ -1106,10 +1123,17 @@
      *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of requested shorts.
      *        Must not be negative, or cause the data access to go out of bounds of the array.
-     * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
+     * @return zero or the positive number of shorts that were read, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to exceed
+     *    sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
         return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
@@ -1129,10 +1153,17 @@
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
+     * @return zero or the positive number of shorts that were read, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to exceed
+     *    sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
             @ReadMode int readMode) {
@@ -1169,10 +1200,17 @@
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
+     * @return zero or the positive number of floats that were read, or one of the following
+     *    error codes. The number of floats will be a multiple of the channel count not to exceed
+     *    sizeInFloats.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @ReadMode int readMode) {
@@ -1213,11 +1251,17 @@
      * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
      *    that the number of bytes requested be a multiple of the frame size (sample size in
      *    bytes multiplied by the channel count).
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
-     *    The number of bytes read will be truncated to be a multiple of the frame size.
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will not exceed sizeInBytes and will be truncated to be
+     *    a multiple of the frame size.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
         return read(audioBuffer, sizeInBytes, READ_BLOCKING);
@@ -1240,11 +1284,17 @@
      *     is read.
      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
-     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
-     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
-     *    The number of bytes read will be truncated to be a multiple of the frame size.
+     * @return zero or the positive number of bytes that were read, or one of the following
+     *    error codes. The number of bytes will not exceed sizeInBytes and will be truncated to be
+     *    a multiple of the frame size.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next read()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
         if (mState != STATE_INITIALIZED) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 12e88a2..9a81668 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -156,7 +156,6 @@
     /**
      * An error code indicating that the object reporting it is no longer valid and needs to
      * be recreated.
-     * @hide
      */
     public  static final int ERROR_DEAD_OBJECT                     = AudioSystem.DEAD_OBJECT;
     /**
@@ -1840,17 +1839,17 @@
      *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInBytes the number of bytes to write in audioData after the offset.
      *    Must not be negative, or cause the data access to go out of bounds of the array.
-     * @return zero or the positive number of bytes that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of bytes will be a multiple of the frame size in bytes
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes. The number of bytes will be a multiple of the frame size in bytes
      *    not to exceed sizeInBytes.
-     *
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      * This is equivalent to {@link #write(byte[], int, int, int)} with <code>writeMode</code>
      * set to  {@link #WRITE_BLOCKING}.
      */
@@ -1888,16 +1887,17 @@
      *         to the audio sink.
      *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of bytes that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of bytes will be a multiple of the frame size in bytes
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes. The number of bytes will be a multiple of the frame size in bytes
      *    not to exceed sizeInBytes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
             @WriteMode int writeMode) {
@@ -1950,16 +1950,17 @@
      *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of shorts to read in audioData after the offset.
      *    Must not be negative, or cause the data access to go out of bounds of the array.
-     * @return zero or the positive number of shorts that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
-     *
+     * @return zero or the positive number of shorts that were written, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to
+     *    exceed sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      * This is equivalent to {@link #write(short[], int, int, int)} with <code>writeMode</code>
      * set to  {@link #WRITE_BLOCKING}.
      */
@@ -1995,15 +1996,17 @@
      *         to the audio sink.
      *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of shorts that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
+     * @return zero or the positive number of shorts that were written, or one of the following
+     *    error codes. The number of shorts will be a multiple of the channel count not to
+     *    exceed sizeInShorts.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
             @WriteMode int writeMode) {
@@ -2074,15 +2077,17 @@
      *         to the audio sink.
      *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of floats that were written, or
-     *    {@link #ERROR_INVALID_OPERATION}
-     *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes, or
-     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *    needs to be recreated.
-     *    The dead object error code is not returned if some data was successfully transferred.
-     *    In this case, the error is returned at the next write().
-     *    The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
+     * @return zero or the positive number of floats that were written, or one of the following
+     *    error codes. The number of floats will be a multiple of the channel count not to
+     *    exceed sizeInFloats.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @WriteMode int writeMode) {
@@ -2154,12 +2159,16 @@
      *         to the audio sink.
      *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @return zero or the positive number of bytes that were written, or
-     *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
-     *     {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *     needs to be recreated.
-     *     The dead object error code is not returned if some data was successfully transferred.
-     *     In this case, the error is returned at the next write().
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode) {
@@ -2223,12 +2232,16 @@
      *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
      * @param timestamp The timestamp of the first decodable audio frame in the provided audioData.
-     * @return zero or a positive number of bytes that were written, or
-     *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
-     *     {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
-     *     needs to be recreated.
-     *     The dead object error code is not returned if some data was successfully transferred.
-     *     In this case, the error is returned at the next write().
+     * @return zero or the positive number of bytes that were written, or one of the following
+     *    error codes.
+     * <ul>
+     * <li>{@link #ERROR_INVALID_OPERATION} if the track isn't properly initialized</li>
+     * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
+     * <li>{@link #ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated. The dead object error code is not returned if some data was
+     *    successfully transferred. In this case, the error is returned at the next write()</li>
+     * <li>{@link #ERROR} in case of other error</li>
+     * </ul>
      */
     public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode, long timestamp) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 97f670b..c7931fc 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -164,5 +164,5 @@
 
     oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
 
-    AudioRecordingConfiguration[] getActiveRecordingConfigurations();
+    List<AudioRecordingConfiguration> getActiveRecordingConfigurations();
 }
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
index e803283..f4e98b2 100644
--- a/media/java/android/media/IRecordingConfigDispatcher.aidl
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -25,6 +25,6 @@
  */
 oneway interface IRecordingConfigDispatcher {
 
-    void dispatchRecordingConfigChange(in AudioRecordingConfiguration[] configs);
+    void dispatchRecordingConfigChange(in List<AudioRecordingConfiguration> configs);
 
 }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index b9ed269..87c6d88 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1918,13 +1918,15 @@
                         case CodecProfileLevel.MPEG2ProfileMain:
                             switch (profileLevel.level) {
                                 case CodecProfileLevel.MPEG2LevelLL:
-                                    FR = 30; W = 22; H =  18; MBPS =  11880; FS =  396; BR =  4000; break;
+                                    FR = 30; W = 22; H =  18; MBPS =  11880; FS =   396; BR =  4000; break;
                                 case CodecProfileLevel.MPEG2LevelML:
-                                    FR = 30; W = 45; H =  36; MBPS =  48600; FS =  1620; BR =  15000; break;
+                                    FR = 30; W = 45; H =  36; MBPS =  48600; FS =  1620; BR = 15000; break;
                                 case CodecProfileLevel.MPEG2LevelH14:
-                                    FR = 60; W = 90; H =  68; MBPS =  367200; FS =  6120; BR = 60000; break;
+                                    FR = 60; W = 90; H =  68; MBPS = 183600; FS =  6120; BR = 60000; break;
                                 case CodecProfileLevel.MPEG2LevelHL:
-                                    FR = 60; W = 120; H = 68; MBPS =  489600; FS =  8160; BR = 80000; break;
+                                    FR = 60; W = 120; H = 68; MBPS = 244800; FS =  8160; BR = 80000; break;
+                                case CodecProfileLevel.MPEG2LevelHP:
+                                    FR = 60; W = 120; H = 68; MBPS = 489600; FS =  8160; BR = 80000; break;
                                 default:
                                     Log.w(TAG, "Unrecognized profile/level "
                                             + profileLevel.profile + "/"
@@ -1982,15 +1984,12 @@
                                     FR = 30; W = 22; H = 18; MBPS =  5940; FS = 396; BR = 128; break;
                                 case CodecProfileLevel.MPEG4Level3:
                                     FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384; break;
-                                case CodecProfileLevel.MPEG4Level4:
                                 case CodecProfileLevel.MPEG4Level4a:
+                                    FR = 30; W = 40; H = 30; MBPS = 36000; FS = 1200; BR = 4000; break;
                                 case CodecProfileLevel.MPEG4Level5:
-                                    // While MPEG4 SP does not have level 4 or 5, some vendors
-                                    // report it. Use the same limits as level 3, but mark as
-                                    // unsupported.
-                                    FR = 30; W = 22; H = 18; MBPS = 11880; FS = 396; BR = 384;
-                                    supported = false;
-                                    break;
+                                    FR = 30; W = 45; H = 36; MBPS = 40500; FS = 1620; BR = 8000; break;
+                                case CodecProfileLevel.MPEG4Level6:
+                                    FR = 30; W = 80; H = 45; MBPS = 108000; FS = 3600; BR = 12000; break;
                                 default:
                                     Log.w(TAG, "Unrecognized profile/level "
                                             + profileLevel.profile + "/"
@@ -2007,12 +2006,9 @@
                                     FR = 30; W = 22; H = 18; MBPS =  5940; FS =  396; BR =  384; break;
                                 case CodecProfileLevel.MPEG4Level3:
                                     FR = 30; W = 22; H = 18; MBPS = 11880; FS =  396; BR =  768; break;
-                                // case CodecProfileLevel.MPEG4Level3b:
-                                // TODO: MPEG4 level 3b is not defined in OMX
-                                //  MBPS = 11880; FS =  396; BR = 1500; break;
+                                case CodecProfileLevel.MPEG4Level3b:
+                                    FR = 30; W = 22; H = 18; MBPS = 11880; FS =  396; BR = 1500; break;
                                 case CodecProfileLevel.MPEG4Level4:
-                                case CodecProfileLevel.MPEG4Level4a:
-                                    // TODO: MPEG4 level 4a is not defined in spec
                                     FR = 30; W = 44; H = 36; MBPS = 23760; FS =  792; BR = 3000; break;
                                 case CodecProfileLevel.MPEG4Level5:
                                     FR = 30; W = 45; H = 36; MBPS = 48600; FS = 1620; BR = 8000; break;
@@ -2640,9 +2636,11 @@
         public static final int MPEG4Level1      = 0x04;
         public static final int MPEG4Level2      = 0x08;
         public static final int MPEG4Level3      = 0x10;
+        public static final int MPEG4Level3b     = 0x18;
         public static final int MPEG4Level4      = 0x20;
         public static final int MPEG4Level4a     = 0x40;
         public static final int MPEG4Level5      = 0x80;
+        public static final int MPEG4Level6      = 0x100;
 
         // from OMX_VIDEO_MPEG2PROFILETYPE
         public static final int MPEG2ProfileSimple              = 0x00;
@@ -2657,6 +2655,7 @@
         public static final int MPEG2LevelML     = 0x01;
         public static final int MPEG2LevelH14    = 0x02;
         public static final int MPEG2LevelHL     = 0x03;
+        public static final int MPEG2LevelHP     = 0x04;
 
         // from OMX_AUDIO_AACPROFILETYPE
         public static final int AACObjectMain       = 1;
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_header.xml b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
index ff342cb..9eee0c4 100644
--- a/packages/PrintSpooler/res/layout/add_printer_list_header.xml
+++ b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
@@ -16,7 +16,8 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="fill_parent"
-        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
         android:paddingStart="?android:attr/listPreferredItemPaddingStart"
         android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
         android:orientation="horizontal"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7a8c6f2..89c46d7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -809,7 +809,8 @@
     <!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
     <string name="home">Settings Home</string>
 
-    <string-array name="battery_labels" translatable="false">
+    <!-- Labels on the side of batter graph in Quick Settings and Settings [CHAR LIMIT=8] -->
+    <string-array name="battery_labels">
         <item>0%</item>
         <item>50%</item>
         <item>100%</item>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 474e3e6..6bc4df7 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -815,6 +815,14 @@
      */
     private void onBugreportFinished(int id, Intent intent) {
         final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
+        // Since BugreportProvider and BugreportProgressService aren't tightly coupled,
+        // we need to make sure they are explicitly tied to a single unique notification URI
+        // so that the service can alert the provider of changes it has done (ie. new bug
+        // reports)
+        // See { @link Cursor#setNotificationUri } and {@link ContentResolver#notifyChanges }
+        final Uri notificationUri = BugreportStorageProvider.getNotificationUri();
+        mContext.getContentResolver().notifyChange(notificationUri, null, false);
+
         if (bugreportFile == null) {
             // Should never happen, dumpstate always set the file.
             Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
index 0f92fa6..8b07599 100644
--- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -19,9 +19,11 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
+import android.net.Uri;
 import android.os.CancellationSignal;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsProvider;
@@ -32,6 +34,7 @@
 import java.io.FileNotFoundException;
 
 public class BugreportStorageProvider extends DocumentsProvider {
+    private static final String AUTHORITY = "com.android.shell.documents";
     private static final String DOC_ID_ROOT = "bugreport";
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
@@ -103,6 +106,7 @@
                 for (File file : files) {
                     addFileRow(result, file);
                 }
+                result.setNotificationUri(getContext().getContentResolver(), getNotificationUri());
             }
         }
         return result;
@@ -130,6 +134,12 @@
         }
     }
 
+    // This is used by BugreportProgressService so that the notification uri shared by
+    // BugreportProgressService and BugreportStorageProvider are guaranteed the same and unique
+    protected static Uri getNotificationUri() {
+      return DocumentsContract.buildChildDocumentsUri(AUTHORITY, DOC_ID_ROOT);
+    }
+
     private static String[] resolveRootProjection(String[] projection) {
         return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
     }
diff --git a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
index 201f47d..36df924 100644
--- a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
+++ b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
@@ -19,6 +19,7 @@
         android:layout_height="wrap_content"
         android:focusable="true"
         android:focusableInTouchMode="true"
+        android:contentDescription="@string/status_bar_recent_inspect_item_title"
         android:layout_gravity="center"
         android:layout_centerInParent="true"
         android:clipToPadding="false"
@@ -49,6 +50,7 @@
             android:layout_gravity="center_horizontal"
             android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
             android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
+            android:contentDescription="@string/status_bar_accessibility_dismiss_recents"
             android:alpha="@integer/dismiss_unselected_alpha"
             android:src="@drawable/recents_tv_dismiss_icon" />
     <TextView
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index dad7cea..76315b8 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -20,4 +20,4 @@
     android:id="@+id/notification_more_divider"
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_divider_height"
-    android:background="#61000000" />
+    android:background="#FF616161" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 658571e..2c8a559 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -53,8 +53,7 @@
             android:layout_width="@dimen/notification_panel_width"
             android:layout_height="match_parent"
             android:layout_gravity="@integer/notification_panel_layout_gravity"
-            android:layout_marginBottom="@dimen/close_handle_underlap"
-            android:importantForAccessibility="no" />
+            android:layout_marginBottom="@dimen/close_handle_underlap" />
 
         <ViewStub
             android:id="@+id/keyguard_user_switcher"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b8f576b..cbb3c0a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -248,6 +248,9 @@
     <!-- The height of the divider between the individual notifications. -->
     <dimen name="notification_divider_height">0.5dp</dimen>
 
+    <!-- The height of a notification header -->
+    <dimen name="notification_header_height">53dp</dimen>
+
     <!-- The height of the divider between the individual notifications when the notification wants it to be increased. This is currently the case for notification groups -->
     <dimen name="notification_divider_height_increased">6dp</dimen>
 
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
index dc0483f..9259415 100644
--- a/packages/SystemUI/res/values/values_tv.xml
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -16,5 +16,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <item format="float" type="integer" name="unselected_scale">1.0</item>
     <item format="float" type="integer" name="selected_scale">1.259</item>
-    <item format="float" type="integer" name="dismiss_unselected_alpha">0.1</item>
+    <item format="float" type="integer" name="dismiss_unselected_alpha">0.3</item>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index b6d8095..7a5e322 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -45,6 +45,7 @@
         void setUserLockedChild(View v, boolean userLocked);
         void expansionStateChanged(boolean isExpanding);
         int getMaxExpandHeight(ExpandableView view);
+        void setExpansionCancelled(View view);
     }
 
     private static final String TAG = "ExpandHelper";
@@ -558,6 +559,8 @@
                 public void onAnimationEnd(Animator animation) {
                     if (!mCancelled) {
                         mCallback.setUserExpandedChild(scaledView, expand);
+                    } else {
+                        mCallback.setExpansionCancelled(scaledView);
                     }
                     mCallback.setUserLockedChild(scaledView, false);
                     mScaleAnimation.removeListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 4341ab9c..ecb64f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -54,9 +54,9 @@
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.tv.animations.HomeRecentsEnterExitAnimationHolder;
 import com.android.systemui.recents.tv.views.RecentsTvView;
+import com.android.systemui.recents.tv.views.TaskCardView;
 import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
 import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
-import com.android.systemui.recents.views.AnimationProps;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.tv.pip.PipManager;
 import com.android.systemui.tv.pip.PipRecentsOverlayManager;
@@ -184,12 +184,14 @@
         if (!plan.hasTasks()) {
             loader.preloadTasks(plan, -1, !launchState.launchedFromHome);
         }
+
+        int numVisibleTasks = TaskCardView.getNumberOfVisibleTasks(getApplicationContext());
         mLaunchedFromHome = launchState.launchedFromHome;
         TaskStack stack = plan.getTaskStack();
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
         loadOpts.runningTaskId = launchState.launchedToTaskId;
-        loadOpts.numVisibleTasks = stack.getStackTaskCount();
-        loadOpts.numVisibleTaskThumbnails = stack.getStackTaskCount();
+        loadOpts.numVisibleTasks = numVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = numVisibleTasks;
         loader.loadTasks(this, plan, loadOpts);
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index c3efe64..8218599 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -104,6 +104,7 @@
 
         mTargetView.getDismissIconView().setAlpha(mDismissIconAlpha * level);
         mTargetView.getThumbnailView().setZ(z);
+        mTargetView.getDismissIconView().setZ(z);
     }
 
     public float getFocusProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 85fb50e..bd85748 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -38,6 +38,8 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
 import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
 import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
@@ -85,6 +87,24 @@
         mCornerRadius = getResources().getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius);
         mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (ssp.isTouchExplorationEnabled()) {
+            mDismissIconView.setFocusable(true);
+            mDismissIconView.setFocusableInTouchMode(true);
+            mDismissIconView.setOnFocusChangeListener(new OnFocusChangeListener() {
+                @Override
+                public void onFocusChange(View v, boolean hasFocus) {
+                    if (hasFocus) {
+                        setDismissState(true);
+                    } else {
+                        setDismissState(false);
+                    }
+                }
+            });
+        } else {
+            mDismissIconView.setFocusable(false);
+            mDismissIconView.setFocusableInTouchMode(false);
+        }
         mViewFocusAnimator = new ViewFocusAnimator(this);
     }
 
@@ -288,7 +308,6 @@
         lp.height = LayoutParams.MATCH_PARENT;
 
         screenshotView.setLayoutParams(lp);
-        screenshotView.setImageBitmap(screenshot);
         screenshotView.setClipToOutline(true);
         screenshotView.setOutlineProvider(new ViewOutlineProvider() {
             @Override
@@ -296,6 +315,7 @@
                 outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
             }
         });
+        screenshotView.setImageBitmap(screenshot);
     }
 
     private void setAsBannerView(Drawable banner, ImageView bannerView) {
@@ -304,7 +324,6 @@
                 .getDimensionPixelSize(R.dimen.recents_tv_banner_width);
         lp.height = getResources()
                 .getDimensionPixelSize(R.dimen.recents_tv_banner_height);
-
         bannerView.setLayoutParams(lp);
         bannerView.setImageDrawable(banner);
     }
@@ -327,4 +346,16 @@
     public View getDismissIconView() {
         return mDismissIconView;
     }
+
+    public static int getNumberOfVisibleTasks(Context context) {
+        Resources res = context.getResources();
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+        Point size = new Point();
+        display.getSize(size);
+        int screenWidth = size.x;
+        int cardWidth = res.getDimensionPixelSize(R.dimen.recents_tv_card_width);
+        int spacing = res.getDimensionPixelSize(R.dimen.recents_tv_gird_card_spacing);
+        return (int) (1.0 + Math.ceil(screenWidth / (cardWidth + spacing * 2.0)));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index 0a6d1ed..b6b86b4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -23,9 +23,11 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.views.AnimationProps;
 
@@ -122,7 +124,10 @@
 
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
-        holder.init(mTaskList.get(position));
+        Task task = mTaskList.get(position);
+        // Retrives from caches, loading only if necessary
+        Recents.getTaskLoader().loadTaskData(task);
+        holder.init(task);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 64eaeec..f728aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -1065,9 +1065,6 @@
         mEntranceAnimationRunning = true;
         resizeStack(position, mSnapAlgorithm.getMiddleTarget().position,
                 mSnapAlgorithm.getMiddleTarget());
-
-        // Vibrate after docking
-        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
     public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
@@ -1105,9 +1102,6 @@
             mExitStartPosition = getCurrentPosition();
             stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */,
                     0 /* endDelay */, Interpolators.FAST_OUT_SLOW_IN);
-
-            // Vibrate after undocking
-            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e66dc74..63933d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -189,6 +189,7 @@
 
     protected boolean mVisible;
     protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+    protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
 
     /**
      * Notifications with keys in this set are not actually around anymore. We kept them around
@@ -968,7 +969,7 @@
         return vetoButton;
     }
 
-    private void performRemoveNotification(StatusBarNotification n, boolean removeView) {
+    protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
         final String pkg = n.getPackageName();
         final String tag = n.getTag();
         final int id = n.getId();
@@ -980,6 +981,9 @@
                 mKeysKeptForRemoteInput.remove(n.getKey());
                 removeView = true;
             }
+            if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
+                removeView = true;
+            }
             if (removeView) {
                 removeNotification(n.getKey(), null);
             }
@@ -2085,8 +2089,7 @@
 
     /**
      * The LEDs are turned off when the notification panel is shown, even just a little bit.
-     * See also NotificationStackScrollLayout.setIsExpanded() for another place where we
-     * attempt to do this.
+     * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
      */
     protected void handleVisibleToUserChanged(boolean visibleToUser) {
         try {
@@ -2326,8 +2329,9 @@
         Entry entry = mNotificationData.get(key);
         if (entry == null) {
             return;
-        } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+        } else {
             mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+            mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
         }
 
         Notification n = notification.getNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b4ce9cd..c53ab59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -22,7 +22,6 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.Notification;
 import android.content.Context;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
@@ -31,6 +30,8 @@
 import android.os.Build;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Property;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
@@ -39,13 +40,11 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Chronometer;
 import android.widget.ImageView;
-import android.widget.RemoteViews;
 
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
@@ -65,6 +64,7 @@
     private int mMaxHeadsUpHeight;
     private int mNotificationMinHeight;
     private int mNotificationMaxHeight;
+    private int mIncreasedPaddingBetweenElements;
 
     /** Does this row contain layouts that can adapt to row expansion */
     private boolean mExpandable;
@@ -97,7 +97,7 @@
      */
     private boolean mOnKeyguard;
 
-    private AnimatorSet mTranslateAnim;
+    private Animator mTranslateAnim;
     private ArrayList<View> mTranslateableViews;
     private NotificationContentView mPublicLayout;
     private NotificationContentView mPrivateLayout;
@@ -115,8 +115,6 @@
     private String mAppName;
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
-    private NotificationHeaderView mNotificationHeader;
-    private NotificationViewWrapper mNotificationHeaderWrapper;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
     private boolean mChildrenExpanded;
@@ -128,13 +126,14 @@
     private boolean mIsPinned;
     private FalsingManager mFalsingManager;
     private HeadsUpManager mHeadsUpManager;
-    private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this);
 
     private boolean mJustClicked;
     private boolean mIconAnimationRunning;
     private boolean mShowNoBackground;
     private ExpandableNotificationRow mNotificationParent;
     private OnExpandClickListener mOnExpandClickListener;
+    private boolean mGroupExpansionChanging;
+
     private OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -142,6 +141,8 @@
                 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
                 mOnExpandClickListener.onExpandClicked(mEntry,
                         mGroupManager.isGroupExpanded(mStatusBarNotification));
+                mGroupExpansionChanging = true;
+                updateBackgroundForGroupState();
             } else {
                 boolean nowExpanded;
                 if (isPinned()) {
@@ -160,6 +161,29 @@
     private boolean mDismissed;
     private boolean mKeepInParent;
     private boolean mRemoved;
+    private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
+            new FloatProperty<ExpandableNotificationRow>("translate") {
+                @Override
+                public void setValue(ExpandableNotificationRow object, float value) {
+                    object.setTranslation(value);
+                }
+
+                @Override
+                public Float get(ExpandableNotificationRow object) {
+                    return object.getTranslation();
+                }
+    };
+
+    public boolean isGroupExpansionChanging() {
+        if (isChildInGroup()) {
+            return mNotificationParent.isGroupExpansionChanging();
+        }
+        return mGroupExpansionChanging;
+    }
+
+    public void setGroupExpansionChanging(boolean changing) {
+        mGroupExpansionChanging = changing;
+    }
 
     public NotificationContentView getPrivateLayout() {
         return mPrivateLayout;
@@ -172,8 +196,8 @@
     public void setIconAnimationRunning(boolean running) {
         setIconAnimationRunning(running, mPublicLayout);
         setIconAnimationRunning(running, mPrivateLayout);
-        setIconAnimationRunningForChild(running, mNotificationHeader);
         if (mIsSummaryWithChildren) {
+            setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
             List<ExpandableNotificationRow> notificationChildren =
                     mChildrenContainer.getNotificationChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
@@ -235,7 +259,7 @@
         updateNotificationColor();
         updateClearability();
         if (mIsSummaryWithChildren) {
-            recreateNotificationHeader();
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener, mEntry.notification);
             mChildrenContainer.onNotificationUpdated();
         }
         if (mIconAnimationRunning) {
@@ -329,9 +353,6 @@
         if (mChildrenContainer != null) {
             mChildrenContainer.removeNotification(row);
         }
-        if (!row.isRemoved()) {
-            mHeaderUtil.restoreNotificationHeader(row);
-        }
         onChildrenCountChanged();
         row.setIsChildInGroup(false, null);
     }
@@ -352,7 +373,10 @@
         boolean childInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
         mNotificationParent = childInGroup ? parent : null;
         mPrivateLayout.setIsChildInGroup(childInGroup);
-        updateNoBackgroundState();
+        updateBackgroundForGroupState();
+        if (mNotificationParent != null) {
+            mNotificationParent.updateBackgroundForGroupState();
+        }
     }
 
     @Override
@@ -544,15 +568,15 @@
     }
 
     public NotificationHeaderView getNotificationHeader() {
-        if (mNotificationHeader != null) {
-            return mNotificationHeader;
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getHeaderView();
         }
         return mPrivateLayout.getNotificationHeader();
     }
 
     private NotificationHeaderView getVisibleNotificationHeader() {
-        if (mNotificationHeader != null) {
-            return mNotificationHeader;
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getHeaderView();
         }
         return getShowingLayout().getVisibleNotificationHeader();
     }
@@ -568,11 +592,8 @@
     public void reInflateViews() {
         initDimens();
         if (mIsSummaryWithChildren) {
-            removeView(mNotificationHeader);
-            mNotificationHeader = null;
-            recreateNotificationHeader();
             if (mChildrenContainer != null) {
-                mChildrenContainer.reInflateViews();
+                mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
             }
         }
         if (mGuts != null) {
@@ -646,9 +667,6 @@
                 continue;
             }
             mChildrenContainer.removeNotification(row);
-            if (!row.isRemoved()) {
-                mHeaderUtil.restoreNotificationHeader(row);
-            }
             row.setIsChildInGroup(false, null);
         }
         onChildrenCountChanged();
@@ -684,8 +702,13 @@
         return mRemoved;
     }
 
-    public void setRemoved(boolean removed) {
-        mRemoved = removed;
+    public void setRemoved() {
+        mRemoved = true;
+
+        mPrivateLayout.setRemoved();
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setRemoved();
+        }
     }
 
     public NotificationChildrenContainer getChildrenContainer() {
@@ -709,6 +732,8 @@
         mMaxHeadsUpHeightLegacy = getFontScaledHeight(
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
+        mIncreasedPaddingBetweenElements = getResources()
+                .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
     }
 
     /**
@@ -804,17 +829,13 @@
         mTranslateableViews.remove(mGutsStub);
     }
 
-    private void setTranslationForOutline(float translationX) {
-        setOutlineRect(false, translationX, getTop(), getRight() + translationX, getBottom());
-    }
-
     public void resetTranslation() {
         if (mTranslateableViews != null) {
             for (int i = 0; i < mTranslateableViews.size(); i++) {
                 mTranslateableViews.get(i).setTranslationX(0);
             }
-            setTranslationForOutline(0);
         }
+        invalidateOutline();
         if (mSettingsIconRow != null) {
             mSettingsIconRow.resetState();
         }
@@ -824,8 +845,7 @@
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
         }
-        mTranslateAnim = (AnimatorSet) getTranslateViewAnimator(leftTarget,
-                null /* updateListener */);
+        mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
         if (mTranslateAnim != null) {
             mTranslateAnim.start();
         }
@@ -843,7 +863,7 @@
                 mTranslateableViews.get(i).setTranslationX(translationX);
             }
         }
-        setTranslationForOutline(translationX);
+        invalidateOutline();
         if (mSettingsIconRow != null) {
             mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth());
         }
@@ -867,48 +887,29 @@
             // No translation if guts are exposed.
             return null;
         }
-        AnimatorSet set = new AnimatorSet();
-        if (mTranslateableViews != null) {
-            for (int i = 0; i < mTranslateableViews.size(); i++) {
-                final View animView = mTranslateableViews.get(i);
-                final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(
-                        animView, "translationX", leftTarget);
-                if (i == 0) {
-                    translateAnim.addUpdateListener(new AnimatorUpdateListener() {
-                        @Override
-                        public void onAnimationUpdate(ValueAnimator animation) {
-                            setTranslationForOutline((float) animation.getAnimatedValue());
-                            if (mSettingsIconRow != null) {
-                                mSettingsIconRow.updateSettingsIcons(
-                                        (float) animation.getAnimatedValue(), getMeasuredWidth());
-                            }
-                        }
-                    });
-                    if (listener != null) {
-                        translateAnim.addUpdateListener(listener);
-                    }
-                    translateAnim.addListener(new AnimatorListenerAdapter() {
-                        boolean cancelled = false;
-
-                        @Override
-                        public void onAnimationCancel(Animator anim) {
-                            cancelled = true;
-                        }
-
-                        @Override
-                        public void onAnimationEnd(Animator anim) {
-                            if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
-                                mSettingsIconRow.resetState();
-                                mTranslateAnim = null;
-                            }
-                        }
-                    });
-                }
-                set.play(translateAnim);
-            }
+        final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
+                leftTarget);
+        if (listener != null) {
+            translateAnim.addUpdateListener(listener);
         }
-        mTranslateAnim = set;
-        return set;
+        translateAnim.addListener(new AnimatorListenerAdapter() {
+            boolean cancelled = false;
+
+            @Override
+            public void onAnimationCancel(Animator anim) {
+                cancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator anim) {
+                if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
+                    mSettingsIconRow.resetState();
+                    mTranslateAnim = null;
+                }
+            }
+        });
+        mTranslateAnim = translateAnim;
+        return translateAnim;
     }
 
     public float getSpaceForGear() {
@@ -937,9 +938,8 @@
         if (mChildrenContainer != null) {
             mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
                     : INVISIBLE);
-        }
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
+            mChildrenContainer.updateHeaderVisibility(!mShowingPublic && mIsSummaryWithChildren
+                    ? VISIBLE
                     : INVISIBLE);
         }
         // The limits might have changed if the view suddenly became a group or vice versa
@@ -969,7 +969,6 @@
         }
         if (mIsSummaryWithChildren) {
             mChildrenContainer.setDark(dark, fade, delay);
-            mNotificationHeaderWrapper.setDark(dark, fade, delay);
         }
     }
 
@@ -1044,6 +1043,9 @@
         mPrivateLayout.setUserExpanding(userLocked);
         if (mIsSummaryWithChildren) {
             mChildrenContainer.setUserLocked(userLocked);
+            if (userLocked) {
+                updateBackgroundForGroupState();
+            }
         }
     }
 
@@ -1123,30 +1125,28 @@
         }
     }
 
-    private boolean isGroupExpanded() {
+    public boolean isGroupExpanded() {
         return mGroupManager.isGroupExpanded(mStatusBarNotification);
     }
 
-    /**
-     * @return whether this view has a header on the top of the content
-     */
-    private boolean hasNotificationHeader() {
-        return mIsSummaryWithChildren;
-    }
-
     private void onChildrenCountChanged() {
         mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
-                && mChildrenContainer != null && mChildrenContainer.getChildCount() > 0;
-        if (mIsSummaryWithChildren) {
-            if (mNotificationHeader == null) {
-                recreateNotificationHeader();
-            }
+                && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
+        if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
+                    mEntry.notification);
         }
         mPrivateLayout.updateExpandButtons(isExpandable());
         updateChildrenHeaderAppearance();
         updateChildrenVisibility();
     }
 
+    public void updateChildrenHeaderAppearance() {
+        if (mChildrenContainer != null) {
+            mChildrenContainer.updateChildrenHeaderAppearance();
+        }
+    }
+
     /**
      * Check whether the view state is currently expanded. This is given by the system in {@link
      * #setSystemExpanded(boolean)} and can be overridden by user expansion or
@@ -1251,8 +1251,8 @@
     }
 
     private void animateShowingPublic(long delay, long duration) {
-        View[] privateViews = mIsSummaryWithChildren ?
-                new View[] {mChildrenContainer, mNotificationHeader}
+        View[] privateViews = mIsSummaryWithChildren
+                ? new View[] {mChildrenContainer}
                 : new View[] {mPrivateLayout};
         View[] publicViews = new View[] {mPublicLayout};
         View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
@@ -1302,9 +1302,6 @@
 
     public void setChildrenExpanded(boolean expanded, boolean animate) {
         mChildrenExpanded = expanded;
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setExpanded(expanded);
-        }
         if (mChildrenContainer != null) {
             mChildrenContainer.setChildrenExpanded(expanded);
         }
@@ -1349,6 +1346,14 @@
     }
 
     @Override
+    public int getExtraBottomPadding() {
+        if (mIsSummaryWithChildren && isGroupExpanded()) {
+            return mIncreasedPaddingBetweenElements;
+        }
+        return 0;
+    }
+
+    @Override
     public void setActualHeight(int height, boolean notifyListeners) {
         super.setActualHeight(height, notifyListeners);
         if (mGuts != null && mGuts.areGutsExposed()) {
@@ -1406,33 +1411,6 @@
         }
     }
 
-    private void recreateNotificationHeader() {
-        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
-                getStatusBarNotification().getNotification());
-        final RemoteViews header = builder.makeNotificationHeader();
-        if (mNotificationHeader == null) {
-            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
-            final View expandButton = mNotificationHeader.findViewById(
-                    com.android.internal.R.id.expand_button);
-            expandButton.setVisibility(VISIBLE);
-            mNotificationHeader.setOnClickListener(mExpandClickListener);
-            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
-                    mNotificationHeader, this);
-            addView(mNotificationHeader, indexOfChild(mChildrenContainer) + 1);
-            mTranslateableViews.add(mNotificationHeader);
-        } else {
-            header.reapply(getContext(), mNotificationHeader);
-            mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
-        }
-        updateChildrenHeaderAppearance();
-    }
-
-    public void updateChildrenHeaderAppearance() {
-        if (mIsSummaryWithChildren) {
-            mHeaderUtil.updateChildrenHeaderAppearance();
-        }
-    }
-
     public boolean isMaxExpandHeightInitialized() {
         return mMaxExpandHeight != 0;
     }
@@ -1451,19 +1429,50 @@
     @Override
     protected void updateBackgroundTint() {
         super.updateBackgroundTint();
-        updateNoBackgroundState();
+        updateBackgroundForGroupState();
         if (mIsSummaryWithChildren) {
             List<ExpandableNotificationRow> notificationChildren =
                     mChildrenContainer.getNotificationChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
                 ExpandableNotificationRow child = notificationChildren.get(i);
-                child.updateNoBackgroundState();
+                child.updateBackgroundForGroupState();
             }
         }
     }
 
-    private void updateNoBackgroundState() {
-        mShowNoBackground = isChildInGroup() && hasSameBgColor(mNotificationParent);
+    /**
+     * Called when a group has finished animating from collapsed or expanded state.
+     */
+    public void onFinishedExpansionChange() {
+        mGroupExpansionChanging = false;
+        updateBackgroundForGroupState();
+    }
+
+    /**
+     * Updates the parent and children backgrounds in a group based on the expansion state.
+     */
+    public void updateBackgroundForGroupState() {
+        if (mIsSummaryWithChildren) {
+            // Only when the group has finished expanding do we hide its background.
+            mShowNoBackground = isGroupExpanded() && !isGroupExpansionChanging() && !isUserLocked();
+            mChildrenContainer.updateHeaderForExpansion(mShowNoBackground);
+            List<ExpandableNotificationRow> children = mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < children.size(); i++) {
+                children.get(i).updateBackgroundForGroupState();
+            }
+        } else if (isChildInGroup()) {
+            final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
+            // Only show a background if the group is expanded OR if it is expanding / collapsing
+            // and has a custom background color
+            final boolean showBackground = isGroupExpanded()
+                    || ((mNotificationParent.isGroupExpansionChanging()
+                            || mNotificationParent.isUserLocked()) && childColor != 0);
+            mShowNoBackground = !showBackground;
+        } else {
+            // Only children or parents ever need no background.
+            mShowNoBackground = false;
+        }
+        updateOutline();
         updateBackground();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index f98e87d..9d9f3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -33,22 +33,25 @@
     private boolean mCustomOutline;
     private float mOutlineAlpha = -1f;
 
+    ViewOutlineProvider mProvider = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            int translation = (int) getTranslation();
+            if (!mCustomOutline) {
+                outline.setRect(translation,
+                        mClipTopAmount,
+                        getWidth() + translation,
+                        Math.max(getActualHeight(), mClipTopAmount));
+            } else {
+                outline.setRect(mOutlineRect);
+            }
+            outline.setAlpha(mOutlineAlpha);
+        }
+    };
+
     public ExpandableOutlineView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                if (!mCustomOutline) {
-                    outline.setRect(0,
-                            mClipTopAmount,
-                            getWidth(),
-                            Math.max(getActualHeight(), mClipTopAmount));
-                } else {
-                    outline.setRect(mOutlineRect);
-                }
-                outline.setAlpha(mOutlineAlpha);
-            }
-        });
+        setOutlineProvider(mProvider);
     }
 
     @Override
@@ -87,17 +90,30 @@
 
     @Override
     public int getOutlineTranslation() {
-        return mCustomOutline ? mOutlineRect.left : 0;
+        return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
+    }
+
+    public void updateOutline() {
+        if (mCustomOutline) {
+            return;
+        }
+        boolean hasOutline = true;
+        if (isChildInGroup()) {
+            hasOutline = isGroupExpanded() && !isGroupExpansionChanging();
+        } else if (isSummaryWithChildren()) {
+            hasOutline = !isGroupExpanded() || isGroupExpansionChanging();
+        }
+        setOutlineProvider(hasOutline ? mProvider : null);
+    }
+
+    public boolean isOutlineShowing() {
+        ViewOutlineProvider op = getOutlineProvider();
+        return op != null;
     }
 
     protected void setOutlineRect(float left, float top, float right, float bottom) {
-        setOutlineRect(true, left, top, right, bottom);
-    }
-
-    protected void setOutlineRect(boolean clipToOutline, float left, float top, float right,
-            float bottom) {
         mCustomOutline = true;
-        setClipToOutline(clipToOutline);
+        setClipToOutline(true);
 
         mOutlineRect.set((int) left, (int) top, (int) right, (int) bottom);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 280663b..2c302ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -323,7 +323,7 @@
             if (top >= getActualHeight()) {
                 top = getActualHeight() - 1;
             }
-            mClipRect.set(0, top, getWidth(), getActualHeight());
+            mClipRect.set(0, top, getWidth(), getActualHeight() + getExtraBottomPadding());
             setClipBounds(mClipRect);
         } else {
             setClipBounds(null);
@@ -411,6 +411,28 @@
     }
 
     /**
+     * @return padding used to alter how much of the view is clipped.
+     */
+    public int getExtraBottomPadding() {
+        return 0;
+    }
+
+    /**
+     * @return true if the group's expansion state is changing, false otherwise.
+     */
+    public boolean isGroupExpansionChanging() {
+        return false;
+    }
+
+    public boolean isGroupExpanded() {
+        return false;
+    }
+
+    public boolean isChildInGroup() {
+        return false;
+    }
+
+    /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
     public interface OnHeightChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5a8d4b3..32b61cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -71,7 +71,6 @@
     private HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
-    private int mUnrestrictedContentHeight;
     private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
     private boolean mDark;
     private boolean mAnimate;
@@ -371,8 +370,7 @@
     }
 
     public void setContentHeight(int contentHeight) {
-        mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());;
-        mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
+        mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
         selectLayout(mAnimate /* animate */, false /* force */);
 
         int minHeightHint = getMinContentHeightHint();
@@ -591,7 +589,21 @@
         mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
     }
 
-    private int getBackgroundColor(int visibleType) {
+    public int getVisibleType() {
+        return mVisibleType;
+    }
+
+    public int getBackgroundColorForExpansionState() {
+        // When expanding or user locked we want the new type, when collapsing we want
+        // the original type
+        final int visibleType = (mContainingNotification.isGroupExpanded()
+                || mContainingNotification.isUserLocked())
+                        ? calculateVisibleType()
+                        : getVisibleType();
+        return getBackgroundColor(visibleType);
+    }
+
+    public int getBackgroundColor(int visibleType) {
         NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
         int customBackgroundColor = 0;
         if (currentVisibleWrapper != null) {
@@ -699,7 +711,7 @@
     /**
      * @return one of the static enum types in this view, calculated form the current state
      */
-    private int calculateVisibleType() {
+    public int calculateVisibleType() {
         if (mUserExpanding) {
             int height = !mIsChildInGroup || isGroupExpanded()
                     || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
@@ -1041,4 +1053,13 @@
     public HybridNotificationView getSingleLineView() {
         return mSingleLineView;
     }
+
+    public void setRemoved() {
+        if (mExpandedRemoteInput != null) {
+            mExpandedRemoteInput.setRemoved();
+        }
+        if (mHeadsUpRemoteInput != null) {
+            mHeadsUpRemoteInput.setRemoved();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 7575dc8..7f95d48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -90,7 +90,7 @@
     private final ArrayList<HeaderProcessor> mComparators = new ArrayList<>();
     private final HashSet<Integer> mDividers = new HashSet<>();
 
-    NotificationHeaderUtil(ExpandableNotificationRow row) {
+    public NotificationHeaderUtil(ExpandableNotificationRow row) {
         mRow = row;
         // To hide the icons if they are the same and the color is the same
         mComparators.add(new HeaderProcessor(mRow,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 182075f..0df1204 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -215,17 +215,23 @@
 
     public boolean isOnlyChildInSuppressedGroup(StatusBarNotification sbn) {
         return isGroupSuppressed(sbn.getGroupKey())
-                && isOnlyChildInGroup(sbn);
+                && isOnlyChild(sbn);
     }
 
-    public boolean isOnlyChildInGroup(StatusBarNotification sbn) {
+    private boolean isOnlyChild(StatusBarNotification sbn) {
         return !sbn.getNotification().isGroupSummary()
                 && getTotalNumberOfChildren(sbn) == 1;
     }
 
+    public boolean isOnlyChildInGroup(StatusBarNotification sbn) {
+        return isOnlyChild(sbn) && getLogicalGroupSummary(sbn) != null;
+    }
+
     private int getTotalNumberOfChildren(StatusBarNotification sbn) {
-        return getNumberOfIsolatedChildren(sbn.getGroupKey())
-                + mGroupMap.get(sbn.getGroupKey()).children.size();
+        int isolatedChildren = getNumberOfIsolatedChildren(sbn.getGroupKey());
+        NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
+        int realChildren = group != null ? group.children.size() : 0;
+        return isolatedChildren + realChildren;
     }
 
     private boolean isGroupSuppressed(String groupKey) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9dbec6d..6a37099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,6 +39,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 import android.widget.TextView;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.AutoReinflateContainer;
@@ -2291,6 +2292,12 @@
         setLaunchingAffordance(false);
     }
 
+    @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+        mNotificationStackScroller.setParentFadingOut(alpha != 1.0f);
+    }
+
     /**
      * Set whether we are currently launching an affordance. This is currently only set when
      * launched via a camera gesture.
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 e3ce1e2..31fa3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -263,6 +263,14 @@
      * This affects the status bar UI. */
     private static final boolean FREEFORM_WINDOW_MANAGEMENT;
 
+    /**
+     * How long to wait before auto-dismissing a notification that was kept for remote input, and
+     * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
+     * these given that they technically don't exist anymore. We wait a bit in case the app issues
+     * an update.
+     */
+    private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
+
     static {
         boolean onlyCoreApps;
         boolean freeformWindowManagement;
@@ -1181,16 +1189,24 @@
         mIconPolicy.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
 
-        if (FORCE_REMOTE_INPUT_HISTORY) {
-            mRemoteInputController.addCallback(new RemoteInputController.Callback() {
-                @Override
-                public void onRemoteInputSent(Entry entry) {
-                    if (mKeysKeptForRemoteInput.contains(entry.key)) {
-                        removeNotification(entry.key, null);
-                    }
+        mRemoteInputController.addCallback(new RemoteInputController.Callback() {
+            @Override
+            public void onRemoteInputSent(Entry entry) {
+                if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
+                    removeNotification(entry.key, null);
+                } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
+                    // We're currently holding onto this notification, but from the apps point of
+                    // view it is already canceled, so we'll need to cancel it on the apps behalf
+                    // after sending - unless the app posts an update in the mean time, so wait a
+                    // bit.
+                    mHandler.postDelayed(() -> {
+                        if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
+                            removeNotification(entry.key, null);
+                        }
+                    }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
                 }
-            });
-        }
+            }
+        });
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
         mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
@@ -1507,8 +1523,15 @@
             return;
         }
         Entry entry = mNotificationData.get(key);
+
+        if (entry != null && mRemoteInputController.isRemoteInputActive(entry)) {
+            mLatestRankingMap = ranking;
+            mRemoteInputEntriesToRemoveOnCollapse.add(entry);
+            return;
+        }
+
         if (entry != null && entry.row != null) {
-            entry.row.setRemoved(true);
+            entry.row.setRemoved();
         }
         // Let's remove the children if this was a summary
         handleGroupSummaryRemoved(key, ranking);
@@ -1556,7 +1579,7 @@
                 toRemove.get(i).setKeepInParent(true);
                 // we need to set this state earlier as otherwise we might generate some weird
                 // animations
-                toRemove.get(i).setRemoved(true);
+                toRemove.get(i).setRemoved();
             }
             for (int i = 0; i < toRemove.size(); i++) {
                 removeNotification(toRemove.get(i).getStatusBarNotification().getKey(), ranking);
@@ -1568,6 +1591,15 @@
     }
 
     @Override
+    protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
+        Entry entry = mNotificationData.get(n.getKey());
+        if (mRemoteInputController.isRemoteInputActive(entry)) {
+            mRemoteInputController.removeRemoteInput(entry);
+        }
+        super.performRemoveNotification(n, removeView);
+    }
+
+    @Override
     protected void refreshLayout(int layoutDirection) {
         if (mNavigationBarView != null) {
             mNavigationBarView.setLayoutDirection(layoutDirection);
@@ -2464,6 +2496,26 @@
 
     public void setPanelExpanded(boolean isExpanded) {
         mStatusBarWindowManager.setPanelExpanded(isExpanded);
+
+        if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
+            if (DEBUG) {
+                Log.v(TAG, "clearing notification effects from setPanelExpanded");
+            }
+            clearNotificationEffects();
+        }
+
+        if (!isExpanded) {
+            removeRemoteInputEntriesKeptUntilCollapsed();
+        }
+    }
+
+    private void removeRemoteInputEntriesKeptUntilCollapsed() {
+        for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
+            Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
+            mRemoteInputController.removeRemoteInput(entry);
+            removeNotification(entry.key, mLatestRankingMap);
+        }
+        mRemoteInputEntriesToRemoveOnCollapse.clear();
     }
 
     public void onScreenTurnedOff() {
@@ -3865,6 +3917,7 @@
                 mScrimController.forceHideScrims(true /* hide */);
                 updateMediaMetaData(false, true);
                 mNotificationPanel.setAlpha(1);
+                mStackScroller.setParentFadingOut(true);
                 mNotificationPanel.animate()
                         .alpha(0)
                         .setStartDelay(FADE_KEYGUARD_START_DELAY)
@@ -4206,6 +4259,9 @@
                 || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
             clearNotificationEffects();
         }
+        if (state == StatusBarState.KEYGUARD) {
+            removeRemoteInputEntriesKeptUntilCollapsed();
+        }
         mState = state;
         mGroupManager.setStatusBarState(state);
         mFalsingManager.setStatusBarState(state);
@@ -4334,6 +4390,9 @@
         if (expandView instanceof ExpandableNotificationRow) {
             row = (ExpandableNotificationRow) expandView;
             row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+            // Indicate that the group expansion is changing at this time -- this way the group
+            // and children backgrounds / divider animations will look correct.
+            row.setGroupExpansionChanging(true);
         }
         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 493b23f..29b4db1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -211,7 +211,7 @@
 
     public void animateToggleQSExpansion() {
         // TODO: Better path to animated panel expansion.
-        mHeader.performClick();
+        mHeader.callOnClick();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index eae0b8e..9c4480e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -322,6 +322,7 @@
         if (scrim instanceof ScrimView) {
             float alpha2 = getDozeAlpha(scrim);
             float alpha = 1 - (1 - alpha1) * (1 - alpha2);
+            alpha = Math.max(0, Math.min(1.0f, alpha));
             ((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0));
         } else {
             scrim.setAlpha(alpha1);
@@ -468,6 +469,7 @@
                     PropertyValuesHolder[] values = previousAnimator.getValues();
                     float relativeDiff = alpha - previousEndValue;
                     float newStartValue = previousStartValue + relativeDiff;
+                    newStartValue = Math.max(0, Math.min(1.0f, newStartValue));
                     values[0].setFloatValues(newStartValue, alpha);
                     scrim.setTag(TAG_START_ALPHA, newStartValue);
                     scrim.setTag(TAG_END_ALPHA, alpha);
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 ddded49..21d03fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -53,7 +53,7 @@
 
     public void showMirror() {
         mBrightnessMirror.setVisibility(View.VISIBLE);
-        mStackScroller.setFadedOut(true);
+        mStackScroller.setFadingOut(true);
         mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, Interpolators.ALPHA_OUT);
         outAnimation(mNotificationPanel.animate())
                 .withLayer();
@@ -67,7 +67,7 @@
                     @Override
                     public void run() {
                         mBrightnessMirror.setVisibility(View.INVISIBLE);
-                        mStackScroller.setFadedOut(false);
+                        mStackScroller.setFadingOut(false);
                     }
                 });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 095265a..2f522f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@
 
     private ScrollContainer mScrollContainer;
     private View mScrollContainerChild;
+    private boolean mRemoved;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -171,7 +172,12 @@
     public void onDefocus() {
         mController.removeRemoteInput(mEntry);
         mEntry.remoteInputText = mEditText.getText();
-        setVisibility(INVISIBLE);
+
+        // During removal, we get reattached and lose focus. Not hiding in that
+        // case to prevent flicker.
+        if (!mRemoved) {
+            setVisibility(INVISIBLE);
+        }
         MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
                 mEntry.notification.getPackageName());
     }
@@ -347,6 +353,10 @@
         return mPendingIntent;
     }
 
+    public void setRemoved() {
+        mRemoved = true;
+    }
+
     /**
      * An EditText that changes appearance based on whether it's focusable and becomes
      * un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -416,6 +426,15 @@
         }
 
         @Override
+        public boolean onCheckIsTextEditor() {
+            // Stop being editable while we're being removed. During removal, we get reattached,
+            // and editable views get their spellchecking state re-evaluated which is too costly
+            // during the removal animation.
+            boolean flyingOut = mRemoteInputView != null && mRemoteInputView.mRemoved;
+            return !flyingOut && super.onCheckIsTextEditor();
+        }
+
+        @Override
         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
             final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
 
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 e919d5c..cb0b848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -16,21 +16,28 @@
 
 package com.android.systemui.statusbar.stack;
 
+import android.app.Notification;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.drawable.ColorDrawable;
+import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.RemoteViews;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationHeaderUtil;
 import com.android.systemui.statusbar.notification.HybridGroupManager;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 import java.util.ArrayList;
@@ -51,7 +58,7 @@
     private int mChildPadding;
     private int mDividerHeight;
     private int mMaxNotificationHeight;
-    private int mNotificationHeaderHeight;
+    private int mNotificationHeaderMargin;
     private int mNotificatonTopPadding;
     private float mCollapsedBottompadding;
     private ViewInvertHelper mOverflowInvertHelper;
@@ -63,6 +70,12 @@
     private boolean mUserLocked;
     private int mActualHeight;
     private boolean mNeverAppliedGroupState;
+    private int mHeaderHeight;
+
+    private NotificationHeaderView mNotificationHeader;
+    private NotificationViewWrapper mNotificationHeaderWrapper;
+    private NotificationHeaderUtil mHeaderUtil;
+    private ViewState mHeaderViewState;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -88,9 +101,10 @@
                 R.dimen.notification_children_padding);
         mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
                 R.dimen.notification_divider_height));
+        mHeaderHeight = getResources().getDimensionPixelSize(R.dimen.notification_header_height);
         mMaxNotificationHeight = getResources().getDimensionPixelSize(
                 R.dimen.notification_max_height);
-        mNotificationHeaderHeight = getResources().getDimensionPixelSize(
+        mNotificationHeaderMargin = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_top);
         mNotificatonTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notification_children_container_top_padding);
@@ -112,6 +126,10 @@
             mOverflowNumber.layout(getWidth() - mOverflowNumber.getMeasuredWidth(), 0, getWidth(),
                     mOverflowNumber.getMeasuredHeight());
         }
+        if (mNotificationHeader != null) {
+            mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
+                    mNotificationHeader.getMeasuredHeight());
+        }
     }
 
     @Override
@@ -131,7 +149,7 @@
                     newHeightSpec);
         }
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
-        int height = mNotificationHeaderHeight + mNotificatonTopPadding;
+        int height = mNotificationHeaderMargin + mNotificatonTopPadding;
         int childCount = Math.min(mChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
         int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         int overflowIndex = childCount > collapsedChildren ? collapsedChildren - 1 : -1;
@@ -155,6 +173,12 @@
         if (heightMode != MeasureSpec.UNSPECIFIED) {
             height = Math.min(height, size);
         }
+
+        if (mNotificationHeader != null) {
+            int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
+            mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
+        }
+
         setMeasuredDimension(width, height);
     }
 
@@ -201,6 +225,41 @@
         row.setSystemChildExpanded(false);
         row.setUserLocked(false);
         updateGroupOverflow();
+        if (!row.isRemoved()) {
+            mHeaderUtil.restoreNotificationHeader(row);
+        }
+    }
+
+    /**
+     * @return The number of notification children in the container.
+     */
+    public int getNotificationChildCount() {
+        return mChildren.size();
+    }
+
+    public void recreateNotificationHeader(OnClickListener listener, StatusBarNotification notification) {
+        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
+                mNotificationParent.getStatusBarNotification().getNotification());
+        final RemoteViews header = builder.makeNotificationHeader();
+        if (mNotificationHeader == null) {
+            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
+            final View expandButton = mNotificationHeader.findViewById(
+                    com.android.internal.R.id.expand_button);
+            expandButton.setVisibility(VISIBLE);
+            mNotificationHeader.setOnClickListener(listener);
+            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
+                    mNotificationHeader, mNotificationParent);
+            addView(mNotificationHeader, 0);
+            invalidate();
+        } else {
+            header.reapply(getContext(), mNotificationHeader);
+            mNotificationHeaderWrapper.notifyContentUpdated(notification);
+        }
+        updateChildrenHeaderAppearance();
+    }
+
+    public void updateChildrenHeaderAppearance() {
+        mHeaderUtil.updateChildrenHeaderAppearance();
     }
 
     public void updateGroupOverflow() {
@@ -210,7 +269,7 @@
             mOverflowNumber = mHybridGroupManager.bindOverflowNumber(
                     mOverflowNumber, childCount - maxAllowedVisibleChildren);
             if (mOverflowInvertHelper == null) {
-                mOverflowInvertHelper= new ViewInvertHelper(mOverflowNumber,
+                mOverflowInvertHelper = new ViewInvertHelper(mOverflowNumber,
                         NotificationPanelView.DOZE_ANIMATION_DURATION);
             }
             if (mGroupOverFlowState == null) {
@@ -300,7 +359,7 @@
      *         in @param maxAllowedVisibleChildren
      */
     private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
-        int intrinsicHeight = mNotificationHeaderHeight;
+        int intrinsicHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         boolean firstChild = true;
@@ -353,7 +412,7 @@
      */
     public void getState(StackScrollState resultState, StackViewState parentState) {
         int childCount = mChildren.size();
-        int yPosition = mNotificationHeaderHeight;
+        int yPosition = mNotificationHeaderMargin;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         int lastVisibleIndex = maxAllowedVisibleChildren - 1;
@@ -363,12 +422,16 @@
             expandFactor = getGroupExpandFraction();
             firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         }
+
+        boolean childrenExpanded = !mNotificationParent.isGroupExpansionChanging()
+                && mChildrenExpanded;
+        int parentHeight = parentState.height;
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (!firstChild) {
                 if (mUserLocked) {
-                     yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
-                             expandFactor);
+                    yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
+                            expandFactor);
                 } else {
                     yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
                 }
@@ -383,11 +446,28 @@
                 }
                 firstChild = false;
             }
+
             StackViewState childState = resultState.getViewStateForView(child);
             int intrinsicHeight = child.getIntrinsicHeight();
+            if (childrenExpanded) {
+                // When a group is expanded and moving into bottom stack, the bottom visible child
+                // adjusts its height to move into it. Children after it are hidden.
+                if (updateChildStateForExpandedGroup(child, parentHeight, childState, yPosition)) {
+                    // Clipping might be deactivated if the view is transforming, however, clipping
+                    // the child into the bottom stack should take precedent over this.
+                    childState.isBottomClipped = true;
+                }
+            } else {
+                childState.hidden = false;
+                childState.height = intrinsicHeight;
+                childState.isBottomClipped = false;
+            }
             childState.yTranslation = yPosition;
-            childState.zTranslation = 0;
-            childState.height = intrinsicHeight;
+            // When the group is expanded, the children cast the shadows rather than the parent
+            // so use the parent's elevation here.
+            childState.zTranslation = childrenExpanded
+                    ? mNotificationParent.getTranslationZ()
+                    : 0;
             childState.dimmed = parentState.dimmed;
             childState.dark = parentState.dark;
             childState.hideSensitive = parentState.hideSensitive;
@@ -422,10 +502,44 @@
                     mGroupOverFlowState.alpha = mirrorView.getAlpha();
                 }
             } else {
-                mGroupOverFlowState.yTranslation += mNotificationHeaderHeight;
+                mGroupOverFlowState.yTranslation += mNotificationHeaderMargin;
                 mGroupOverFlowState.alpha = 0.0f;
             }
         }
+        if (mNotificationHeader != null) {
+            if (mHeaderViewState == null) {
+                mHeaderViewState = new ViewState();
+            }
+            mHeaderViewState.initFrom(mNotificationHeader);
+            mHeaderViewState.zTranslation = childrenExpanded
+                    ? mNotificationParent.getTranslationZ()
+                    : 0;
+        }
+    }
+
+    /**
+     * When moving into the bottom stack, the bottom visible child in an expanded group adjusts its
+     * height, children in the group after this are gone.
+     *
+     * @param child the child who's height to adjust.
+     * @param parentHeight the height of the parent.
+     * @param childState the state to update.
+     * @param yPosition the yPosition of the view.
+     * @return true if children after this one should be hidden.
+     */
+    private boolean updateChildStateForExpandedGroup(ExpandableNotificationRow child,
+            int parentHeight, StackViewState childState, int yPosition) {
+        final int top = yPosition + child.getClipTopAmount();
+        final int intrinsicHeight = child.getIntrinsicHeight();
+        final int bottom = top + intrinsicHeight;
+        int newHeight = intrinsicHeight;
+        if (bottom >= parentHeight) {
+            // Child is either clipped or gone
+            newHeight = Math.max((parentHeight - top), 0);
+        }
+        childState.hidden = newHeight == 0;
+        childState.height = newHeight;
+        return childState.height != intrinsicHeight && !childState.hidden;
     }
 
     private int getMaxAllowedVisibleChildren() {
@@ -450,6 +564,8 @@
         if (mUserLocked) {
             expandFraction = getGroupExpandFraction();
         }
+        final boolean dividersVisible = mUserLocked
+                || mNotificationParent.isGroupExpansionChanging();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -464,6 +580,7 @@
                 alpha = NotificationUtils.interpolate(0, 0.5f,
                         Math.min(viewState.alpha, expandFraction));
             }
+            tmpState.hidden = !dividersVisible;
             tmpState.alpha = alpha;
             state.applyViewState(divider, tmpState);
             // There is no fake shadow to be drawn on the children
@@ -473,6 +590,9 @@
             state.applyViewState(mOverflowNumber, mGroupOverFlowState);
             mNeverAppliedGroupState = false;
         }
+        if (mNotificationHeader != null) {
+            state.applyViewState(mNotificationHeader, mHeaderViewState);
+        }
     }
 
     /**
@@ -491,6 +611,8 @@
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = getGroupExpandFraction();
+        final boolean dividersVisible = mUserLocked
+                || mNotificationParent.isGroupExpansionChanging();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
@@ -505,6 +627,7 @@
                 alpha = NotificationUtils.interpolate(0, 0.5f,
                         Math.min(viewState.alpha, expandFraction));
             }
+            tmpState.hidden = !dividersVisible;
             tmpState.alpha = alpha;
             stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
             // There is no fake shadow to be drawn on the children
@@ -521,6 +644,9 @@
             stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState,
                     baseDelay, duration);
         }
+        if (mNotificationHeader != null) {
+            state.applyViewState(mNotificationHeader, mHeaderViewState);
+        }
     }
 
     public ExpandableNotificationRow getViewAtPosition(float y) {
@@ -541,14 +667,45 @@
     public void setChildrenExpanded(boolean childrenExpanded) {
         mChildrenExpanded = childrenExpanded;
         updateExpansionStates();
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setExpanded(childrenExpanded);
+        }
     }
 
     public void setNotificationParent(ExpandableNotificationRow parent) {
         mNotificationParent = parent;
+        mHeaderUtil = new NotificationHeaderUtil(mNotificationParent);
+    }
+
+    public NotificationHeaderView getHeaderView() {
+        return mNotificationHeader;
+    }
+
+    public void updateHeaderVisibility(int visiblity) {
+        if (mNotificationHeader != null) {
+            mNotificationHeader.setVisibility(visiblity);
+        }
+    }
+
+    /**
+     * Called when a groups expansion changes to adjust the background of the header view.
+     *
+     * @param expanded whether the group is expanded.
+     */
+    public void updateHeaderForExpansion(boolean expanded) {
+        if (mNotificationHeader != null) {
+            if (expanded) {
+                ColorDrawable cd = new ColorDrawable();
+                cd.setColor(mNotificationParent.calculateBgColor());
+                mNotificationHeader.setHeaderBackgroundDrawable(cd);
+            } else {
+                mNotificationHeader.setHeaderBackgroundDrawable(null);
+            }
+        }
     }
 
     public int getMaxContentHeight() {
-        int maxContentHeight = mNotificationHeaderHeight + mNotificatonTopPadding;
+        int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -601,7 +758,7 @@
     }
 
     private int getVisibleChildrenExpandHeight() {
-        int intrinsicHeight = mNotificationHeaderHeight + mNotificatonTopPadding + mDividerHeight;
+        int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -628,7 +785,7 @@
     }
 
     private int getMinHeight(int maxAllowedVisibleChildren) {
-        int minExpandHeight = mNotificationHeaderHeight;
+        int minExpandHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
         boolean firstChild = true;
         int childCount = mChildren.size();
@@ -653,9 +810,13 @@
         if (mOverflowNumber != null) {
             mOverflowInvertHelper.setInverted(dark, fade, delay);
         }
+        mNotificationHeaderWrapper.setDark(dark, fade, delay);
     }
 
-    public void reInflateViews() {
+    public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
+        removeView(mNotificationHeader);
+        mNotificationHeader = null;
+        recreateNotificationHeader(listener, notification);
         initDimens();
         for (int i = 0; i < mDividers.size(); i++) {
             View prevDivider = mDividers.get(i);
@@ -685,4 +846,12 @@
         mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
                 mNotificationParent.getNotificationColor());
     }
+
+    public void setRemoved() {
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableNotificationRow child = mChildren.get(i);
+            child.setRemoved();
+        }
+    }
 }
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 70d4aec..4c242cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -34,6 +34,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -47,6 +48,8 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.OverScroller;
@@ -333,8 +336,10 @@
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
     private boolean mPulsing;
     private boolean mDrawBackgroundAsSrc;
-    private boolean mFadedOut;
+    private boolean mFadingOut;
+    private boolean mParentFadingOut;
     private boolean mGroupExpandedForMeasure;
+    private boolean mScrollable;
     private View mForcedScroll;
     private float mBackgroundFadeAmount = 1.0f;
     private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
@@ -442,7 +447,6 @@
 
     private void initView(Context context) {
         mScroller = new OverScroller(getContext());
-        setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setClipChildren(false);
         final ViewConfiguration configuration = ViewConfiguration.get(context);
@@ -470,7 +474,8 @@
     }
 
     private void updateSrcDrawing() {
-        mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadedOut ? mSrcMode : null);
+        mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && (!mFadingOut && !mParentFadingOut)
+                ? mSrcMode : null);
         invalidate();
     }
 
@@ -728,6 +733,7 @@
         mQsContainer = qsContainer;
     }
 
+    @Override
     public void onChildDismissed(View v) {
         ExpandableNotificationRow row = (ExpandableNotificationRow) v;
         if (!row.isDismissed()) {
@@ -815,6 +821,7 @@
         return true; // Don't fade out the notification
     }
 
+    @Override
     public void onBeginDrag(View v) {
         mFalsingManager.onNotificatonStartDismissing();
         setSwipingInProgress(true);
@@ -843,6 +850,7 @@
         return false;
     }
 
+    @Override
     public void onDragCancelled(View v) {
         mFalsingManager.onNotificatonStopDismissing();
         setSwipingInProgress(false);
@@ -901,11 +909,13 @@
         return closestChild;
     }
 
+    @Override
     public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
         getLocationOnScreen(mTempInt2);
         return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
     }
 
+    @Override
     public ExpandableView getChildAtPosition(float touchX, float touchY) {
         // find the view under the pointer, accounting for GONE views
         final int count = getChildCount();
@@ -942,12 +952,14 @@
         return null;
     }
 
+    @Override
     public boolean canChildBeExpanded(View v) {
         return v instanceof ExpandableNotificationRow
                 && ((ExpandableNotificationRow) v).isExpandable()
                 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
     }
 
+    @Override
     public void setUserExpandedChild(View v, boolean userExpanded) {
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserExpanded(userExpanded,
@@ -955,6 +967,14 @@
         }
     }
 
+    @Override
+    public void setExpansionCancelled(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
+        }
+    }
+
+    @Override
     public void setUserLockedChild(View v, boolean userLocked) {
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserLocked(userLocked);
@@ -1065,6 +1085,7 @@
         return mScrollingEnabled;
     }
 
+    @Override
     public boolean canChildBeDismissed(View v) {
         return StackScrollAlgorithm.canChildBeDismissed(v);
     }
@@ -1726,6 +1747,15 @@
             }
         }
         mContentHeight = height + mTopPadding;
+        updateScrollability();
+    }
+
+    private void updateScrollability() {
+        boolean scrollable = getScrollRange() > 0;
+        if (scrollable != mScrollable) {
+            mScrollable = scrollable;
+            setFocusable(scrollable);
+        }
     }
 
     private void updateBackground() {
@@ -1871,7 +1901,7 @@
     }
 
     private void applyCurrentBackgroundBounds() {
-        if (!mFadedOut) {
+        if (!mFadingOut) {
             mScrimController.setExcludedBackgroundArea(mCurrentBounds);
         }
         invalidate();
@@ -1913,7 +1943,8 @@
                 // we're ending up at the same location as we are now, lets just skip the animation
                 bottom = finalBottom;
             } else {
-                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
+                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+                        - lastView.getExtraBottomPadding());
                 bottom = Math.min(bottom, getHeight());
             }
         } else {
@@ -2927,16 +2958,6 @@
         if (changed) {
             if (!mIsExpanded) {
                 mGroupManager.collapseAllGroups();
-            } else {
-                // XXX: HACK: we should not be clearing notification effects from way down here.
-                // But at the moment we don't have a reliable way to know when the window is
-                // actually exposed to the air, so
-                if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
-                    if (DEBUG) {
-                        Log.v(TAG, "clearing notification effects from scroller");
-                    }
-                    mPhoneStatusBar.clearNotificationEffects();
-                }
             }
             updateNotificationAnimationStates();
             updateChronometers();
@@ -3139,8 +3160,9 @@
                 expandableView.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
             } else {
                 float yLocation = previous.getTranslationY() + previous.getActualHeight() -
-                        expandableView.getTranslationY();
-                expandableView.setFakeShadowIntensity(diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
+                        expandableView.getTranslationY() - previous.getExtraBottomPadding();
+                expandableView.setFakeShadowIntensity(
+                        diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
                         previous.getOutlineAlpha(), (int) yLocation,
                         previous.getOutlineTranslation());
             }
@@ -3511,6 +3533,12 @@
         if (!mGroupExpandedForMeasure) {
             onHeightChanged(changedRow, false /* needsAnimation */);
         }
+        runAfterAnimationFinished(new Runnable() {
+            @Override
+            public void run() {
+                changedRow.onFinishedExpansionChange();
+            }
+        });
     }
 
     @Override
@@ -3518,6 +3546,68 @@
         mPhoneStatusBar.requestNotificationUpdate();
     }
 
+    /** @hide */
+    @Override
+    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEventInternal(event);
+        event.setScrollable(mScrollable);
+        event.setScrollX(mScrollX);
+        event.setScrollY(mOwnScrollY);
+        event.setMaxScrollX(mScrollX);
+        event.setMaxScrollY(getScrollRange());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        final int scrollRange = getScrollRange();
+        if (scrollRange > 0) {
+            info.setScrollable(true);
+            if (mScrollY > 0) {
+                info.addAction(
+                        AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
+            }
+            if (mScrollY < scrollRange) {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
+            }
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        int direction = -1;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollDown:
+                direction = 1;
+                // fall through
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollUp:
+                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+                        - mBottomStackPeekSize - mBottomStackSlowDownHeight;
+                final int targetScrollY = Math.max(0,
+                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+                if (targetScrollY != mOwnScrollY) {
+                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+                    postInvalidateOnAnimation();
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
     @Override
     public void onGroupsChanged() {
         mPhoneStatusBar.requestNotificationUpdate();
@@ -3610,22 +3700,33 @@
         updateNotificationAnimationStates();
     }
 
-    public void setFadedOut(boolean fadingOut) {
-        if (fadingOut != mFadedOut) {
-            mFadedOut = fadingOut;
-            if (fadingOut) {
-                mScrimController.setExcludedBackgroundArea(null);
-            } else {
-                applyCurrentBackgroundBounds();
-            }
-            updateSrcDrawing();
+    public void setFadingOut(boolean fadingOut) {
+        if (fadingOut != mFadingOut) {
+            mFadingOut = fadingOut;
+            updateFadingState();
         }
     }
 
+    public void setParentFadingOut(boolean fadingOut) {
+        if (fadingOut != mParentFadingOut) {
+            mParentFadingOut = fadingOut;
+            updateFadingState();
+        }
+    }
+
+    private void updateFadingState() {
+        if (mFadingOut || mParentFadingOut || mAmbientState.isDark()) {
+            mScrimController.setExcludedBackgroundArea(null);
+        } else {
+            applyCurrentBackgroundBounds();
+        }
+        updateSrcDrawing();
+    }
+
     @Override
     public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
         super.setAlpha(alpha);
-        setFadedOut(alpha != 1.0f);
+        setFadingOut(alpha != 1.0f);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 1ccc908..d6c5506 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -173,6 +173,9 @@
         }
         if (view instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (state.isBottomClipped) {
+                row.setClipToActualHeight(true);
+            }
             row.applyChildrenState(this);
         }
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 6f451ff..0f94227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -223,7 +223,7 @@
         startViewAnimations(child, viewState, delay, duration);
 
         // start height animation
-        if (heightChanging && child.getActualHeight() != 0) {
+        if (heightChanging) {
             startHeightAnimation(child, viewState, duration, delay);
         }
 
@@ -270,7 +270,8 @@
     public void startViewAnimations(View child, ViewState viewState, long delay, long duration) {
         boolean wasVisible = child.getVisibility() == View.VISIBLE;
         final float alpha = viewState.alpha;
-        if (!wasVisible && alpha != 0 && !viewState.gone && !viewState.hidden) {
+        if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
+                && !viewState.gone && !viewState.hidden) {
             child.setVisibility(View.VISIBLE);
         }
         boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
@@ -477,11 +478,27 @@
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
         animator.addListener(new AnimatorListenerAdapter() {
+            boolean mWasCancelled;
+
             @Override
             public void onAnimationEnd(Animator animation) {
                 child.setTag(TAG_ANIMATOR_HEIGHT, null);
                 child.setTag(TAG_START_HEIGHT, null);
                 child.setTag(TAG_END_HEIGHT, null);
+                if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
+                    ((ExpandableNotificationRow) child).setGroupExpansionChanging(
+                            false /* isExpansionChanging */);
+                }
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
             }
         });
         startAnimator(animator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
index 1114e03..ecdee4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
@@ -57,6 +57,11 @@
      */
     public int location;
 
+    /**
+     * Whether a child in a group is being clipped at the bottom.
+     */
+    public boolean isBottomClipped;
+
     @Override
     public void copyFrom(ViewState viewState) {
         super.copyFrom(viewState);
@@ -71,6 +76,7 @@
             clipTopAmount = svs.clipTopAmount;
             notGoneIndex = svs.notGoneIndex;
             location = svs.location;
+            isBottomClipped = svs.isBottomClipped;
         }
     }
 }
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 74f0cd3..476598d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -227,6 +227,13 @@
     }
 
     /**
+     * Updates the PIP per configuration changed.
+     */
+    void onConfigurationChanged() {
+        mPipRecentsOverlayManager.onConfigurationChanged(mContext);
+    }
+
+    /**
      * Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
      */
     public void showTvPictureInPictureMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index fe5d8bc..6e4a593 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -42,9 +42,9 @@
 
     private final PipManager mPipManager = PipManager.getInstance();
     private final WindowManager mWindowManager;
-    private final View mOverlayView;
-    private final PipRecentsControlsView mPipControlsView;
-    private final View mRecentsView;
+    private View mOverlayView;
+    private PipRecentsControlsView mPipControlsView;
+    private View mRecentsView;
 
     private final LayoutParams mPipRecentsControlsViewLayoutParams;
     private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
@@ -73,6 +73,21 @@
     PipRecentsOverlayManager(Context context) {
         mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
 
+        mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                LayoutParams.TYPE_SYSTEM_DIALOG,
+                LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
+                PixelFormat.TRANSLUCENT);
+        mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                LayoutParams.TYPE_SYSTEM_DIALOG,
+                0,
+                PixelFormat.TRANSLUCENT);
+
+        initViews(context);
+    }
+
+    private void initViews(Context context) {
         LayoutInflater inflater = (LayoutInflater) context
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
@@ -86,17 +101,6 @@
                 }
             }
         });
-
-        mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                LayoutParams.TYPE_SYSTEM_DIALOG,
-                LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
-                PixelFormat.TRANSLUCENT);
-        mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                LayoutParams.TYPE_SYSTEM_DIALOG,
-                0,
-                PixelFormat.TRANSLUCENT);
     }
 
     /**
@@ -210,4 +214,14 @@
     boolean isRecentsShown() {
         return mIsRecentsShown;
     }
+
+    /**
+     * Updates the PIP per configuration changed.
+     */
+    void onConfigurationChanged(Context context) {
+        if (mIsRecentsShown) {
+            Log.w(TAG, "Configuration is changed while Recents is shown");
+        }
+        initViews(context);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
index 182b9b0..b3e9f43 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
@@ -48,6 +48,6 @@
         if (!mSupportPip) {
             return;
         }
-        // TODO: handle configuration change.
+        PipManager.getInstance().onConfigurationChanged();
     }
 }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index 3f95427..de2c58c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -44,4 +44,19 @@
             mImpl.setSafeMode(isSafeMode());
         }
     }
+
+    @Override
+    public void onUnlockUser(int userHandle) {
+        mImpl.onUserUnlocked(userHandle);
+    }
+
+    @Override
+    public void onStopUser(int userHandle) {
+        mImpl.onUserStopped(userHandle);
+    }
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        mImpl.reloadWidgetsMaskedStateForGroup(userHandle);
+    }
 }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index ac2dda3..4e0ddd6 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -158,12 +158,6 @@
 
             if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 onConfigurationChanged();
-            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                onUserUnlocked(userId);
-            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                onUserStopped(userId);
-            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                reloadWidgetsMaskedStateForGroup(userId);
             } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
                     || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
                 synchronized (mLock) {
@@ -281,13 +275,6 @@
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                 sdFilter, null, null);
 
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-        userFilter.addAction(Intent.ACTION_USER_STOPPED);
-        userFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                userFilter, null, null);
-
         IntentFilter offModeFilter = new IntentFilter();
         offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
@@ -454,7 +441,7 @@
      * due to user not being available and package suspension.
      * userId must be the group parent.
      */
-    private void reloadWidgetsMaskedStateForGroup(int userId) {
+    void reloadWidgetsMaskedStateForGroup(int userId) {
         if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
             return;
         }
@@ -2525,7 +2512,7 @@
         }
     }
 
-    private void onUserUnlocked(int userId) {
+    void onUserUnlocked(int userId) {
         if (isProfileWithLockedParent(userId)) {
             return;
         }
@@ -3072,7 +3059,7 @@
         return new AtomicFile(settingsFile);
     }
 
-    private void onUserStopped(int userId) {
+    void onUserStopped(int userId) {
         synchronized (mLock) {
             boolean crossProfileWidgetsChanged = false;
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 1edc9b1..8ad6e6a 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -125,6 +125,8 @@
     long mNativeData;
     private long mNextWakeup;
     private long mNextNonWakeup;
+    private long mLastWakeupSet;
+    private long mLastWakeup;
     int mBroadcastRefCount = 0;
     PowerManager.WakeLock mWakeLock;
     boolean mLastWakeLockUnimportantForLogging;
@@ -1400,6 +1402,9 @@
                     pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
             pw.print("  Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
                     pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
+            pw.print("  Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
+            pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
+            pw.println();
             pw.print("  Num time change events: "); pw.println(mNumTimeChanged);
             pw.println("  mDeviceIdleUserWhitelist=" + Arrays.toString(mDeviceIdleUserWhitelist));
 
@@ -1838,6 +1843,7 @@
             final Batch firstBatch = mAlarmBatches.get(0);
             if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
                 mNextWakeup = firstWakeup.start;
+                mLastWakeupSet = SystemClock.elapsedRealtime();
                 setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
             }
             if (firstBatch != firstWakeup) {
@@ -2436,6 +2442,7 @@
             while (true)
             {
                 int result = waitForAlarm(mNativeData);
+                mLastWakeup = SystemClock.elapsedRealtime();
 
                 triggerList.clear();
 
@@ -2536,6 +2543,11 @@
                             deliverAlarmsLocked(triggerList, nowELAPSED);
                         }
                     }
+
+                } else {
+                    // Just in case -- even though no wakeup flag was set, make sure
+                    // we have updated the kernel to the next alarm time.
+                    rescheduleKernelAlarmsLocked();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ca3c39f..dab7d70 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1316,13 +1316,14 @@
             // For each client, check that the given op is not restricted, or that the given
             // package is exempt from the restriction.
             ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
-            if (restrictionState.hasRestriction(code, packageName, userHandle)
-                    && AppOpsManager.opAllowSystemBypassRestriction(code)) {
-                // If we are the system, bypass user restrictions for certain codes
-                synchronized (this) {
-                    Ops ops = getOpsRawLocked(uid, packageName, true);
-                    if ((ops != null) && ops.isPrivileged) {
-                        return false;
+            if (restrictionState.hasRestriction(code, packageName, userHandle)) {
+                if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+                    // If we are the system, bypass user restrictions for certain codes
+                    synchronized (this) {
+                        Ops ops = getOpsRawLocked(uid, packageName, true);
+                        if ((ops != null) && ops.isPrivileged) {
+                            return false;
+                        }
                     }
                 }
                 return true;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 5e8687a..f30a126 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -52,6 +52,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -126,6 +127,8 @@
     private IBinder mBluetoothBinder;
     private IBluetooth mBluetooth;
     private IBluetoothGatt mBluetoothGatt;
+    private final ReentrantReadWriteLock mBluetoothLock =
+        new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
     // used inside handler thread
@@ -190,12 +193,15 @@
                     }
 
                     int st = BluetoothAdapter.STATE_OFF;
-                    if (mBluetooth != null) {
-                        try {
+                    try {
+                        mBluetoothLock.readLock().lock();
+                        if (mBluetooth != null) {
                             st = mBluetooth.getState();
-                        } catch (RemoteException e) {
-                            Slog.e(TAG,"Unable to call getState", e);
                         }
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to call getState", e);
+                    } finally {
+                        mBluetoothLock.readLock().unlock();
                     }
                     Slog.d(TAG, "state" + st);
 
@@ -208,12 +214,15 @@
                         if (st == BluetoothAdapter.STATE_BLE_ON) {
                             //if state is BLE_ON make sure you trigger disableBLE part
                             try {
+                                mBluetoothLock.readLock().lock();
                                 if (mBluetooth != null) {
                                     mBluetooth.onBrEdrDown();
                                     mEnableExternal = false;
                                 }
-                            } catch(RemoteException e) {
+                            } catch (RemoteException e) {
                                 Slog.e(TAG,"Unable to call onBrEdrDown", e);
+                            } finally {
+                                mBluetoothLock.readLock().lock();
                             }
                         } else if (st == BluetoothAdapter.STATE_ON){
                             // disable without persisting the setting
@@ -366,9 +375,8 @@
         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
         msg.obj = callback;
         mHandler.sendMessage(msg);
-        synchronized(mConnection) {
-            return mBluetooth;
-        }
+
+        return mBluetooth;
     }
 
     public void unregisterAdapter(IBluetoothManagerCallback callback) {
@@ -406,12 +414,13 @@
             return false;
         }
 
-        synchronized(mConnection) {
-            try {
-                return (mBluetooth != null && mBluetooth.isEnabled());
-            } catch (RemoteException e) {
-                Slog.e(TAG, "isEnabled()", e);
-            }
+        try {
+            mBluetoothLock.readLock().lock();
+            if (mBluetooth != null) return mBluetooth.isEnabled();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "isEnabled()", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
         return false;
     }
@@ -424,11 +433,14 @@
             if (mBleAppCount == 0) {
                 if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
                 try {
+                    mBluetoothLock.readLock().lock();
                     if (mBluetooth != null) {
                         mBluetooth.onBrEdrDown();
                     }
-                } catch(RemoteException e) {
+                } catch (RemoteException e) {
                      Slog.e(TAG,"Unable to call onBrEdrDown", e);
+                } finally {
+                    mBluetoothLock.readLock().unlock();
                 }
             }
         }
@@ -456,9 +468,12 @@
                     disableBleScanMode();
                     clearBleApps();
                     try {
+                        mBluetoothLock.readLock().lock();
                         if (mBluetooth != null) mBluetooth.onBrEdrDown();
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error when disabling bluetooth", e);
+                    } finally {
+                        mBluetoothLock.readLock().unlock();
                     }
                 }
             }
@@ -472,12 +487,15 @@
     // Disable ble scan only mode.
     private void disableBleScanMode() {
         try {
+            mBluetoothLock.writeLock().lock();
             if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
                 if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable");
                 mEnable = false;
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "getState()", e);
+        } finally {
+            mBluetoothLock.writeLock().unlock();
         }
     }
 
@@ -536,7 +554,8 @@
      */
     private void onBluetoothGattServiceUp() {
         if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up");
-        try{
+        try {
+            mBluetoothLock.readLock().lock();
             if (isBleAppPresent() == false && mBluetooth != null
                   && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
                 mBluetooth.onLeServiceUp();
@@ -546,8 +565,10 @@
                 persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
                 Binder.restoreCallingIdentity(callingIdentity);
             }
-        } catch(RemoteException e) {
-                Slog.e(TAG,"Unable to call onServiceUp", e);
+        } catch (RemoteException e) {
+            Slog.e(TAG,"Unable to call onServiceUp", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
     }
 
@@ -558,22 +579,25 @@
     private void sendBrEdrDownCallback() {
         if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks");
 
-        if(mBluetooth == null) {
+        if (mBluetooth == null) {
             Slog.w(TAG, "Bluetooth handle is null");
             return;
         }
 
         if (isBleAppPresent() == false) {
             try {
-                mBluetooth.onBrEdrDown();
-            } catch(RemoteException e) {
+                mBluetoothLock.readLock().lock();
+                if (mBluetooth != null) mBluetooth.onBrEdrDown();
+            } catch (RemoteException e) {
                 Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
+            } finally {
+                mBluetoothLock.readLock().unlock();
             }
         } else {
             // Need to stay at BLE ON. Disconnect all Gatt connections
-            try{
+            try {
                 mBluetoothGatt.unregAll();
-            } catch(RemoteException e) {
+            } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to disconnect all apps.", e);
             }
         }
@@ -673,7 +697,8 @@
                 " mBinding = " + mBinding);
         }
 
-        synchronized (mConnection) {
+        try {
+            mBluetoothLock.writeLock().lock();
             if (mUnbinding) return;
             mUnbinding = true;
             if (mBluetooth != null) {
@@ -695,6 +720,8 @@
                 mUnbinding=false;
             }
             mBluetoothGatt = null;
+        } finally {
+            mBluetoothLock.writeLock().unlock();
         }
     }
 
@@ -1010,14 +1037,13 @@
             return BluetoothAdapter.DEFAULT_MAC_ADDRESS;
         }
 
-        synchronized(mConnection) {
-            if (mBluetooth != null) {
-                try {
-                    return mBluetooth.getAddress();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
-                }
-            }
+        try {
+            mBluetoothLock.readLock().lock();
+            if (mBluetooth != null) return mBluetooth.getAddress();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "getAddress(): Unable to retrieve address remotely. Returning cached address", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
 
         // mAddress is accessed from outside.
@@ -1036,15 +1062,15 @@
             return null;
         }
 
-        synchronized(mConnection) {
-            if (mBluetooth != null) {
-                try {
-                    return mBluetooth.getName();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
-                }
-            }
+        try {
+            mBluetoothLock.readLock().lock();
+            if (mBluetooth != null) return mBluetooth.getName();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
+
         // mName is accessed from outside.
         // It alright without a lock. Here, bluetooth is off, no other thread is
         // changing mName
@@ -1101,7 +1127,8 @@
             switch (msg.what) {
                 case MESSAGE_GET_NAME_AND_ADDRESS:
                     if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
-                    synchronized(mConnection) {
+                    try {
+                        mBluetoothLock.writeLock().lock();
                         if ((mBluetooth == null) && (!mBinding)) {
                             if (DBG) Slog.d(TAG, "Binding to service to get name and address");
                             mGetNameAddressOnly = true;
@@ -1127,6 +1154,8 @@
                             }
                             mGetNameAddressOnly = false;
                         }
+                    } finally {
+                        mBluetoothLock.writeLock().unlock();
                     }
                     break;
 
@@ -1209,7 +1238,8 @@
                     if (DBG) Slog.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
 
                     IBinder service = (IBinder) msg.obj;
-                    synchronized(mConnection) {
+                    try {
+                        mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
                             onBluetoothGattServiceUp();
@@ -1264,6 +1294,8 @@
                         } catch (RemoteException e) {
                             Slog.e(TAG,"Unable to call enable()",e);
                         }
+                    } finally {
+                        mBluetoothLock.writeLock().unlock();
                     }
 
                     if (!mEnable) {
@@ -1275,9 +1307,10 @@
                 }
                 case MESSAGE_TIMEOUT_BIND: {
                     Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
-                    synchronized(mConnection) {
-                        mBinding = false;
-                    }
+                    mBluetoothLock.writeLock().lock();
+                    mBinding = false;
+                    mBluetoothLock.writeLock().unlock();
+
                     break;
                 }
                 case MESSAGE_BLUETOOTH_STATE_CHANGE:
@@ -1312,7 +1345,8 @@
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
                     Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
-                    synchronized(mConnection) {
+                    try {
+                        mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTH) {
                             // if service is unbinded already, do nothing and return
                             if (mBluetooth == null) break;
@@ -1324,6 +1358,8 @@
                             Slog.e(TAG, "Bad msg.arg1: " + msg.arg1);
                             break;
                         }
+                    } finally {
+                        mBluetoothLock.writeLock().unlock();
                     }
 
                     if (mEnable) {
@@ -1369,9 +1405,9 @@
                 case MESSAGE_TIMEOUT_UNBIND:
                 {
                     Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
-                    synchronized(mConnection) {
-                        mUnbinding = false;
-                    }
+                    mBluetoothLock.writeLock().lock();
+                    mUnbinding = false;
+                    mBluetoothLock.writeLock().unlock();
                     break;
                 }
 
@@ -1381,15 +1417,15 @@
 
                     /* disable and enable BT when detect a user switch */
                     if (mEnable && mBluetooth != null) {
-                        synchronized (mConnection) {
+                        try {
+                            mBluetoothLock.readLock().lock();
                             if (mBluetooth != null) {
-                                //Unregister callback object
-                                try {
-                                    mBluetooth.unregisterCallback(mBluetoothCallback);
-                                } catch (RemoteException re) {
-                                    Slog.e(TAG, "Unable to unregister",re);
-                                }
+                                mBluetooth.unregisterCallback(mBluetoothCallback);
                             }
+                        } catch (RemoteException re) {
+                            Slog.e(TAG, "Unable to unregister", re);
+                        } finally {
+                            mBluetoothLock.readLock().unlock();
                         }
 
                         if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
@@ -1420,14 +1456,16 @@
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
                                                     BluetoothAdapter.STATE_OFF);
                         sendBluetoothServiceDownCallback();
-                        synchronized (mConnection) {
-                            if (mBluetooth != null) {
-                                mBluetooth = null;
-                                //Unbind
-                                mContext.unbindService(mConnection);
-                            }
-                            mBluetoothGatt = null;
+
+                        mBluetoothLock.writeLock().lock();
+                        if (mBluetooth != null) {
+                            mBluetooth = null;
+                            // Unbind
+                            mContext.unbindService(mConnection);
                         }
+                        mBluetoothGatt = null;
+                        mBluetoothLock.writeLock().unlock();
+
                         SystemClock.sleep(100);
 
                         mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
@@ -1450,14 +1488,12 @@
                     if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
                     mHandler.removeMessages(MESSAGE_USER_SWITCHED);
 
-                    synchronized (mConnection) {
-                        if (mEnable && !mBinding && (mBluetooth == null)) {
-                            // We should be connected, but we gave up for some
-                            // reason; maybe the Bluetooth service wasn't encryption
-                            // aware, so try binding again.
-                            if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
-                            handleEnable(mQuietEnable);
-                        }
+                    if (mEnable && !mBinding && (mBluetooth == null)) {
+                        // We should be connected, but we gave up for some
+                        // reason; maybe the Bluetooth service wasn't encryption
+                        // aware, so try binding again.
+                        if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+                        handleEnable(mQuietEnable);
                     }
                 }
             }
@@ -1467,7 +1503,8 @@
     private void handleEnable(boolean quietMode) {
         mQuietEnable = quietMode;
 
-        synchronized(mConnection) {
+        try {
+            mBluetoothLock.writeLock().lock();
             if ((mBluetooth == null) && (!mBinding)) {
                 //Start bind timeout and bind
                 Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
@@ -1496,6 +1533,8 @@
                     Slog.e(TAG,"Unable to call enable()",e);
                 }
             }
+        } finally {
+            mBluetoothLock.writeLock().unlock();
         }
     }
 
@@ -1510,18 +1549,18 @@
     }
 
     private void handleDisable() {
-        synchronized(mConnection) {
+        try {
+            mBluetoothLock.readLock().lock();
             if (mBluetooth != null) {
                 if (DBG) Slog.d(TAG,"Sending off request.");
-
-                try {
-                    if(!mBluetooth.disable()) {
-                        Slog.e(TAG,"IBluetooth.disable() returned false");
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG,"Unable to call disable()",e);
+                if (!mBluetooth.disable()) {
+                    Slog.e(TAG,"IBluetooth.disable() returned false");
                 }
             }
+        } catch (RemoteException e) {
+            Slog.e(TAG,"Unable to call disable()",e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
     }
 
@@ -1648,20 +1687,21 @@
     private boolean waitForOnOff(boolean on, boolean off) {
         int i = 0;
         while (i < 10) {
-            synchronized(mConnection) {
-                try {
-                    if (mBluetooth == null) break;
-                    if (on) {
-                        if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
-                    } else if (off) {
-                        if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
-                    } else {
-                        if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "getState()", e);
-                    break;
+            try {
+                mBluetoothLock.readLock().lock();
+                if (mBluetooth == null) break;
+                if (on) {
+                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+                } else if (off) {
+                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+                } else {
+                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
                 }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "getState()", e);
+                break;
+            } finally {
+                mBluetoothLock.readLock().unlock();
             }
             if (on || off) {
                 SystemClock.sleep(300);
@@ -1684,34 +1724,36 @@
     }
 
     private boolean canUnbindBluetoothService() {
-        synchronized(mConnection) {
+        try {
             //Only unbind with mEnable flag not set
             //For race condition: disable and enable back-to-back
             //Avoid unbind right after enable due to callback from disable
             //Only unbind with Bluetooth at OFF state
             //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message
-            try {
-                if (mEnable || (mBluetooth == null)) return false;
-                if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false;
-                return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "getState()", e);
-            }
+            mBluetoothLock.readLock().lock();
+            if (mEnable || (mBluetooth == null)) return false;
+            if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false;
+            return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "getState()", e);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
         return false;
     }
 
     private void recoverBluetoothServiceFromError() {
         Slog.e(TAG,"recoverBluetoothServiceFromError");
-        synchronized (mConnection) {
+        try {
+            mBluetoothLock.readLock().lock();
             if (mBluetooth != null) {
                 //Unregister callback object
-                try {
-                    mBluetooth.unregisterCallback(mBluetoothCallback);
-                } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to unregister",re);
-                }
+                mBluetooth.unregisterCallback(mBluetoothCallback);
             }
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to unregister", re);
+        } finally {
+            mBluetoothLock.readLock().unlock();
         }
 
         SystemClock.sleep(500);
@@ -1722,14 +1764,15 @@
         waitForOnOff(false, true);
 
         sendBluetoothServiceDownCallback();
-        synchronized (mConnection) {
-            if (mBluetooth != null) {
-                mBluetooth = null;
-                //Unbind
-                mContext.unbindService(mConnection);
-            }
-            mBluetoothGatt = null;
+
+        mBluetoothLock.writeLock().lock();
+        if (mBluetooth != null) {
+            mBluetooth = null;
+            // Unbind
+            mContext.unbindService(mConnection);
         }
+        mBluetoothGatt = null;
+        mBluetoothLock.writeLock().unlock();
 
         mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
         mState = BluetoothAdapter.STATE_OFF;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c096fa5f..95a9781 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4066,7 +4066,8 @@
             synchronized(mRulesLock) {
                 uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
             }
-            if ((uidRules & RULE_ALLOW_ALL) == 0) {
+            if (mRestrictBackground && (uidRules & RULE_ALLOW_METERED) == 0
+                    && (uidRules & RULE_TEMPORARY_ALLOW_METERED) == 0) {
                 // we could silently fail or we can filter the available nets to only give
                 // them those they have access to.  Chose the more useful option.
                 networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3194d89..2017e48 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -12822,12 +12823,18 @@
                     com.android.internal.R.dimen.thumbnail_width);
             mThumbnailHeight = res.getDimensionPixelSize(
                     com.android.internal.R.dimen.thumbnail_height);
-            mFullscreenThumbnailScale = res.getFraction(
-                    com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
             mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
                     com.android.internal.R.string.config_defaultPictureInPictureBounds));
             mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
                     com.android.internal.R.string.config_appsNotReportingCrashes));
+            if ((mConfiguration.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+                mFullscreenThumbnailScale = (float) res
+                    .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
+                    (float) mConfiguration.screenWidthDp;
+            } else {
+                mFullscreenThumbnailScale = res.getFraction(
+                    com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+            }
         }
     }
 
@@ -16856,6 +16863,13 @@
                 return false;
             }
 
+            // If the app is a regular app (uid >= 10000) and not the system server or phone
+            // process, etc, then mark it as being in full backup so that certain calls to the
+            // process can be blocked. This is not reset to false anywhere because we kill the
+            // process after the full backup is done and the ProcessRecord will vaporize anyway.
+            if (UserHandle.isApp(app.uid) && backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+                proc.inFullBackup = true;
+            }
             r.app = proc;
             mBackupTarget = r;
             mBackupAppName = app.packageName;
@@ -18318,21 +18332,21 @@
                 EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
 
                 if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
-                    final Locale locale;
-                    if (values.getLocales().size() == 1) {
-                        // This is an optimization to avoid the JNI call when the result of
-                        // getFirstMatch() does not depend on the supported locales.
-                        locale = values.getLocales().get(0);
-                    } else {
+                    final LocaleList locales = values.getLocales();
+                    int bestLocaleIndex = 0;
+                    if (locales.size() > 1) {
                         if (mSupportedSystemLocales == null) {
                             mSupportedSystemLocales =
                                     Resources.getSystem().getAssets().getLocales();
                         }
-                        locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
+                        bestLocaleIndex = Math.max(0,
+                                locales.getFirstMatchIndex(mSupportedSystemLocales));
                     }
-                    SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
+                    SystemProperties.set("persist.sys.locale",
+                            locales.get(bestLocaleIndex).toLanguageTag());
+                    LocaleList.setDefault(locales, bestLocaleIndex);
                     mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
-                            locale));
+                            locales.get(bestLocaleIndex)));
                 }
 
                 mConfigurationSeq++;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 3811b61..8b637e0 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1819,22 +1819,24 @@
     private boolean shouldBeVisible(ActivityRecord r, boolean behindTranslucentActivity,
             boolean stackVisibleBehind, ActivityRecord visibleBehind,
             boolean behindFullscreenActivity) {
-        // mLaunchingBehind: Activities launching behind are at the back of the task stack
-        // but must be drawn initially for the animation as though they were visible.
-        final boolean activityVisibleBehind =
-                (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == r;
 
         if (!okToShowLocked(r)) {
             return false;
         }
 
+        // mLaunchingBehind: Activities launching behind are at the back of the task stack
+        // but must be drawn initially for the animation as though they were visible.
+        final boolean activityVisibleBehind =
+                (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == r;
+
         boolean isVisible =
                 !behindFullscreenActivity || r.mLaunchTaskBehind || activityVisibleBehind;
 
         if (isVisible && r.isRecentsActivity()) {
-            // Recents activity can only be visible if the home stack isn't fullscreen or is the
-            // focused stack.
-            isVisible = !mFullscreen || mStackSupervisor.isFocusedStack(this);
+            // Recents activity can only be visible if the home stack is the focused stack or we are
+            // in split-screen mode.
+            isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null
+                    || mStackSupervisor.isFocusedStack(this);
         }
 
         return isVisible;
@@ -2256,6 +2258,20 @@
             }
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return true;
+        } else if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
+                mStackSupervisor.allResumedActivitiesComplete()) {
+            // It is possible for the activity to be resumed when we paused back stacks above if the
+            // next activity doesn't have to wait for pause to complete.
+            // So, nothing else to-do except:
+            // Make sure we have executed any pending transitions, since there
+            // should be nothing left to do at this point.
+            mWindowManager.executeAppTransition();
+            mNoAnimActivities.clear();
+            ActivityOptions.abort(options);
+            if (DEBUG_STATES) Slog.d(TAG_STATES,
+                    "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
+            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+            return true;
         }
 
         // If the most recent activity was noHistory but was only stopped rather
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0acc2a0..8ffc6f3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -258,6 +258,11 @@
         if (app.thread == null) {
             throw new RemoteException();
         }
+        if (app.inFullBackup) {
+            skipReceiverLocked(r);
+            return;
+        }
+
         r.receiver = app.thread.asBinder();
         r.curApp = app;
         app.curReceiver = r;
@@ -341,13 +346,17 @@
         }
 
         if (r != null) {
-            logBroadcastReceiverDiscardLocked(r);
-            finishReceiverLocked(r, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, false);
-            scheduleBroadcastsLocked();
+            skipReceiverLocked(r);
         }
     }
 
+    private void skipReceiverLocked(BroadcastRecord r) {
+        logBroadcastReceiverDiscardLocked(r);
+        finishReceiverLocked(r, r.resultCode, r.resultData,
+                r.resultExtras, r.resultAbort, false);
+        scheduleBroadcastsLocked();
+    }
+
     public void scheduleBroadcastsLocked() {
         if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                 + mQueueName + "]: current="
@@ -641,9 +650,17 @@
         try {
             if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                     "Delivering to " + filter + " : " + r);
-            performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
-                    new Intent(r.intent), r.resultCode, r.resultData,
-                    r.resultExtras, r.ordered, r.initialSticky, r.userId);
+            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
+                // Skip delivery if full backup in progress
+                // If it's an ordered broadcast, we need to continue to the next receiver.
+                if (ordered) {
+                    skipReceiverLocked(r);
+                }
+            } else {
+                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+                        new Intent(r.intent), r.resultCode, r.resultData,
+                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
+            }
             if (ordered) {
                 r.state = BroadcastRecord.CALL_DONE_RECEIVE;
             }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 059acbd..da18f32 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -192,6 +192,9 @@
     // app that installed the package.
     ComponentName errorReportReceiver;
 
+    // Process is currently hosting a backup agent for backup or restore
+    public boolean inFullBackup;
+
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0874fa7..7777ae23f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6342,7 +6342,7 @@
         mRecordMonitor.unregisterRecordingCallback(rcdb);
     }
 
-    public AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+    public List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
         return mRecordMonitor.getActiveRecordingConfigurations();
     }
 
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 7a085e1..57d55de 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -29,6 +29,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * Class to receive and dispatch updates from AudioSystem about recording configurations.
@@ -54,7 +55,7 @@
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             return;
         }
-        final AudioRecordingConfiguration[] configs =
+        final List<AudioRecordingConfiguration> configs =
                 updateSnapshot(event, session, source, recordingInfo);
         if (configs != null){
             synchronized(mClients) {
@@ -104,9 +105,9 @@
         }
     }
 
-    AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+    List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
         synchronized(mRecordConfigs) {
-            return mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
+            return new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
         }
     }
 
@@ -118,13 +119,13 @@
      * @param recordingFormat see
      *     {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
      *     for the definition of the contents of the array
-     * @return null if the list of active recording sessions has not been modified, an array
+     * @return null if the list of active recording sessions has not been modified, a list
      *     with the current active configurations otherwise.
      */
-    private AudioRecordingConfiguration[] updateSnapshot(int event, int session, int source,
+    private List<AudioRecordingConfiguration> updateSnapshot(int event, int session, int source,
             int[] recordingInfo) {
         final boolean configChanged;
-        final AudioRecordingConfiguration[] configs;
+        final ArrayList<AudioRecordingConfiguration> configs;
         synchronized(mRecordConfigs) {
             switch (event) {
             case AudioManager.RECORD_CONFIG_EVENT_STOP:
@@ -171,7 +172,7 @@
                 configChanged = false;
             }
             if (configChanged) {
-                configs = mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
+                configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
             } else {
                 configs = null;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 04be34a..946e4f7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2252,6 +2252,7 @@
                                         .setGroup(newAutoBundleKey)
                                         .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
                                         .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+                                        .setColor(adjustedSbn.getNotification().color)
                                         .build();
                         summaryNotification.extras.putAll(extras);
                         Intent appIntent = getContext().getPackageManager()
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 00d7a7b..7b573da 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -384,7 +384,7 @@
             noisyImportance = new ImportanceHistogram(context, "note_imp_noisy_");
             quietImportance = new ImportanceHistogram(context, "note_imp_quiet_");
             finalImportance = new ImportanceHistogram(context, "note_importance_");
-            enqueueRate = new RateEstimator(mCreated);
+            enqueueRate = new RateEstimator();
         }
 
         public AggregatedStats getPrevious() {
diff --git a/services/core/java/com/android/server/notification/RateEstimator.java b/services/core/java/com/android/server/notification/RateEstimator.java
index 4dc30a4..c17db4a 100644
--- a/services/core/java/com/android/server/notification/RateEstimator.java
+++ b/services/core/java/com/android/server/notification/RateEstimator.java
@@ -25,30 +25,43 @@
 public class RateEstimator {
     private static final double RATE_ALPHA = 0.8;
     private static final double MINIMUM_DT = 0.0005;
-    private long mLastEventTime;
-    private float mInterarrivalTime;
+    private Long mLastEventTime;
+    private Float mInterarrivalTime;
 
-    public RateEstimator(long now) {
-        mLastEventTime = now;
-    }
+    public RateEstimator() {}
 
-    /** Update the estimate to account for an event that jsut happened. */
+    /** Update the estimate to account for an event that just happened. */
     public float update(long now) {
-        mInterarrivalTime = (float) getInterarrivalEstimate(now);
+        float rate;
+        if (mLastEventTime == null) {
+            // No last event time, rate is zero.
+            rate = 0f;
+        } else {
+            // Calculate the new inter-arrival time based on last event time.
+            mInterarrivalTime = (float) getInterarrivalEstimate(now);
+            rate = (float) (1.0 / mInterarrivalTime);
+        }
         mLastEventTime = now;
-        return (float) (1.0 / mInterarrivalTime);
+        return rate;
     }
 
     /** @return the estimated rate if there were a new event right now. */
     public float getRate(long now) {
+        if (mLastEventTime == null) {
+            return 0f;
+        }
         return (float) (1.0 / getInterarrivalEstimate(now));
     }
 
     /** @return the average inter-arrival time if there were a new event right now. */
     private double getInterarrivalEstimate(long now) {
-        // a*iat_old + (1-a)*(t_now-t_last)
         double dt = ((double) (now - mLastEventTime)) / 1000.0;
         dt = Math.max(dt, MINIMUM_DT);
+        if (mInterarrivalTime == null) {
+            // No last inter-arrival time, return the new value directly.
+            return dt;
+        }
+        // a*iat_old + (1-a)*(t_now-t_last)
         return (RATE_ALPHA * mInterarrivalTime + (1.0 - RATE_ALPHA) * dt);
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 91de797..ecab009 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -217,7 +217,7 @@
             mSystemInterface = systemInterface;
         }
 
-        private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
+        private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
 
         // Keeps track of the number of running relro creations
         private int mNumRelroCreationsStarted = 0;
@@ -487,6 +487,10 @@
                     // Either the current relro creation  isn't done yet, or the new relro creatioin
                     // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
                     webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+                    Slog.e(TAG, "Timed out waiting for relro creation, relros started "
+                            + mNumRelroCreationsStarted
+                            + " relros finished " + mNumRelroCreationsFinished
+                            + " package dirty? " + mWebViewPackageDirty);
                 }
             }
             if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 82cd0fa..aaed8ca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2359,7 +2359,13 @@
                 // immediately after the enter animation is done. If the app is not yet drawn then
                 // it will show up as a flicker.
                 win.mRemoveOnExit = true;
-                win.mWindowRemovalAllowed = true;
+                // Request a focus update as this window's input channel is already gone. Otherwise
+                // we could have no focused window in input manager.
+                final boolean focusChanged = updateFocusedWindowLocked(
+                        UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
+                if (focusChanged) {
+                    mInputMonitor.updateInputWindowsLw(false /*force*/);
+                }
                 Binder.restoreCallingIdentity(origId);
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e5e74a6..c15afb3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1265,8 +1265,8 @@
      * @return true if the window should be considered while evaluating allDrawn flags.
      */
     boolean mightAffectAllDrawn(boolean visibleOnly) {
-        final boolean isViewVisible = (mViewVisibility == View.VISIBLE)
-                && (mAppToken == null || !mAppToken.clientHidden);
+        final boolean isViewVisible = (mAppToken == null || !mAppToken.clientHidden)
+                && (mViewVisibility == View.VISIBLE) && !mWindowRemovalAllowed;
         return (isOnScreenIgnoringKeyguard() && (!visibleOnly || isViewVisible)
                 || mWinAnimator.mAttrType == TYPE_BASE_APPLICATION)
                 && !mAnimatingExit && !mDestroying;
@@ -1822,7 +1822,7 @@
     /** @return true if this window desires key events. */
     boolean canReceiveKeys() {
         return isVisibleOrAdding()
-                && (mViewVisibility == View.VISIBLE)
+                && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                 && (mAppToken == null || mAppToken.windowsAreFocusable())
                 && !isAdjustedForMinimizedDock();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index aea395d..42eddd5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1334,7 +1334,17 @@
             final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
             if (wallpaperTarget != null) {
                 final Task task = wallpaperTarget.getTask();
-                if (task != null && !task.isFullscreen()) {
+                final WindowStateAnimator winAnimator = wallpaperTarget.mWinAnimator;
+                // We can only crop the wallpaper using final crop with stack bounds if the target
+                // is not animating, or if it's animating with clip mode STACK_CLIP_AFTER_ANIM.
+                // If it's animating with mode STACK_CLIP_NONE, we shouldn't crop either the task
+                // itself or the wallpaper. If it's animating with STACK_CLIP_BEFORE_ANIM, the crop
+                // is before the transform on the task itself.
+                final boolean useFinalCropOnWallpaper = !winAnimator.isAnimationSet()
+                        || winAnimator.resolveStackClip() == STACK_CLIP_AFTER_ANIM;
+                if (task != null && !task.isFullscreen()
+                        && task.cropWindowsToStackBounds()
+                        && useFinalCropOnWallpaper){
                     final TaskStack stack = task.mStack;
                     if (stack != null && !stack.isFullscreen()) {
                         stack.getDimBounds(mTmpStackBounds);
@@ -1459,7 +1469,7 @@
             // We need to ensure for each surface, that we disable transformation matrix
             // scaling in the same transaction which we resize the surface in.
             // As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
-            // then take over the scaling until the new buffer arrives, and things 
+            // then take over the scaling until the new buffer arrives, and things
             // will be seamless.
             mForceScaleUntilResize = true;
         } else {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index e29452a..e8d4c58 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -72,15 +72,18 @@
 #define GPS_MAX_SATELLITE_COUNT 32
 #define GNSS_MAX_SATELLITE_COUNT 64
 
-// Let these through, with ID remapped by offset
+// Let these through, with ID remapped down to 1, 2... by offset
 #define GLONASS_SVID_OFFSET 64
 #define GLONASS_SVID_COUNT 24
 #define BEIDOU_SVID_OFFSET 200
 #define BEIDOU_SVID_COUNT 35
 
+// Let these through, with ID remapped up (33->120 ... 64->151, etc.)
+#define SBAS_SVID_MIN 33
+#define SBAS_SVID_MAX 64
+#define SBAS_SVID_ADD 87
+
 // Let these through, with no ID remapping
-#define SBAS_SVID_MIN 120
-#define SBAS_SVID_MAX 151
 #define QZSS_SVID_MIN 193
 #define QZSS_SVID_MAX 200
 
@@ -159,6 +162,7 @@
             info.svid -= BEIDOU_SVID_OFFSET;
         } else if (info.svid >= SBAS_SVID_MIN && info.svid <= SBAS_SVID_MAX) {
             info.constellation = GNSS_CONSTELLATION_SBAS;
+            info.svid += SBAS_SVID_ADD;
         } else if (info.svid >= QZSS_SVID_MIN && info.svid <= QZSS_SVID_MAX) {
             info.constellation = GNSS_CONSTELLATION_QZSS;
         } else {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a5eb0b2..c1913de 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -484,7 +484,8 @@
                     }
                 });
             }
-            if (Intent.ACTION_BOOT_COMPLETED.equals(action)
+            if (Intent.ACTION_USER_UNLOCKED.equals(action)
+                    || Intent.ACTION_USER_STARTED.equals(action)
                     || KeyChain.ACTION_STORAGE_CHANGED.equals(action)) {
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
                 new MonitoringCertNotificationTask().execute(userId);
@@ -1582,6 +1583,7 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7079b9e..538e8f8 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -648,14 +648,31 @@
         // Here's a basic summary of what the IPv6 filter program does:
         //
         // if it's not ICMPv6:
+        //   if it's multicast and we're dropping multicast:
+        //     drop
         //   pass
         // if it's ICMPv6 NA to ff02::1:
         //   drop
 
-        // If not ICMPv6, pass
         gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
-        // TODO: Drop multicast if the multicast filter is enabled.
-        gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+
+        // Drop multicast if the multicast filter is enabled.
+        if (mMulticastFilter) {
+            // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
+            String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
+            gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
+
+            // Drop all other packets sent to ff00::/8.
+            gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
+            gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
+            // Not multicast and not ICMPv6. Pass.
+            gen.addJump(gen.PASS_LABEL);
+            gen.defineLabel(skipIpv6MulticastFilterLabel);
+        } else {
+            // If not ICMPv6, pass.
+            gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+        }
+
         // Add unsolicited multicast neighbor announcements filter
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
         // If not neighbor announcements, skip unsolicited multicast NA filter
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 505d15a..99b10c3 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -125,17 +125,18 @@
     public static final int CMD_CONFIGURE_LINKADDRESS       = PUBLIC_BASE + 8;
     public static final int EVENT_LINKADDRESS_CONFIGURED    = PUBLIC_BASE + 9;
 
-    /* Message.arg1 arguments to CMD_POST_DHCP notification */
+    /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
     public static final int DHCP_SUCCESS = 1;
     public static final int DHCP_FAILURE = 2;
 
-    // Messages.
+    // Internal messages.
     private static final int PRIVATE_BASE         = Protocol.BASE_DHCP + 100;
     private static final int CMD_KICK             = PRIVATE_BASE + 1;
     private static final int CMD_RECEIVED_PACKET  = PRIVATE_BASE + 2;
     private static final int CMD_TIMEOUT          = PRIVATE_BASE + 3;
     private static final int CMD_RENEW_DHCP       = PRIVATE_BASE + 4;
-    private static final int CMD_EXPIRE_DHCP      = PRIVATE_BASE + 5;
+    private static final int CMD_REBIND_DHCP      = PRIVATE_BASE + 5;
+    private static final int CMD_EXPIRE_DHCP      = PRIVATE_BASE + 6;
 
     // For message logging.
     private static final Class[] sMessageClasses = { DhcpClient.class };
@@ -177,6 +178,7 @@
     private final WakeupMessage mKickAlarm;
     private final WakeupMessage mTimeoutAlarm;
     private final WakeupMessage mRenewAlarm;
+    private final WakeupMessage mRebindAlarm;
     private final WakeupMessage mExpiryAlarm;
     private final String mIfaceName;
 
@@ -241,8 +243,9 @@
         mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
         // Used to time out PacketRetransmittingStates.
         mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
-        // Used to schedule DHCP renews.
+        // Used to schedule DHCP reacquisition.
         mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
+        mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
         mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
     }
 
@@ -276,6 +279,10 @@
     }
 
     private boolean initSockets() {
+        return initPacketSocket() && initUdpSocket();
+    }
+
+    private boolean initPacketSocket() {
         try {
             mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
             PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.getIndex());
@@ -285,6 +292,10 @@
             Log.e(TAG, "Error creating packet socket", e);
             return false;
         }
+        return true;
+    }
+
+    private boolean initUdpSocket() {
         try {
             mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
@@ -363,16 +374,25 @@
         return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
     }
 
-    private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
+    private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
         try {
-            if (to.equals(INADDR_BROADCAST)) {
+            if (encap == DhcpPacket.ENCAP_L2) {
                 if (DBG) Log.d(TAG, "Broadcasting " + description);
                 Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
+            } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
+                if (DBG) Log.d(TAG, "Broadcasting " + description);
+                // We only send L3-encapped broadcasts in DhcpRebindingState,
+                // where we have an IP address and an unconnected UDP socket.
+                //
+                // N.B.: We only need this codepath because DhcpRequestPacket
+                // hardcodes the source IP address to 0.0.0.0. We could reuse
+                // the packet socket if this ever changes.
+                Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
             } else {
                 // It's safe to call getpeername here, because we only send unicast packets if we
-                // have an IP address, and we connect the UDP socket before
-                // ConfiguringInterfaceState#exit.
-                if (DBG) Log.d(TAG, "Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+                // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
+                if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
+                        description, Os.getpeername(mUdpSock)));
                 Os.write(mUdpSock, buf);
             }
         } catch(ErrnoException|IOException e) {
@@ -386,14 +406,15 @@
         ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
                 DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
                 DO_UNICAST, REQUESTED_PARAMS);
-        return transmitPacket(packet, "DHCPDISCOVER", INADDR_BROADCAST);
+        return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
     }
 
     private boolean sendRequestPacket(
             Inet4Address clientAddress, Inet4Address requestedAddress,
             Inet4Address serverAddress, Inet4Address to) {
         // TODO: should we use the transaction ID from the server?
-        int encap = to.equals(INADDR_BROADCAST) ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
+        final int encap = INADDR_ANY.equals(clientAddress)
+                ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
 
         ByteBuffer packet = DhcpPacket.buildRequestPacket(
                 encap, mTransactionId, getSecs(), clientAddress,
@@ -403,7 +424,7 @@
         String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
                              " request=" + requestedAddress.getHostAddress() +
                              " serverid=" + serverStr;
-        return transmitPacket(packet, description, to);
+        return transmitPacket(packet, description, encap, to);
     }
 
     private void scheduleLeaseTimers() {
@@ -413,14 +434,21 @@
         }
 
         final long now = SystemClock.elapsedRealtime();
-        long renewTime = (now + mDhcpLeaseExpiry) / 2;
-        mRenewAlarm.schedule(renewTime);
-        long secondsHence = (renewTime - now) / 1000;
-        Log.d(TAG, "Scheduling renewal in " + secondsHence + "s");
 
-        mExpiryAlarm.schedule(mDhcpLeaseExpiry);
-        secondsHence = (mDhcpLeaseExpiry - now) / 1000;
-        Log.d(TAG, "Scheduling expiry in " + secondsHence + "s");
+        // TODO: consider getting the renew and rebind timers from T1 and T2.
+        // See also:
+        //     https://tools.ietf.org/html/rfc2131#section-4.4.5
+        //     https://tools.ietf.org/html/rfc1533#section-9.9
+        //     https://tools.ietf.org/html/rfc1533#section-9.10
+        final long remainingDelay = mDhcpLeaseExpiry - now;
+        final long renewDelay = remainingDelay / 2;
+        final long rebindDelay = remainingDelay * 7 / 8;
+        mRenewAlarm.schedule(now + renewDelay);
+        mRebindAlarm.schedule(now + rebindDelay);
+        mExpiryAlarm.schedule(now + remainingDelay);
+        Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
+        Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
+        Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
     }
 
     private void notifySuccess() {
@@ -719,7 +747,6 @@
 
     class DhcpRequestingState extends PacketRetransmittingState {
         public DhcpRequestingState() {
-            super();
             mTimeout = DHCP_TIMEOUT_MS / 2;
         }
 
@@ -777,7 +804,11 @@
 
         @Override
         public void exit() {
+            // Clear any extant alarms.
+            mRenewAlarm.cancel();
+            mRebindAlarm.cancel();
             mExpiryAlarm.cancel();
+            clearDhcpState();
             // Tell IpManager to clear the IPv4 address. There is no need to
             // wait for confirmation since any subsequent packets are sent from
             // INADDR_ANY anyway (DISCOVER, REQUEST).
@@ -797,21 +828,7 @@
             super.processMessage(message);
             switch (message.what) {
                 case EVENT_LINKADDRESS_CONFIGURED:
-                    if (mDhcpLease.serverAddress != null &&
-                            !connectUdpSock(mDhcpLease.serverAddress)) {
-                        // There's likely no point in going into DhcpInitState here, we'll probably
-                        // just repeat the transaction, get the same IP address as before, and fail.
-                        //
-                        // NOTE: It is observed that connectUdpSock() basically never fails, due to
-                        // SO_BINDTODEVICE. Examining the local socket address shows it will happily
-                        // return an IPv4 address from another interface, or even return "0.0.0.0".
-                        //
-                        // TODO: Consider deleting this check, following testing on several kernels.
-                        notifyFailure();
-                        transitionTo(mStoppedState);
-                    } else {
-                        transitionTo(mDhcpBoundState);
-                    }
+                    transitionTo(mDhcpBoundState);
                     return HANDLED;
                 default:
                     return NOT_HANDLED;
@@ -823,8 +840,19 @@
         @Override
         public void enter() {
             super.enter();
-            // TODO: DhcpStateMachine only supported renewing at 50% of the lease time,
-            // and did not support rebinding. Now that the legacy DHCP client is gone, fix this.
+            if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
+                // There's likely no point in going into DhcpInitState here, we'll probably
+                // just repeat the transaction, get the same IP address as before, and fail.
+                //
+                // NOTE: It is observed that connectUdpSock() basically never fails, due to
+                // SO_BINDTODEVICE. Examining the local socket address shows it will happily
+                // return an IPv4 address from another interface, or even return "0.0.0.0".
+                //
+                // TODO: Consider deleting this check, following testing on several kernels.
+                notifyFailure();
+                transitionTo(mStoppedState);
+            }
+
             scheduleLeaseTimers();
         }
 
@@ -843,18 +871,10 @@
                     return NOT_HANDLED;
             }
         }
-
-        @Override
-        public void exit() {
-            mRenewAlarm.cancel();
-        }
     }
 
-    class DhcpRenewingState extends PacketRetransmittingState {
-        public DhcpRenewingState() {
-            super();
-            mTimeout = DHCP_TIMEOUT_MS;
-        }
+    abstract class DhcpReacquiringState extends PacketRetransmittingState {
+        protected String mLeaseMsg;
 
         @Override
         public void enter() {
@@ -862,16 +882,14 @@
             startNewTransaction();
         }
 
+        abstract protected Inet4Address packetDestination();
+
         protected boolean sendPacket() {
-            // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
-            // http://b/25343517 . Try to make things work anyway by using broadcast renews.
-            Inet4Address to = (mDhcpLease.serverAddress != null) ?
-                    mDhcpLease.serverAddress : INADDR_BROADCAST;
             return sendRequestPacket(
                     (Inet4Address) mDhcpLease.ipAddress.getAddress(),  // ciaddr
                     INADDR_ANY,                                        // DHCP_REQUESTED_IP
                     null,                                              // DHCP_SERVER_IDENTIFIER
-                    to);                                               // packet destination address
+                    packetDestination());                              // packet destination address
         }
 
         protected void receivePacket(DhcpPacket packet) {
@@ -890,7 +908,7 @@
                     // in IpManager and by any overridden relevant handlers of
                     // the registered IpManager.Callback.  IP address changes
                     // are not supported here.
-                    acceptDhcpResults(results, "Renewed");
+                    acceptDhcpResults(results, mLeaseMsg);
                     transitionTo(mDhcpBoundState);
                 }
             } else if (packet instanceof DhcpNakPacket) {
@@ -901,8 +919,57 @@
         }
     }
 
-    // Not implemented--yet. DhcpStateMachine did not implement it either.
-    class DhcpRebindingState extends LoggingState {
+    class DhcpRenewingState extends DhcpReacquiringState {
+        public DhcpRenewingState() {
+            mLeaseMsg = "Renewed";
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (super.processMessage(message) == HANDLED) {
+                return HANDLED;
+            }
+
+            switch (message.what) {
+                case CMD_REBIND_DHCP:
+                    transitionTo(mDhcpRebindingState);
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+
+        @Override
+        protected Inet4Address packetDestination() {
+            // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
+            // http://b/25343517 . Try to make things work anyway by using broadcast renews.
+            return (mDhcpLease.serverAddress != null) ?
+                    mDhcpLease.serverAddress : INADDR_BROADCAST;
+        }
+    }
+
+    class DhcpRebindingState extends DhcpReacquiringState {
+        public DhcpRebindingState() {
+            mLeaseMsg = "Rebound";
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+
+            // We need to broadcast and possibly reconnect the socket to a
+            // completely different server.
+            closeQuietly(mUdpSock);
+            if (!initUdpSocket()) {
+                Log.e(TAG, "Failed to recreate UDP socket");
+                transitionTo(mDhcpInitState);
+            }
+        }
+
+        @Override
+        protected Inet4Address packetDestination() {
+            return INADDR_BROADCAST;
+        }
     }
 
     class DhcpInitRebootState extends LoggingState {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index d8eab35..cece6c8 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -1027,6 +1027,8 @@
     }
 
     class StartedState extends State {
+        private boolean mDhcpActionInFlight;
+
         @Override
         public void enter() {
             mStartTimeMillis = SystemClock.elapsedRealtime();
@@ -1066,7 +1068,7 @@
         @Override
         public void exit() {
             mProvisioningTimeoutAlarm.cancel();
-            mDhcpActionTimeoutAlarm.cancel();
+            stopDhcpAction();
 
             if (mIpReachabilityMonitor != null) {
                 mIpReachabilityMonitor.stop();
@@ -1086,16 +1088,22 @@
             resetLinkProperties();
         }
 
-        private void startDhcpAction() {
-            mCallback.onPreDhcpAction();
-            final long alarmTime = SystemClock.elapsedRealtime() +
-                    mConfiguration.mRequestedPreDhcpActionMs;
-            mDhcpActionTimeoutAlarm.schedule(alarmTime);
+        private void ensureDhcpAction() {
+            if (!mDhcpActionInFlight) {
+                mCallback.onPreDhcpAction();
+                mDhcpActionInFlight = true;
+                final long alarmTime = SystemClock.elapsedRealtime() +
+                        mConfiguration.mRequestedPreDhcpActionMs;
+                mDhcpActionTimeoutAlarm.schedule(alarmTime);
+            }
         }
 
         private void stopDhcpAction() {
             mDhcpActionTimeoutAlarm.cancel();
-            mCallback.onPostDhcpAction();
+            if (mDhcpActionInFlight) {
+                mCallback.onPostDhcpAction();
+                mDhcpActionInFlight = false;
+            }
         }
 
         @Override
@@ -1165,9 +1173,8 @@
                     break;
 
                 case DhcpClient.CMD_PRE_DHCP_ACTION:
-                    if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
                     if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
-                        startDhcpAction();
+                        ensureDhcpAction();
                     } else {
                         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
                     }
@@ -1193,18 +1200,18 @@
                 // This message is only received when:
                 //
                 //     a) initial address acquisition succeeds,
-                //     b) renew succeeds,
-                //     c) renew fails,
+                //     b) renew succeeds or is NAK'd,
+                //     c) rebind succeeds or is NAK'd, or
+                //     c) the lease expires,
                 //
                 // but never when initial address acquisition fails. The latter
                 // condition is now governed by the provisioning timeout.
-                case DhcpClient.CMD_POST_DHCP_ACTION: {
+                case DhcpClient.CMD_POST_DHCP_ACTION:
                     stopDhcpAction();
 
-                    final DhcpResults dhcpResults = (DhcpResults) msg.obj;
                     switch (msg.arg1) {
                         case DhcpClient.DHCP_SUCCESS:
-                            handleIPv4Success(dhcpResults);
+                            handleIPv4Success((DhcpResults) msg.obj);
                             break;
                         case DhcpClient.DHCP_FAILURE:
                             handleIPv4Failure();
@@ -1213,7 +1220,6 @@
                             Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
                     }
                     break;
-                }
 
                 case DhcpClient.CMD_ON_QUIT:
                     // DHCPv4 quit early for some reason.
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index e9c5bdd..9e04d23 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -720,36 +720,52 @@
     }
 
     @LargeTest
-    public void testApfFilterIPv4Multicast() throws Exception {
+    public void testApfFilterMulticast() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
         byte[] program = ipManagerCallback.getApfProgram();
 
+        // Construct IPv4 and IPv6 multicast packets.
+        ByteBuffer v4packet = ByteBuffer.wrap(new byte[100]);
+        v4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+        v4packet.position(IPV4_DEST_ADDR_OFFSET);
+        v4packet.put(new byte[]{(byte)224,0,0,1});
+
+        ByteBuffer v6packet = ByteBuffer.wrap(new byte[100]);
+        v6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+        v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
+        v6packet.position(IPV6_DEST_ADDR_OFFSET);
+        v6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+
         // Verify initially disabled multicast filter is off
-        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
-        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
-        packet.position(IPV4_DEST_ADDR_OFFSET);
-        packet.put(new byte[]{(byte)224,0,0,1});
-        assertPass(program, packet.array(), 0);
+        assertPass(program, v4packet.array(), 0);
+        assertPass(program, v6packet.array(), 0);
 
         // Turn on multicast filter and verify it works
         ipManagerCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(true);
         program = ipManagerCallback.getApfProgram();
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, v4packet.array(), 0);
+        assertDrop(program, v6packet.array(), 0);
 
         // Turn off multicast filter and verify it's off
         ipManagerCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(false);
         program = ipManagerCallback.getApfProgram();
-        assertPass(program, packet.array(), 0);
+        assertPass(program, v4packet.array(), 0);
+        assertPass(program, v6packet.array(), 0);
 
         // Verify it can be initialized to on
         ipManagerCallback.resetApfProgramWait();
         apfFilter.shutdown();
         apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
         program = ipManagerCallback.getApfProgram();
-        assertDrop(program, packet.array(), 0);
+        assertDrop(program, v4packet.array(), 0);
+        assertDrop(program, v6packet.array(), 0);
+
+        // Verify that ICMPv6 multicast is not dropped.
+        v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+        assertPass(program, v6packet.array(), 0);
 
         apfFilter.shutdown();
     }
@@ -839,7 +855,7 @@
     @LargeTest
     public void testApfFilterRa() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
-        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+        TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify RA is passed the first time
@@ -848,6 +864,8 @@
         basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
         basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
         basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
+        basePacket.position(IPV6_DEST_ADDR_OFFSET);
+        basePacket.put(IPV6_ALL_NODES_ADDRESS);
         assertPass(program, basePacket.array(), 0);
 
         testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
diff --git a/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
index cc0920f..b5698d5 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RateEstimatorTest.java
@@ -26,18 +26,18 @@
     @Override
     public void setUp() {
         mTestStartTime = 1225731600000L;
-        mEstimator = new RateEstimator(mTestStartTime);
+        mEstimator = new RateEstimator();
     }
 
     @SmallTest
     public void testRunningTimeBackwardDoesntExplodeUpdate() throws Exception {
-        final float rate = mEstimator.update(mTestStartTime - 1000L);
-        assertFalse(Float.isInfinite(rate));
-        assertFalse(Float.isNaN(rate));
+        assertUpdateTime(mTestStartTime);
+        assertUpdateTime(mTestStartTime - 1000L);
     }
 
     @SmallTest
     public void testRunningTimeBackwardDoesntExplodeGet() throws Exception {
+        assertUpdateTime(mTestStartTime);
         final float rate = mEstimator.getRate(mTestStartTime - 1000L);
         assertFalse(Float.isInfinite(rate));
         assertFalse(Float.isNaN(rate));
@@ -45,13 +45,14 @@
 
     @SmallTest
     public void testInstantaneousEventsDontExplodeUpdate() throws Exception {
-        final float rate = mEstimator.update(mTestStartTime);
-        assertFalse(Float.isInfinite(rate));
-        assertFalse(Float.isNaN(rate));
+        assertUpdateTime(mTestStartTime);
+        assertUpdateTime(mTestStartTime);
     }
 
     @SmallTest
     public void testInstantaneousEventsDontExplodeGet() throws Exception {
+        assertUpdateTime(mTestStartTime);
+        assertUpdateTime(mTestStartTime);
         final float rate = mEstimator.getRate(mTestStartTime);
         assertFalse(Float.isInfinite(rate));
         assertFalse(Float.isNaN(rate));
@@ -59,6 +60,7 @@
 
     @SmallTest
     public void testCompactBurstIsEstimatedUnderTwoPercent() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 1, 5); // five events at 1000Hz
         final float rate = mEstimator.getRate(nextEventTime);
@@ -67,6 +69,7 @@
 
     @SmallTest
     public void testSustained1000HzBurstIsEstimatedOverNinetyPercent() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 1, 100); // one hundred events at 1000Hz
         final float rate = mEstimator.getRate(nextEventTime);
@@ -75,6 +78,7 @@
 
     @SmallTest
     public void testSustained100HzBurstIsEstimatedOverNinetyPercent() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 10, 100); // one hundred events at 100Hz
         final float rate = mEstimator.getRate(nextEventTime);
@@ -84,6 +88,7 @@
 
     @SmallTest
     public void testRecoverQuicklyAfterSustainedBurst() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 10, 1000); // one hundred events at 100Hz
         final float rate = mEstimator.getRate(nextEventTime + 5000L); // two seconds later
@@ -92,12 +97,19 @@
 
     @SmallTest
     public void testEstimateShouldNotOvershoot() throws Exception {
+        assertUpdateTime(mTestStartTime);
         long eventStart = mTestStartTime + 1000; // start event a long time after initialization
         long nextEventTime = postEvents(eventStart, 1, 1000); // one thousand events at 1000Hz
         final float rate = mEstimator.getRate(nextEventTime);
         assertLessThan("Rate", rate, 1000f);
     }
 
+    @SmallTest
+    public void testGetRateWithoutUpdate() throws Exception {
+        final float rate = mEstimator.getRate(mTestStartTime);
+        assertLessThan("Rate", rate, 0.1f);
+    }
+
     private void assertLessThan(String label, float a, float b)  {
         assertTrue(String.format("%s was %f, but should be less than %f", label, a, b), a < b);
     }
@@ -115,4 +127,10 @@
         }
         return time;
     }
+
+    private void assertUpdateTime(long time) {
+        final float rate = mEstimator.update(time);
+        assertFalse(Float.isInfinite(rate));
+        assertFalse(Float.isNaN(rate));
+    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 57d68fd..51f5899 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1101,7 +1101,7 @@
                 if (hit && doit) {
                     // The user is force stopping our current interactor/recognizer.
                     // Clear the current settings and restore default state.
-                    synchronized (VoiceInteractionManagerService.this) {
+                    synchronized (VoiceInteractionManagerServiceStub.this) {
                         unloadAllKeyphraseModels();
                         if (mImpl != null) {
                             mImpl.shutdownLocked();
@@ -1126,7 +1126,7 @@
                 int userHandle = getChangingUserId();
                 if (DEBUG) Slog.d(TAG, "onSomePackagesChanged user=" + userHandle);
 
-                synchronized (VoiceInteractionManagerService.this) {
+                synchronized (VoiceInteractionManagerServiceStub.this) {
                     ComponentName curInteractor = getCurInteractor(userHandle);
                     ComponentName curRecognizer = getCurRecognizer(userHandle);
                     if (curRecognizer == null) {