Merge "Update docs for Drawable.setLayoutDirection() return value" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 7d762fe..cf2801a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5031,13 +5031,13 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
-    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
     method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
-    method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
     method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
@@ -5897,11 +5897,11 @@
     method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
-    method public java.lang.String getLongSupportMessage(android.content.ComponentName);
+    method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
-    method public java.lang.String getOrganizationName(android.content.ComponentName);
+    method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -5920,7 +5920,7 @@
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
-    method public java.lang.String getShortSupportMessage(android.content.ComponentName);
+    method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5971,12 +5971,12 @@
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
-    method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
+    method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
     method public void setOrganizationColor(android.content.ComponentName, int);
-    method public void setOrganizationName(android.content.ComponentName, java.lang.String);
+    method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
     method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean);
     method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
     method public void setPasswordHistoryLength(android.content.ComponentName, int);
@@ -5999,7 +5999,7 @@
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setSecurityLoggingEnabled(android.content.ComponentName, boolean);
-    method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
+    method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
@@ -8615,6 +8615,7 @@
     field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO";
     field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
     field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+    field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
     field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
     field public static final java.lang.String ACTION_SYNC = "android.intent.action.SYNC";
     field public static final java.lang.String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
@@ -8709,6 +8710,7 @@
     field public static final java.lang.String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
     field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -9497,11 +9499,8 @@
 
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
-    method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
     method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public int getShortcutIconResId(android.content.pm.ShortcutInfo);
-    method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -10042,7 +10041,9 @@
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
+    method public java.util.List<java.lang.String> getCategories();
     method public android.os.PersistableBundle getExtras();
+    method public int getIconResourceId();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
     method public long getLastChangedTimestamp();
@@ -10066,12 +10067,14 @@
     field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
     field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
     field public static final int FLAG_PINNED = 2; // 0x2
+    field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+    method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.List<java.lang.String>);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
@@ -10082,15 +10085,15 @@
   }
 
   public class ShortcutManager {
-    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
-    method public void deleteAllDynamicShortcuts();
-    method public void deleteDynamicShortcut(java.lang.String);
+    method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
     method public int getIconMaxDimensions();
     method public int getMaxDynamicShortcutCount();
     method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
     method public long getRateLimitResetTime();
     method public int getRemainingCallCount();
+    method public void removeAllDynamicShortcuts();
+    method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
     method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
   }
@@ -14436,13 +14439,13 @@
 
   public final class OutputConfiguration implements android.os.Parcelable {
     ctor public OutputConfiguration(android.view.Surface);
+    ctor public OutputConfiguration(int, android.view.Surface);
     method public int describeContents();
     method public android.view.Surface getSurface();
-    method public int getSurfaceSetId();
-    method public void setSurfaceSetId(int);
+    method public int getSurfaceGroupId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
-    field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff
+    field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
   }
 
   public final class RggbChannelVector {
@@ -22966,6 +22969,7 @@
     method public android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public int describeContents();
+    method public android.os.Bundle getExtras();
     method public java.lang.String getId();
     method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
@@ -22995,6 +22999,7 @@
     ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
     method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
+    method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
   }
 
@@ -29584,6 +29589,7 @@
   public class StorageManager {
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+    method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
@@ -49061,6 +49067,7 @@
     ctor public BufferedReader(java.io.Reader, int);
     ctor public BufferedReader(java.io.Reader);
     method public void close() throws java.io.IOException;
+    method public java.util.stream.Stream<java.lang.String> lines();
     method public int read(char[], int, int) throws java.io.IOException;
     method public java.lang.String readLine() throws java.io.IOException;
   }
@@ -49931,6 +49938,11 @@
     ctor public UTFDataFormatException(java.lang.String);
   }
 
+  public class UncheckedIOException extends java.lang.RuntimeException {
+    ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+    ctor public UncheckedIOException(java.io.IOException);
+  }
+
   public class UnsupportedEncodingException extends java.io.IOException {
     ctor public UnsupportedEncodingException();
     ctor public UnsupportedEncodingException(java.lang.String);
@@ -57587,6 +57599,7 @@
     method public void set(int, int);
     method public void set(int, int, boolean);
     method public int size();
+    method public java.util.stream.IntStream stream();
     method public byte[] toByteArray();
     method public long[] toLongArray();
     method public static java.util.BitSet valueOf(long[]);
@@ -58627,6 +58640,18 @@
   public class Random implements java.io.Serializable {
     ctor public Random();
     ctor public Random(long);
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
     method protected int next(int);
     method public boolean nextBoolean();
     method public void nextBytes(byte[]);
@@ -60144,18 +60169,6 @@
 
   public class ThreadLocalRandom extends java.util.Random {
     method public static java.util.concurrent.ThreadLocalRandom current();
-    method public java.util.stream.DoubleStream doubles(long);
-    method public java.util.stream.DoubleStream doubles();
-    method public java.util.stream.DoubleStream doubles(long, double, double);
-    method public java.util.stream.DoubleStream doubles(double, double);
-    method public java.util.stream.IntStream ints(long);
-    method public java.util.stream.IntStream ints();
-    method public java.util.stream.IntStream ints(long, int, int);
-    method public java.util.stream.IntStream ints(int, int);
-    method public java.util.stream.LongStream longs(long);
-    method public java.util.stream.LongStream longs();
-    method public java.util.stream.LongStream longs(long, long, long);
-    method public java.util.stream.LongStream longs(long, long);
     method public double nextDouble(double);
     method public double nextDouble(double, double);
     method public int nextInt(int, int);
@@ -61528,6 +61541,7 @@
   }
 
   public final class Pattern implements java.io.Serializable {
+    method public java.util.function.Predicate<java.lang.String> asPredicate();
     method public static java.util.regex.Pattern compile(java.lang.String);
     method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
     method public int flags();
@@ -61537,6 +61551,7 @@
     method public static java.lang.String quote(java.lang.String);
     method public java.lang.String[] split(java.lang.CharSequence, int);
     method public java.lang.String[] split(java.lang.CharSequence);
+    method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
     field public static final int CANON_EQ = 128; // 0x80
     field public static final int CASE_INSENSITIVE = 2; // 0x2
     field public static final int COMMENTS = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 362fa3f..19474ed 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5165,13 +5165,13 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
-    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
     method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
-    method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
     method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
@@ -6040,11 +6040,11 @@
     method public java.lang.String getDeviceOwnerNameOnAnyUser();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
-    method public java.lang.String getLongSupportMessage(android.content.ComponentName);
+    method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
-    method public java.lang.String getOrganizationName(android.content.ComponentName);
+    method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -6067,7 +6067,7 @@
     method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
     method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
-    method public java.lang.String getShortSupportMessage(android.content.ComponentName);
+    method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -6121,12 +6121,12 @@
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
-    method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
+    method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
     method public void setOrganizationColor(android.content.ComponentName, int);
-    method public void setOrganizationName(android.content.ComponentName, java.lang.String);
+    method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
     method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean);
     method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
     method public void setPasswordHistoryLength(android.content.ComponentName, int);
@@ -6149,7 +6149,7 @@
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setSecurityLoggingEnabled(android.content.ComponentName, boolean);
-    method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
+    method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
@@ -8934,6 +8934,7 @@
     field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO";
     field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
     field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+    field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
     field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
     field public static final java.lang.String ACTION_SYNC = "android.intent.action.SYNC";
     field public static final java.lang.String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
@@ -9835,11 +9836,8 @@
 
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
-    method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
     method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public int getShortcutIconResId(android.content.pm.ShortcutInfo);
-    method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -10442,7 +10440,9 @@
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
+    method public java.util.List<java.lang.String> getCategories();
     method public android.os.PersistableBundle getExtras();
+    method public int getIconResourceId();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
     method public long getLastChangedTimestamp();
@@ -10466,12 +10466,14 @@
     field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
     field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
     field public static final int FLAG_PINNED = 2; // 0x2
+    field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+    method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.List<java.lang.String>);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
@@ -10482,15 +10484,15 @@
   }
 
   public class ShortcutManager {
-    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
-    method public void deleteAllDynamicShortcuts();
-    method public void deleteDynamicShortcut(java.lang.String);
+    method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
     method public int getIconMaxDimensions();
     method public int getMaxDynamicShortcutCount();
     method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
     method public long getRateLimitResetTime();
     method public int getRemainingCallCount();
+    method public void removeAllDynamicShortcuts();
+    method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
     method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
   }
@@ -14844,20 +14846,20 @@
 
   public final class OutputConfiguration implements android.os.Parcelable {
     ctor public OutputConfiguration(android.view.Surface);
+    ctor public OutputConfiguration(int, android.view.Surface);
     ctor public OutputConfiguration(android.view.Surface, int);
-    ctor public OutputConfiguration(android.hardware.camera2.params.OutputConfiguration);
+    ctor public OutputConfiguration(int, android.view.Surface, int);
     method public int describeContents();
     method public int getRotation();
     method public android.view.Surface getSurface();
-    method public int getSurfaceSetId();
-    method public void setSurfaceSetId(int);
+    method public int getSurfaceGroupId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int ROTATION_0 = 0; // 0x0
     field public static final int ROTATION_180 = 2; // 0x2
     field public static final int ROTATION_270 = 3; // 0x3
     field public static final int ROTATION_90 = 1; // 0x1
-    field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff
+    field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
   }
 
   public final class RggbChannelVector {
@@ -24623,6 +24625,7 @@
     method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.media.tv.TvInputHardwareInfo, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public int describeContents();
+    method public android.os.Bundle getExtras();
     method public android.hardware.hdmi.HdmiDeviceInfo getHdmiDeviceInfo();
     method public java.lang.String getId();
     method public java.lang.String getParentId();
@@ -24656,6 +24659,7 @@
     ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
     method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
+    method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
     method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
     method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon, int);
@@ -31890,6 +31894,7 @@
   public class StorageManager {
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+    method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
@@ -52174,6 +52179,7 @@
     ctor public BufferedReader(java.io.Reader, int);
     ctor public BufferedReader(java.io.Reader);
     method public void close() throws java.io.IOException;
+    method public java.util.stream.Stream<java.lang.String> lines();
     method public int read(char[], int, int) throws java.io.IOException;
     method public java.lang.String readLine() throws java.io.IOException;
   }
@@ -53044,6 +53050,11 @@
     ctor public UTFDataFormatException(java.lang.String);
   }
 
+  public class UncheckedIOException extends java.lang.RuntimeException {
+    ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+    ctor public UncheckedIOException(java.io.IOException);
+  }
+
   public class UnsupportedEncodingException extends java.io.IOException {
     ctor public UnsupportedEncodingException();
     ctor public UnsupportedEncodingException(java.lang.String);
@@ -60700,6 +60711,7 @@
     method public void set(int, int);
     method public void set(int, int, boolean);
     method public int size();
+    method public java.util.stream.IntStream stream();
     method public byte[] toByteArray();
     method public long[] toLongArray();
     method public static java.util.BitSet valueOf(long[]);
@@ -61740,6 +61752,18 @@
   public class Random implements java.io.Serializable {
     ctor public Random();
     ctor public Random(long);
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
     method protected int next(int);
     method public boolean nextBoolean();
     method public void nextBytes(byte[]);
@@ -63257,18 +63281,6 @@
 
   public class ThreadLocalRandom extends java.util.Random {
     method public static java.util.concurrent.ThreadLocalRandom current();
-    method public java.util.stream.DoubleStream doubles(long);
-    method public java.util.stream.DoubleStream doubles();
-    method public java.util.stream.DoubleStream doubles(long, double, double);
-    method public java.util.stream.DoubleStream doubles(double, double);
-    method public java.util.stream.IntStream ints(long);
-    method public java.util.stream.IntStream ints();
-    method public java.util.stream.IntStream ints(long, int, int);
-    method public java.util.stream.IntStream ints(int, int);
-    method public java.util.stream.LongStream longs(long);
-    method public java.util.stream.LongStream longs();
-    method public java.util.stream.LongStream longs(long, long, long);
-    method public java.util.stream.LongStream longs(long, long);
     method public double nextDouble(double);
     method public double nextDouble(double, double);
     method public int nextInt(int, int);
@@ -64641,6 +64653,7 @@
   }
 
   public final class Pattern implements java.io.Serializable {
+    method public java.util.function.Predicate<java.lang.String> asPredicate();
     method public static java.util.regex.Pattern compile(java.lang.String);
     method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
     method public int flags();
@@ -64650,6 +64663,7 @@
     method public static java.lang.String quote(java.lang.String);
     method public java.lang.String[] split(java.lang.CharSequence, int);
     method public java.lang.String[] split(java.lang.CharSequence);
+    method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
     field public static final int CANON_EQ = 128; // 0x80
     field public static final int CASE_INSENSITIVE = 2; // 0x2
     field public static final int COMMENTS = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 8ee1655..3e36c77 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5031,13 +5031,13 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
-    method public boolean getHintContentIntentLaunchesActivity();
+    method public boolean getHintLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
     method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
-    method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
     method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
@@ -5901,11 +5901,11 @@
     method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
-    method public java.lang.String getLongSupportMessage(android.content.ComponentName);
+    method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
-    method public java.lang.String getOrganizationName(android.content.ComponentName);
+    method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -5924,7 +5924,7 @@
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
-    method public java.lang.String getShortSupportMessage(android.content.ComponentName);
+    method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5975,12 +5975,12 @@
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
-    method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
+    method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
     method public void setOrganizationColor(android.content.ComponentName, int);
-    method public void setOrganizationName(android.content.ComponentName, java.lang.String);
+    method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
     method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean);
     method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
     method public void setPasswordHistoryLength(android.content.ComponentName, int);
@@ -6003,7 +6003,7 @@
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setSecurityLoggingEnabled(android.content.ComponentName, boolean);
-    method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
+    method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
@@ -8622,6 +8622,7 @@
     field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO";
     field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
     field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+    field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
     field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
     field public static final java.lang.String ACTION_SYNC = "android.intent.action.SYNC";
     field public static final java.lang.String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
@@ -8716,6 +8717,7 @@
     field public static final java.lang.String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
     field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -9507,11 +9509,8 @@
   public class LauncherApps {
     ctor public LauncherApps(android.content.Context);
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
-    method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
     method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public int getShortcutIconResId(android.content.pm.ShortcutInfo);
-    method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -10053,7 +10052,9 @@
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
+    method public java.util.List<java.lang.String> getCategories();
     method public android.os.PersistableBundle getExtras();
+    method public int getIconResourceId();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
     method public long getLastChangedTimestamp();
@@ -10077,12 +10078,14 @@
     field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
     field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
     field public static final int FLAG_PINNED = 2; // 0x2
+    field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
   public static class ShortcutInfo.Builder {
     ctor public ShortcutInfo.Builder(android.content.Context);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+    method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.List<java.lang.String>);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
@@ -10094,15 +10097,15 @@
 
   public class ShortcutManager {
     ctor public ShortcutManager(android.content.Context);
-    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
-    method public void deleteAllDynamicShortcuts();
-    method public void deleteDynamicShortcut(java.lang.String);
+    method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
     method public int getIconMaxDimensions();
     method public int getMaxDynamicShortcutCount();
     method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
     method public long getRateLimitResetTime();
     method public int getRemainingCallCount();
+    method public void removeAllDynamicShortcuts();
+    method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
     method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
   }
@@ -14448,13 +14451,13 @@
 
   public final class OutputConfiguration implements android.os.Parcelable {
     ctor public OutputConfiguration(android.view.Surface);
+    ctor public OutputConfiguration(int, android.view.Surface);
     method public int describeContents();
     method public android.view.Surface getSurface();
-    method public int getSurfaceSetId();
-    method public void setSurfaceSetId(int);
+    method public int getSurfaceGroupId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
-    field public static final int SURFACE_SET_ID_INVALID = -1; // 0xffffffff
+    field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
   }
 
   public final class RggbChannelVector {
@@ -23034,6 +23037,7 @@
     method public android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public int describeContents();
+    method public android.os.Bundle getExtras();
     method public java.lang.String getId();
     method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
@@ -23063,6 +23067,7 @@
     ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
     method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
+    method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
   }
 
@@ -29653,6 +29658,7 @@
   public class StorageManager {
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+    method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
@@ -49139,6 +49145,7 @@
     ctor public BufferedReader(java.io.Reader, int);
     ctor public BufferedReader(java.io.Reader);
     method public void close() throws java.io.IOException;
+    method public java.util.stream.Stream<java.lang.String> lines();
     method public int read(char[], int, int) throws java.io.IOException;
     method public java.lang.String readLine() throws java.io.IOException;
   }
@@ -50009,6 +50016,11 @@
     ctor public UTFDataFormatException(java.lang.String);
   }
 
+  public class UncheckedIOException extends java.lang.RuntimeException {
+    ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+    ctor public UncheckedIOException(java.io.IOException);
+  }
+
   public class UnsupportedEncodingException extends java.io.IOException {
     ctor public UnsupportedEncodingException();
     ctor public UnsupportedEncodingException(java.lang.String);
@@ -57665,6 +57677,7 @@
     method public void set(int, int);
     method public void set(int, int, boolean);
     method public int size();
+    method public java.util.stream.IntStream stream();
     method public byte[] toByteArray();
     method public long[] toLongArray();
     method public static java.util.BitSet valueOf(long[]);
@@ -58705,6 +58718,18 @@
   public class Random implements java.io.Serializable {
     ctor public Random();
     ctor public Random(long);
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
     method protected int next(int);
     method public boolean nextBoolean();
     method public void nextBytes(byte[]);
@@ -60222,18 +60247,6 @@
 
   public class ThreadLocalRandom extends java.util.Random {
     method public static java.util.concurrent.ThreadLocalRandom current();
-    method public java.util.stream.DoubleStream doubles(long);
-    method public java.util.stream.DoubleStream doubles();
-    method public java.util.stream.DoubleStream doubles(long, double, double);
-    method public java.util.stream.DoubleStream doubles(double, double);
-    method public java.util.stream.IntStream ints(long);
-    method public java.util.stream.IntStream ints();
-    method public java.util.stream.IntStream ints(long, int, int);
-    method public java.util.stream.IntStream ints(int, int);
-    method public java.util.stream.LongStream longs(long);
-    method public java.util.stream.LongStream longs();
-    method public java.util.stream.LongStream longs(long, long, long);
-    method public java.util.stream.LongStream longs(long, long);
     method public double nextDouble(double);
     method public double nextDouble(double, double);
     method public int nextInt(int, int);
@@ -61606,6 +61619,7 @@
   }
 
   public final class Pattern implements java.io.Serializable {
+    method public java.util.function.Predicate<java.lang.String> asPredicate();
     method public static java.util.regex.Pattern compile(java.lang.String);
     method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
     method public int flags();
@@ -61615,6 +61629,7 @@
     method public static java.lang.String quote(java.lang.String);
     method public java.lang.String[] split(java.lang.CharSequence, int);
     method public java.lang.String[] split(java.lang.CharSequence);
+    method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
     field public static final int CANON_EQ = 128; // 0x80
     field public static final int CASE_INSENSITIVE = 2; // 0x2
     field public static final int COMMENTS = 4; // 0x4
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index c6a5152..31035a7 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1200,13 +1200,17 @@
     boolean animateBasedOnTime(long currentTime) {
         boolean done = false;
         if (mRunning) {
-            final float fraction = getScaledDuration() > 0 ?
-                    (float)(currentTime - mStartTime) / getScaledDuration() : 1f;
+            final long scaledDuration = getScaledDuration();
+            final float fraction = scaledDuration > 0 ?
+                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
             final float lastFraction = mOverallFraction;
             final boolean newIteration = (int) fraction > (int) lastFraction;
             final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                     (mRepeatCount != INFINITE);
-            if (newIteration && !lastIterationFinished) {
+            if (scaledDuration == 0) {
+                // 0 duration animator, ignore the repeat count and skip to the end
+                done = true;
+            } else if (newIteration && !lastIterationFinished) {
                 // Time to repeat
                 if (mListeners != null) {
                     int numListeners = mListeners.size();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d1f5143..631a129 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
@@ -1305,6 +1306,12 @@
          */
         public boolean isDockable;
 
+        /**
+         * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
+         * @hide
+         */
+        public int resizeMode;
+
         public RecentTaskInfo() {
         }
 
@@ -1349,6 +1356,7 @@
                 dest.writeInt(0);
             }
             dest.writeInt(isDockable ? 1 : 0);
+            dest.writeInt(resizeMode);
         }
 
         public void readFromParcel(Parcel source) {
@@ -1372,6 +1380,7 @@
             bounds = source.readInt() > 0 ?
                     Rect.CREATOR.createFromParcel(source) : null;
             isDockable = source.readInt() == 1;
+            resizeMode = source.readInt();
         }
 
         public static final Creator<RecentTaskInfo> CREATOR
@@ -1560,6 +1569,12 @@
          */
         public boolean isDockable;
 
+        /**
+         * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
+         * @hide
+         */
+        public int resizeMode;
+
         public RunningTaskInfo() {
         }
 
@@ -1583,6 +1598,7 @@
             dest.writeInt(numActivities);
             dest.writeInt(numRunning);
             dest.writeInt(isDockable ? 1 : 0);
+            dest.writeInt(resizeMode);
         }
 
         public void readFromParcel(Parcel source) {
@@ -1599,6 +1615,7 @@
             numActivities = source.readInt();
             numRunning = source.readInt();
             isDockable = source.readInt() != 0;
+            resizeMode = source.readInt();
         }
 
         public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ed590e6..bf56f25 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1921,6 +1921,16 @@
     }
 
     @Override
+    public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+            IPackageDataObserver observer) {
+        try {
+            mPM.deleteApplicationCacheFilesAsUser(packageName, userId, observer);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public void freeStorageAndNotify(String volumeUuid, long idealStorageSize,
             IPackageDataObserver observer) {
         try {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 784872c..052874f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -29,6 +29,7 @@
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
@@ -43,6 +44,7 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.text.BidiFormatter;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.TextUtils;
@@ -588,8 +590,8 @@
     private static final int COLOR_INVALID = 1;
 
     /**
-     * Sphere of visibility of this notification, which affects how and when the SystemUI reveals 
-     * the notification's presence and contents in untrusted situations (namely, on the secure 
+     * Sphere of visibility of this notification, which affects how and when the SystemUI reveals
+     * the notification's presence and contents in untrusted situations (namely, on the secure
      * lockscreen).
      *
      * The default level, {@link #VISIBILITY_PRIVATE}, behaves exactly as notifications have always
@@ -1419,7 +1421,7 @@
              * an activity and transitions should be generated, false otherwise.
              * @return this object for method chaining
              */
-            public WearableExtender setHintContentIntentLaunchesActivity(
+            public WearableExtender setHintLaunchesActivity(
                     boolean hintLaunchesActivity) {
                 setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
                 return this;
@@ -1432,7 +1434,7 @@
              * should be generated, false otherwise. The default value is {@code false} if this was
              * never set.
              */
-            public boolean getHintContentIntentLaunchesActivity() {
+            public boolean getHintLaunchesActivity() {
                 return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
             }
         }
@@ -2227,7 +2229,8 @@
                         Log.d(TAG, "Unknown style class: " + templateClass);
                     } else {
                         try {
-                            final Constructor<? extends Style> ctor = styleClass.getConstructor();
+                            final Constructor<? extends Style> ctor =
+                                    styleClass.getDeclaredConstructor();
                             ctor.setAccessible(true);
                             final Style style = ctor.newInstance();
                             style.restoreFromExtras(mN.extras);
@@ -3126,6 +3129,18 @@
          * @param hasProgress whether the progress bar should be shown and set
          */
         private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
+            final Bundle ex = mN.extras;
+
+            CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
+            CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
+            return applyStandardTemplate(resId, hasProgress, title, text);
+        }
+
+        /**
+         * @param hasProgress whether the progress bar should be shown and set
+         */
+        private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
+                CharSequence title, CharSequence text) {
             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
 
             resetStandardTemplate(contentView);
@@ -3134,17 +3149,15 @@
 
             bindNotificationHeader(contentView);
             bindLargeIcon(contentView);
-            if (ex.getCharSequence(EXTRA_TITLE) != null) {
+            if (title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
-                contentView.setTextViewText(R.id.title,
-                        processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
+                contentView.setTextViewText(R.id.title, title);
             }
             boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
-            if (ex.getCharSequence(EXTRA_TEXT) != null) {
+            if (text != null) {
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
-                contentView.setTextViewText(textId, processLegacyText(
-                        ex.getCharSequence(EXTRA_TEXT)));
+                contentView.setTextViewText(textId, text);
                 contentView.setViewVisibility(textId, View.VISIBLE);
             }
 
@@ -3295,7 +3308,16 @@
         }
 
         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
-            RemoteViews big = applyStandardTemplate(layoutId);
+            final Bundle ex = mN.extras;
+
+            CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
+            CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
+            return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
+        }
+
+        private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
+                CharSequence title, CharSequence text) {
+            RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
 
             resetStandardTemplateWithActions(big);
 
@@ -3749,6 +3771,10 @@
             return R.layout.notification_template_material_inbox;
         }
 
+        private int getMessagingLayoutResource() {
+            return R.layout.notification_template_material_messaging;
+        }
+
         private int getActionLayoutResource() {
             return R.layout.notification_material_action;
         }
@@ -4375,13 +4401,100 @@
         /**
          * @hide
          */
-        public RemoteViews makeBigContentView() {
-            // TODO handset to write implementation
-            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
+        @Override
+        public RemoteViews makeContentView() {
+            Message m = findLatestIncomingMessage();
+            CharSequence title = mConversationTitle != null
+                    ? mConversationTitle
+                    : (m == null) ? null : m.mSender;
+            CharSequence text = (m == null)
+                    ? null
+                    : mConversationTitle != null ? makeMessageLine(m) : m.mText;
 
+            return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
+                    false /* hasProgress */,
+                    title,
+                    text);
+        }
+
+        private Message findLatestIncomingMessage() {
+            for (int i = mMessages.size() - 1; i >= 0; i--) {
+                Message m = mMessages.get(i);
+                // Incoming messages have a non-empty sender.
+                if (!TextUtils.isEmpty(m.mSender)) {
+                    return m;
+                }
+            }
+            return null;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public RemoteViews makeBigContentView() {
+            CharSequence title = !TextUtils.isEmpty(super.mBigContentTitle)
+                    ? super.mBigContentTitle
+                    : mConversationTitle;
+            boolean hasTitle = !TextUtils.isEmpty(title);
+
+            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
+                    mBuilder.getMessagingLayoutResource(),
+                    false /* hasProgress */,
+                    title,
+                    null /* text */);
+
+            int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
+                    R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
+
+            // Make sure all rows are gone in case we reuse a view.
+            for (int rowId : rowIds) {
+                contentView.setViewVisibility(rowId, View.GONE);
+            }
+
+            int i=0;
+            int titlePadding = mBuilder.mContext.getResources().getDimensionPixelSize(
+                    R.dimen.notification_messaging_spacing);
+            contentView.setViewLayoutMarginBottom(R.id.line1, hasTitle ? titlePadding : 0);
+            contentView.setInt(R.id.notification_messaging, "setNumIndentLines",
+                    mBuilder.mN.mLargeIcon == null ? 0 : (hasTitle ? 1 : 2));
+
+            int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
+            while (firstMessage + i < mMessages.size() && i < rowIds.length) {
+                Message m = mMessages.get(firstMessage + i);
+                int rowId = rowIds[i];
+
+                contentView.setViewVisibility(rowId, View.VISIBLE);
+                contentView.setTextViewText(rowId, makeMessageLine(m));
+
+                i++;
+            }
             return contentView;
         }
 
+        private CharSequence makeMessageLine(Message m) {
+            BidiFormatter bidi = BidiFormatter.getInstance();
+            SpannableStringBuilder sb = new SpannableStringBuilder();
+            if (TextUtils.isEmpty(m.mSender)) {
+                CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
+                sb.append(bidi.unicodeWrap(replyName),
+                        makeFontColorSpan(mBuilder.resolveContrastColor()),
+                        0 /* flags */);
+            } else {
+                sb.append(bidi.unicodeWrap(m.mSender),
+                        makeFontColorSpan(Color.BLACK),
+                        0 /* flags */);
+            }
+            CharSequence text = m.mText == null ? "" : m.mText;
+            sb.append("  ").append(bidi.unicodeWrap(text));
+            return sb;
+        }
+
+        private static TextAppearanceSpan makeFontColorSpan(int color) {
+            return new TextAppearanceSpan(null, 0, 0,
+                    ColorStateList.valueOf(color), null);
+        }
+
         public static final class Message implements Parcelable {
 
             private final CharSequence mText;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 4c4f128..aef92cf 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -29,6 +29,7 @@
 import android.content.res.ResourcesKey;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.IBinder;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.LocaleList;
@@ -430,37 +431,44 @@
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        final ResourcesKey key = new ResourcesKey(
-                resDir,
-                splitResDirs,
-                overlayDirs,
-                libDirs,
-                displayId,
-                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
-                compatInfo);
-        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#createBaseActivityResources");
+            final ResourcesKey key = new ResourcesKey(
+                    resDir,
+                    splitResDirs,
+                    overlayDirs,
+                    libDirs,
+                    displayId,
+                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    compatInfo);
+            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
 
-        if (DEBUG) {
-            Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
-                    + " with key=" + key);
-        }
-
-        synchronized (this) {
-            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                    activityToken);
-
-            if (overrideConfig != null) {
-                activityResources.overrideConfig.setTo(overrideConfig);
-            } else {
-                activityResources.overrideConfig.setToDefaults();
+            if (DEBUG) {
+                Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+                        + " with key=" + key);
             }
+
+            synchronized (this) {
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(
+                                activityToken);
+
+                if (overrideConfig != null) {
+                    activityResources.overrideConfig.setTo(overrideConfig);
+                } else {
+                    activityResources.overrideConfig.setToDefaults();
+                }
+            }
+
+            // Update any existing Activity Resources references.
+            updateResourcesForActivity(activityToken, overrideConfig);
+
+            // Now request an actual Resources object.
+            return getOrCreateResources(activityToken, key, classLoader);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
-
-        // Update any existing Activity Resources references.
-        updateResourcesForActivity(activityToken, overrideConfig);
-
-        // Now request an actual Resources object.
-        return getOrCreateResources(activityToken, key, classLoader);
     }
 
     /**
@@ -490,8 +498,8 @@
             }
 
             if (activityToken != null) {
-                final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                        activityToken);
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(activityToken);
 
                 // Clean up any dead references so they don't pile up.
                 ArrayUtils.unstableRemoveIf(activityResources.activityResources,
@@ -539,6 +547,7 @@
         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
@@ -613,16 +622,21 @@
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader) {
-        final ResourcesKey key = new ResourcesKey(
-                resDir,
-                splitResDirs,
-                overlayDirs,
-                libDirs,
-                displayId,
-                overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
-                compatInfo);
-        classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
-        return getOrCreateResources(activityToken, key, classLoader);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
+            final ResourcesKey key = new ResourcesKey(
+                    resDir,
+                    splitResDirs,
+                    overlayDirs,
+                    libDirs,
+                    displayId,
+                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    compatInfo);
+            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+            return getOrCreateResources(activityToken, key, classLoader);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 
     /**
@@ -636,93 +650,104 @@
      */
     public void updateResourcesForActivity(@NonNull IBinder activityToken,
             @Nullable Configuration overrideConfig) {
-        synchronized (this) {
-            final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
-                    activityToken);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#updateResourcesForActivity");
+            synchronized (this) {
+                final ActivityResources activityResources =
+                        getOrCreateActivityResourcesStructLocked(activityToken);
 
-            if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
-                // They are the same, no work to do.
-                return;
-            }
-
-            // Grab a copy of the old configuration so we can create the delta's of each
-            // Resources object associated with this Activity.
-            final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
-
-            // Update the Activity's base override.
-            if (overrideConfig != null) {
-                activityResources.overrideConfig.setTo(overrideConfig);
-            } else {
-                activityResources.overrideConfig.setToDefaults();
-            }
-
-            if (DEBUG) {
-                Throwable here = new Throwable();
-                here.fillInStackTrace();
-                Slog.d(TAG, "updating resources override for activity=" + activityToken
-                        + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
-                        + " to newConfig="
-                        + Configuration.resourceQualifierString(activityResources.overrideConfig),
-                        here);
-            }
-
-            final boolean activityHasOverrideConfig =
-                    !activityResources.overrideConfig.equals(Configuration.EMPTY);
-
-            // Rebase each Resources associated with this Activity.
-            final int refCount = activityResources.activityResources.size();
-            for (int i = 0; i < refCount; i++) {
-                WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
-                Resources resources = weakResRef.get();
-                if (resources == null) {
-                    continue;
+                if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
+                    // They are the same, no work to do.
+                    return;
                 }
 
-                // Extract the ResourcesKey that was last used to create the Resources for this
-                // activity.
-                final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
-                if (oldKey == null) {
-                    Slog.e(TAG, "can't find ResourcesKey for resources impl="
-                            + resources.getImpl());
-                    continue;
-                }
+                // Grab a copy of the old configuration so we can create the delta's of each
+                // Resources object associated with this Activity.
+                final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
 
-                // Build the new override configuration for this ResourcesKey.
-                final Configuration rebasedOverrideConfig = new Configuration();
+                // Update the Activity's base override.
                 if (overrideConfig != null) {
-                    rebasedOverrideConfig.setTo(overrideConfig);
+                    activityResources.overrideConfig.setTo(overrideConfig);
+                } else {
+                    activityResources.overrideConfig.setToDefaults();
                 }
 
-                if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
-                    // Generate a delta between the old base Activity override configuration and
-                    // the actual final override configuration that was used to figure out the real
-                    // delta this Resources object wanted.
-                    Configuration overrideOverrideConfig = Configuration.generateDelta(
-                            oldConfig, oldKey.mOverrideConfiguration);
-                    rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
-                }
-
-                // Create the new ResourcesKey with the rebased override config.
-                final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
-                        oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
-                        rebasedOverrideConfig, oldKey.mCompatInfo);
-
                 if (DEBUG) {
-                    Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
-                            + " to newKey=" + newKey);
+                    Throwable here = new Throwable();
+                    here.fillInStackTrace();
+                    Slog.d(TAG, "updating resources override for activity=" + activityToken
+                            + " from oldConfig="
+                            + Configuration.resourceQualifierString(oldConfig)
+                            + " to newConfig="
+                            + Configuration.resourceQualifierString(
+                            activityResources.overrideConfig),
+                            here);
                 }
 
-                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
-                if (resourcesImpl == null) {
-                    resourcesImpl = createResourcesImpl(newKey);
-                    mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
-                }
+                final boolean activityHasOverrideConfig =
+                        !activityResources.overrideConfig.equals(Configuration.EMPTY);
 
-                if (resourcesImpl != resources.getImpl()) {
-                    // Set the ResourcesImpl, updating it for all users of this Resources object.
-                    resources.setImpl(resourcesImpl);
+                // Rebase each Resources associated with this Activity.
+                final int refCount = activityResources.activityResources.size();
+                for (int i = 0; i < refCount; i++) {
+                    WeakReference<Resources> weakResRef = activityResources.activityResources.get(
+                            i);
+                    Resources resources = weakResRef.get();
+                    if (resources == null) {
+                        continue;
+                    }
+
+                    // Extract the ResourcesKey that was last used to create the Resources for this
+                    // activity.
+                    final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+                    if (oldKey == null) {
+                        Slog.e(TAG, "can't find ResourcesKey for resources impl="
+                                + resources.getImpl());
+                        continue;
+                    }
+
+                    // Build the new override configuration for this ResourcesKey.
+                    final Configuration rebasedOverrideConfig = new Configuration();
+                    if (overrideConfig != null) {
+                        rebasedOverrideConfig.setTo(overrideConfig);
+                    }
+
+                    if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
+                        // Generate a delta between the old base Activity override configuration and
+                        // the actual final override configuration that was used to figure out the
+                        // real delta this Resources object wanted.
+                        Configuration overrideOverrideConfig = Configuration.generateDelta(
+                                oldConfig, oldKey.mOverrideConfiguration);
+                        rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+                    }
+
+                    // Create the new ResourcesKey with the rebased override config.
+                    final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
+                            oldKey.mSplitResDirs,
+                            oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
+                            rebasedOverrideConfig, oldKey.mCompatInfo);
+
+                    if (DEBUG) {
+                        Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+                                + " to newKey=" + newKey);
+                    }
+
+                    ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
+                    if (resourcesImpl == null) {
+                        resourcesImpl = createResourcesImpl(newKey);
+                        mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+                    }
+
+                    if (resourcesImpl != resources.getImpl()) {
+                        // Set the ResourcesImpl, updating it for all users of this Resources
+                        // object.
+                        resources.setImpl(resourcesImpl);
+                    }
                 }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
     }
 
@@ -745,86 +770,95 @@
 
     public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                              @Nullable CompatibilityInfo compat) {
-        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
-            if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
-                    + mResConfiguration.seq + ", newSeq=" + config.seq);
-            return false;
-        }
-        int changes = mResConfiguration.updateFrom(config);
-        // Things might have changed in display manager, so clear the cached displays.
-        mDisplays.clear();
-        DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#applyConfigurationToResourcesLocked");
 
-        if (compat != null && (mResCompatibilityInfo == null ||
-                !mResCompatibilityInfo.equals(compat))) {
-            mResCompatibilityInfo = compat;
-            changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
-                    | ActivityInfo.CONFIG_SCREEN_SIZE
-                    | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-        }
+            if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+                        + mResConfiguration.seq + ", newSeq=" + config.seq);
+                return false;
+            }
+            int changes = mResConfiguration.updateFrom(config);
+            // Things might have changed in display manager, so clear the cached displays.
+            mDisplays.clear();
+            DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
 
-        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;
+            if (compat != null && (mResCompatibilityInfo == null ||
+                    !mResCompatibilityInfo.equals(compat))) {
+                mResCompatibilityInfo = compat;
+                changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+                        | ActivityInfo.CONFIG_SCREEN_SIZE
+                        | 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(localeAdjustedConfig, defaultDisplayMetrics,
+                    compat);
 
-        ApplicationPackageManager.configurationChanged();
-        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+            ApplicationPackageManager.configurationChanged();
+            //Slog.i(TAG, "Configuration changed in " + currentPackageName());
 
-        Configuration tmpConfig = null;
+            Configuration tmpConfig = null;
 
-        for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
-            ResourcesKey key = mResourceImpls.keyAt(i);
-            ResourcesImpl r = mResourceImpls.valueAt(i).get();
-            if (r != null) {
-                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
-                        + r + " config to: " + localeAdjustedConfig);
-                int displayId = key.mDisplayId;
-                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-                DisplayMetrics dm = defaultDisplayMetrics;
-                final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
-                if (!isDefaultDisplay || hasOverrideConfiguration) {
-                    if (tmpConfig == null) {
-                        tmpConfig = new Configuration();
+            for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+                ResourcesKey key = mResourceImpls.keyAt(i);
+                ResourcesImpl r = mResourceImpls.valueAt(i).get();
+                if (r != null) {
+                    if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+                            + r + " config to: " + localeAdjustedConfig);
+                    int displayId = key.mDisplayId;
+                    boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+                    DisplayMetrics dm = defaultDisplayMetrics;
+                    final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+                    if (!isDefaultDisplay || hasOverrideConfiguration) {
+                        if (tmpConfig == null) {
+                            tmpConfig = new Configuration();
+                        }
+                        tmpConfig.setTo(localeAdjustedConfig);
+                        if (!isDefaultDisplay) {
+                            dm = getDisplayMetrics(displayId);
+                            applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+                        }
+                        if (hasOverrideConfiguration) {
+                            tmpConfig.updateFrom(key.mOverrideConfiguration);
+                        }
+                        r.updateConfiguration(tmpConfig, dm, compat);
+                    } else {
+                        r.updateConfiguration(localeAdjustedConfig, dm, compat);
                     }
-                    tmpConfig.setTo(localeAdjustedConfig);
-                    if (!isDefaultDisplay) {
-                        dm = getDisplayMetrics(displayId);
-                        applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
-                    }
-                    if (hasOverrideConfiguration) {
-                        tmpConfig.updateFrom(key.mOverrideConfiguration);
-                    }
-                    r.updateConfiguration(tmpConfig, dm, compat);
+                    //Slog.i(TAG, "Updated app resources " + v.getKey()
+                    //        + " " + r + ": " + r.getConfiguration());
                 } else {
-                    r.updateConfiguration(localeAdjustedConfig, dm, compat);
+                    //Slog.i(TAG, "Removing old resources " + v.getKey());
+                    mResourceImpls.removeAt(i);
                 }
-                //Slog.i(TAG, "Updated app resources " + v.getKey()
-                //        + " " + r + ": " + r.getConfiguration());
-            } else {
-                //Slog.i(TAG, "Removing old resources " + v.getKey());
-                mResourceImpls.removeAt(i);
             }
-        }
 
-        return changes != 0;
+            return changes != 0;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
     }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d5d4ca7..cd6e572 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -532,7 +532,8 @@
             public WifiScanner createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(Context.WIFI_SCANNING_SERVICE);
                 IWifiScanner service = IWifiScanner.Stub.asInterface(b);
-                return new WifiScanner(ctx.getOuterContext(), service);
+                return new WifiScanner(ctx.getOuterContext(), service,
+                        ConnectivityThread.getInstanceLooper());
             }});
 
         registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 96757bb..269089e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5762,7 +5762,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public void setShortSupportMessage(@NonNull ComponentName admin,
-            @Nullable String message) {
+            @Nullable CharSequence message) {
         if (mService != null) {
             try {
                 mService.setShortSupportMessage(admin, message);
@@ -5776,11 +5776,11 @@
      * Called by a device admin to get the short support message.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @return The message set by {@link #setShortSupportMessage(ComponentName, String)} or null if
-     *         no message has been set.
+     * @return The message set by {@link #setShortSupportMessage(ComponentName, CharSequence)} or
+     *         null if no message has been set.
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
-    public String getShortSupportMessage(@NonNull ComponentName admin) {
+    public CharSequence getShortSupportMessage(@NonNull ComponentName admin) {
         if (mService != null) {
             try {
                 return mService.getShortSupportMessage(admin);
@@ -5806,7 +5806,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public void setLongSupportMessage(@NonNull ComponentName admin,
-            @Nullable String message) {
+            @Nullable CharSequence message) {
         if (mService != null) {
             try {
                 mService.setLongSupportMessage(admin, message);
@@ -5820,11 +5820,11 @@
      * Called by a device admin to get the long support message.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @return The message set by {@link #setLongSupportMessage(ComponentName, String)} or null if
-     *         no message has been set.
+     * @return The message set by {@link #setLongSupportMessage(ComponentName, CharSequence)} or
+     *         null if no message has been set.
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
-    public String getLongSupportMessage(@NonNull ComponentName admin) {
+    public CharSequence getLongSupportMessage(@NonNull ComponentName admin) {
         if (mService != null) {
             try {
                 return mService.getLongSupportMessage(admin);
@@ -5840,11 +5840,12 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle user id the admin is running as.
-     * @return The message set by {@link #setShortSupportMessage(ComponentName, String)}
+     * @return The message set by {@link #setShortSupportMessage(ComponentName, CharSequence)}
      *
      * @hide
      */
-    public String getShortSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
+    public CharSequence getShortSupportMessageForUser(@NonNull ComponentName admin,
+            int userHandle) {
         if (mService != null) {
             try {
                 return mService.getShortSupportMessageForUser(admin, userHandle);
@@ -5861,11 +5862,11 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle user id the admin is running as.
-     * @return The message set by {@link #setLongSupportMessage(ComponentName, String)}
+     * @return The message set by {@link #setLongSupportMessage(ComponentName, CharSequence)}
      *
      * @hide
      */
-    public String getLongSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
+    public CharSequence getLongSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
                 return mService.getLongSupportMessageForUser(admin, userHandle);
@@ -6050,11 +6051,13 @@
      * {@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent}.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param color The 32bit representation of the color to be used.
+     * @param color The 24bit (0xRRGGBB) representation of the color to be used.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationColor(@NonNull ComponentName admin, int color) {
         try {
+            // always enforce alpha channel to have 100% opacity
+            color |= 0xFF000000;
             mService.setOrganizationColor(admin, color);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -6066,7 +6069,7 @@
      *
      * Sets the color used for customization.
      *
-     * @param color The 32bit representation of the color to be used.
+     * @param color The 24bit (0xRRGGBB) representation of the color to be used.
      * @param userId which user to set the color to.
      * @RequiresPermission(allOf = {
      *       Manifest.permission.MANAGE_USERS,
@@ -6074,6 +6077,8 @@
      */
     public void setOrganizationColorForUser(@ColorInt int color, @UserIdInt int userId) {
         try {
+            // always enforce alpha channel to have 100% opacity
+            color |= 0xFF000000;
             mService.setOrganizationColorForUser(color, userId);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -6085,10 +6090,10 @@
      * This color is used as background color of the confirm credentials screen for that user.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @return The 32bit representation of the color to be used.
+     * @return The 24bit (0xRRGGBB) representation of the color to be used.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
-    public int getOrganizationColor(@NonNull ComponentName admin) {
+    public @ColorInt int getOrganizationColor(@NonNull ComponentName admin) {
         try {
             return mService.getOrganizationColor(admin);
         } catch (RemoteException re) {
@@ -6101,9 +6106,9 @@
      * Retrieve the customization color for a given user.
      *
      * @param userHandle The user id of the user we're interested in.
-     * @return The 32bit representation of the color to be used.
+     * @return The 24bit (0xRRGGBB) representation of the color to be used.
      */
-    public int getOrganizationColorForUser(int userHandle) {
+    public @ColorInt int getOrganizationColorForUser(int userHandle) {
         try {
             return mService.getOrganizationColorForUser(userHandle);
         } catch (RemoteException re) {
@@ -6123,7 +6128,7 @@
      * @param title The organization name or {@code null} to clear a previously set name.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
-    public void setOrganizationName(@NonNull ComponentName admin, @Nullable String title) {
+    public void setOrganizationName(@NonNull ComponentName admin, @Nullable CharSequence title) {
         try {
             mService.setOrganizationName(admin, title);
         } catch (RemoteException re) {
@@ -6139,7 +6144,7 @@
      * @return The organization name or {@code null} if none is set.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
-    public String getOrganizationName(@NonNull ComponentName admin) {
+    public CharSequence getOrganizationName(@NonNull ComponentName admin) {
         try {
             return mService.getOrganizationName(admin);
         } catch (RemoteException re) {
@@ -6155,7 +6160,7 @@
      *
      * @hide
      */
-    public String getOrganizationNameForUser(int userHandle) {
+    public CharSequence getOrganizationNameForUser(int userHandle) {
         try {
             return mService.getOrganizationNameForUser(userHandle);
         } catch (RemoteException re) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6df1038..cba64c2 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -269,13 +269,13 @@
     String getWifiMacAddress(in ComponentName admin);
     void reboot(in ComponentName admin);
 
-    void setShortSupportMessage(in ComponentName admin, in String message);
-    String getShortSupportMessage(in ComponentName admin);
-    void setLongSupportMessage(in ComponentName admin, in String message);
-    String getLongSupportMessage(in ComponentName admin);
+    void setShortSupportMessage(in ComponentName admin, in CharSequence message);
+    CharSequence getShortSupportMessage(in ComponentName admin);
+    void setLongSupportMessage(in ComponentName admin, in CharSequence message);
+    CharSequence getLongSupportMessage(in ComponentName admin);
 
-    String getShortSupportMessageForUser(in ComponentName admin, int userHandle);
-    String getLongSupportMessageForUser(in ComponentName admin, int userHandle);
+    CharSequence getShortSupportMessageForUser(in ComponentName admin, int userHandle);
+    CharSequence getLongSupportMessageForUser(in ComponentName admin, int userHandle);
 
     boolean isSeparateProfileChallengeAllowed(int userHandle);
 
@@ -284,9 +284,9 @@
     int getOrganizationColor(in ComponentName admin);
     int getOrganizationColorForUser(int userHandle);
 
-    void setOrganizationName(in ComponentName admin, in String title);
-    String getOrganizationName(in ComponentName admin);
-    String getOrganizationNameForUser(int userHandle);
+    void setOrganizationName(in ComponentName admin, in CharSequence title);
+    CharSequence getOrganizationName(in ComponentName admin);
+    CharSequence getOrganizationNameForUser(int userHandle);
 
     int getUserProvisioningState();
     void setUserProvisioningState(int state, int userHandle);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 828ac38..09050b6 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -138,6 +138,20 @@
      */
     public static final int PRIORITY_TOP_APP = 40;
 
+    /**
+     * Adjustment of {@link #getPriority} if the app has often (50% or more of the time)
+     * been running jobs.
+     * @hide
+     */
+    public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40;
+
+    /**
+     * Adjustment of {@link #getPriority} if the app has always (90% or more of the time)
+     * been running jobs.
+     * @hide
+     */
+    public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80;
+
     private final int jobId;
     private final PersistableBundle extras;
     private final ComponentName service;
@@ -254,7 +268,8 @@
     }
 
     /**
-     * Flex time for this job. Only valid if this is a periodic job.
+     * Flex time for this job. Only valid if this is a periodic job.  The job can
+     * execute at any time in a window of flex length at the end of the period.
      */
     public long getFlexMillis() {
         long interval = getIntervalMillis();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7e67e8d..831de4a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -765,6 +765,19 @@
             = "android.intent.action.APPLICATION_PREFERENCES";
 
     /**
+     * Activity Action: Launch an activity showing the app information.
+     * For applications which install other applications (such as app stores), it is recommended
+     * to handle this action for providing the app information to the user.
+     *
+     * <p>Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose information needs
+     * to be displayed.
+     * <p>Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_APP_INFO
+            = "android.intent.action.SHOW_APP_INFO";
+
+    /**
      * Represents a shortcut/live folder icon resource.
      *
      * @see Intent#ACTION_CREATE_SHORTCUT
@@ -1683,9 +1696,7 @@
      * Type: String
      * </p>
      *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 104feb5..585d2a3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -366,6 +366,9 @@
      *
      * <p>NOTE: {@code WebView} does not honor this flag.
      *
+     * <p>This flag is ignored on Android N and above if an Android Network Security Config is
+     * present.
+     *
      * <p>This flag comes from
      * {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic
      * android:usesCleartextTraffic} of the &lt;application&gt; tag.
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 46321a4..430c7e7 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.ParceledListSlice;
@@ -37,7 +38,7 @@
     void addOnAppsChangedListener(String callingPackage, in IOnAppsChangedListener listener);
     void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
     ParceledListSlice getLauncherActivities(String packageName, in UserHandle user);
-    ResolveInfo resolveActivity(in Intent intent, in UserHandle user);
+    ActivityInfo resolveActivity(in ComponentName component, in UserHandle user);
     void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
     void showAppDetailsAsUser(in ComponentName component, in Rect sourceBounds,
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6fce36b..0526815 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -388,6 +388,15 @@
     void deleteApplicationCacheFiles(in String packageName, IPackageDataObserver observer);
 
     /**
+     * Delete all the cache files in an applications cache directory
+     * @param packageName The package name of the application whose cache
+     * files need to be deleted
+     * @param userId the user to delete application cache for
+     * @param observer a callback used to notify when the deletion is finished.
+     */
+    void deleteApplicationCacheFilesAsUser(in String packageName, int userId, IPackageDataObserver observer);
+
+    /**
      * Clear the user data directory of an application.
      * @param packageName The package name of the application whose cache
      * files need to be deleted
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 31d377b..9c90346 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -28,11 +28,12 @@
 
     ParceledListSlice getDynamicShortcuts(String packageName, int userId);
 
-    boolean addDynamicShortcut(String packageName, in ShortcutInfo shortcutInfo, int userId);
+    boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+            int userId);
 
-    void deleteDynamicShortcut(String packageName, in String shortcutId, int userId);
+    void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId);
 
-    void deleteAllDynamicShortcuts(String packageName, int userId);
+    void removeAllDynamicShortcuts(String packageName, int userId);
 
     ParceledListSlice getPinnedShortcuts(String packageName, int userId);
 
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 40e1a9f..2beca7b 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -39,7 +39,6 @@
 
     private ActivityInfo mActivityInfo;
     private ComponentName mComponentName;
-    private ResolveInfo mResolveInfo;
     private UserHandle mUser;
 
     /**
@@ -49,11 +48,10 @@
      * @param info ResolveInfo from which to create the LauncherActivityInfo.
      * @param user The UserHandle of the profile to which this activity belongs.
      */
-    LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user) {
+    LauncherActivityInfo(Context context, ActivityInfo info, UserHandle user) {
         this(context);
-        mResolveInfo = info;
-        mActivityInfo = info.activityInfo;
-        mComponentName = LauncherApps.getComponentName(info);
+        mActivityInfo = info;
+        mComponentName =  new ComponentName(info.packageName, info.name);
         mUser = user;
     }
 
@@ -91,7 +89,7 @@
      * @return The label for the activity.
      */
     public CharSequence getLabel() {
-        return mResolveInfo.loadLabel(mPm);
+        return mActivityInfo.loadLabel(mPm);
     }
 
     /**
@@ -103,7 +101,7 @@
      * @return The drawable associated with the activity.
      */
     public Drawable getIcon(int density) {
-        final int iconRes = mResolveInfo.getIconResourceInternal();
+        final int iconRes = mActivityInfo.getIconResource();
         Drawable icon = null;
         // Get the preferred density icon from the app's resources
         if (density != 0 && iconRes != 0) {
@@ -116,7 +114,7 @@
         }
         // Get the default density icon
         if (icon == null) {
-            icon = mResolveInfo.loadIcon(mPm);
+            icon = mActivityInfo.loadIcon(mPm);
         }
         return icon;
     }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index a0d2339..824722d 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -39,6 +39,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -291,7 +292,7 @@
         }
         ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
         for (ResolveInfo ri : activities.getList()) {
-            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
             if (DEBUG) {
                 Log.v(TAG, "Returning activity for profile " + user + " : "
                         + lai.getComponentName());
@@ -301,10 +302,6 @@
         return lais;
     }
 
-    static ComponentName getComponentName(ResolveInfo ri) {
-        return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
-    }
-
     /**
      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
      * returns null.
@@ -315,9 +312,9 @@
      */
     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
         try {
-            ResolveInfo ri = mService.resolveActivity(intent, user);
-            if (ri != null) {
-                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
+            ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user);
+            if (ai != null) {
+                LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
                 return info;
             }
         } catch (RemoteException re) {
@@ -389,6 +386,7 @@
      *
      * @return An {@link ApplicationInfo} containing information about the package or
      *         null of the package isn't found.
+     * @hide
      */
     public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
             UserHandle user) {
@@ -493,43 +491,24 @@
     }
 
     /**
-     * Return the icon resource ID, if {@code shortcut} has one
-     * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
-     *
-     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
-     * #hasShortcutHostPermission()}.
-     *
-     * @param shortcut The target shortcut.
+     * @hide kept for testing.
      */
     public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
-        return getShortcutIconResId(shortcut.getPackageName(), shortcut.getId(),
-                shortcut.getUserId());
+        return shortcut.getIconResourceId();
     }
 
     /**
-     * Return the icon resource ID, if {@code shortcut} has one
-     * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
-     *
-     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
-     * #hasShortcutHostPermission()}.
-     *
-     * @param packageName The target package name.
-     * @param shortcutId The ID of the shortcut to lad rom.
-     * @param user The UserHandle of the profile.
+     * @hide kept for testing.
      */
     public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
             @NonNull UserHandle user) {
-        return getShortcutIconResId(packageName, shortcutId, user.getIdentifier());
-    }
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setPackage(packageName);
+        q.setShortcutIds(Arrays.asList(shortcutId));
+        q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+        final List<ShortcutInfo> shortcuts = getShortcuts(q, user);
 
-    private int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
-            int userId) {
-        try {
-            return mService.getShortcutIconResId(mContext.getPackageName(),
-                    packageName, shortcutId, userId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 39bc783..ade2248 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4972,6 +4972,27 @@
             IPackageDataObserver observer);
 
     /**
+     * Attempts to delete the cache files associated with an application for a given user. Since
+     * this may take a little while, the result will be posted back to the given observer. A
+     * deletion will fail if the calling context lacks the
+     * {@link android.Manifest.permission#DELETE_CACHE_FILES} permission, if the named package
+     * cannot be found, or if the named package is a "system package". If {@code userId} does not
+     * belong to the calling user, the caller must have
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+     *
+     * @param packageName The name of the package to delete
+     * @param userId the user for which the cache files needs to be deleted
+     * @param observer An observer callback to get notified when the cache file deletion is
+     *            complete.
+     *            {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
+     *            will be called when that happens. observer may be null to indicate that no
+     *            callback is desired.
+     * @hide
+     */
+    public abstract void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+            IPackageDataObserver observer);
+
+    /**
      * Free storage by deleting LRU sorted list of cache files across
      * all applications. If the currently available free storage
      * on the device is greater than or equal to the requested
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 7a807c4..a900015 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -34,6 +34,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 // TODO Enhance javadoc
 /**
@@ -107,6 +109,11 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface CloneFlags {}
 
+    /**
+     * Shortcut category for
+     */
+    public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+
     private final String mId;
 
     @NonNull
@@ -124,6 +131,9 @@
     @Nullable
     private String mText;
 
+    @NonNull
+    private List<String> mCategories;
+
     /**
      * Intent *with extras removed*.
      */
@@ -168,6 +178,7 @@
         mIcon = b.mIcon;
         mTitle = b.mTitle;
         mText = b.mText;
+        mCategories = clone(b.mCategories);
         mIntent = b.mIntent;
         if (mIntent != null) {
             final Bundle intentExtras = mIntent.getExtras();
@@ -181,6 +192,10 @@
         updateTimestamp();
     }
 
+    private <T> ArrayList<T> clone(List<T> source) {
+        return (source == null) ? null : new ArrayList<>(source);
+    }
+
     /**
      * Throws if any of the mandatory fields is not set.
      *
@@ -202,17 +217,20 @@
         mFlags = source.mFlags;
         mLastChangedTimestamp = source.mLastChangedTimestamp;
 
+        // Just always keep it since it's cheep.
+        mIconResourceId = source.mIconResourceId;
+
         if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
             mActivityComponent = source.mActivityComponent;
 
             if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
                 mIcon = source.mIcon;
                 mBitmapPath = source.mBitmapPath;
-                mIconResourceId = source.mIconResourceId;
             }
 
             mTitle = source.mTitle;
             mText = source.mText;
+            mCategories = clone(source.mCategories);
             if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
                 mIntent = source.mIntent;
                 mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -262,6 +280,9 @@
         if (source.mText != null) {
             mText = source.mText;
         }
+        if (source.mCategories != null) {
+            mCategories = clone(source.mCategories);
+        }
         if (source.mIntent != null) {
             mIntent = source.mIntent;
             mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -325,6 +346,8 @@
 
         private String mText;
 
+        private List<String> mCategories;
+
         private Intent mIntent;
 
         private int mWeight;
@@ -369,8 +392,9 @@
          *
          * <p>For performance reasons, icons will <b>NOT</b> be available on instances
          * returned by {@link ShortcutManager} or {@link LauncherApps}.  Launcher applications
-         * need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo)}
-         * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo)}.
+         * can use {@link ShortcutInfo#getIconResourceId()} if {@link #hasIconResource()} is true.
+         * Otherwise, if {@link #hasIconFile()} is true, use
+         * {@link LauncherApps#getShortcutIconFd} to load the image.
          */
         @NonNull
         public Builder setIcon(Icon icon) {
@@ -403,6 +427,18 @@
         }
 
         /**
+         * Sets categories for a shortcut.  Launcher applications may use this information to
+         * categorise shortcuts.
+         *
+         * @see #SHORTCUT_CATEGORY_CONVERSATION
+         */
+        @NonNull
+        public Builder setCategories(List<String> categories) {
+            mCategories = categories;
+            return this;
+        }
+
+        /**
          * Sets the intent of a shortcut.  This is a mandatory field.  The extras must only contain
          * persistable information.  (See {@link PersistableBundle}).
          */
@@ -500,6 +536,14 @@
     }
 
     /**
+     * Return the categories.
+     */
+    @Nullable
+    public List<String> getCategories() {
+        return mCategories;
+    }
+
+    /**
      * Return the intent.
      *
      * <p>All shortcuts must have an intent, but this method will return null when
@@ -662,7 +706,9 @@
         mIconResourceId = iconResourceId;
     }
 
-    /** @hide */
+    /**
+     * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
+     */
     public int getIconResourceId() {
         return mIconResourceId;
     }
@@ -687,6 +733,8 @@
         mIcon = source.readParcelable(cl);
         mTitle = source.readString();
         mText = source.readString();
+        mCategories = new ArrayList<>();
+        source.readStringList(mCategories);
         mIntent = source.readParcelable(cl);
         mIntentPersistableExtras = source.readParcelable(cl);
         mWeight = source.readInt();
@@ -706,6 +754,7 @@
         dest.writeParcelable(mIcon, flags);
         dest.writeString(mTitle);
         dest.writeString(mText);
+        dest.writeStringList(mCategories);
         dest.writeParcelable(mIntent, flags);
         dest.writeParcelable(mIntentPersistableExtras, flags);
         dest.writeInt(mWeight);
@@ -770,6 +819,9 @@
         sb.append(", text=");
         sb.append(secure ? "***" : mText);
 
+        sb.append(", categories=");
+        sb.append(mCategories);
+
         sb.append(", icon=");
         sb.append(mIcon);
 
@@ -807,7 +859,7 @@
     /** @hide */
     public ShortcutInfo(
             @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
-            Icon icon, String title, String text, Intent intent,
+            Icon icon, String title, String text, List<String> categories, Intent intent,
             PersistableBundle intentPersistableExtras,
             int weight, PersistableBundle extras, long lastChangedTimestamp,
             int flags, int iconResId, String bitmapPath) {
@@ -818,6 +870,7 @@
         mIcon = icon;
         mTitle = title;
         mText = text;
+        mCategories = clone(categories);
         mIntent = intent;
         mIntentPersistableExtras = intentPersistableExtras;
         mWeight = weight;
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 919ccda..75803d3 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -34,11 +34,11 @@
  * <h3>Dynamic shortcuts and pinned shortcuts</h3>
  *
  * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and
- * {@link #addDynamicShortcut(ShortcutInfo)}.  There can be at most
+ * {@link #addDynamicShortcuts(List)}.  There can be at most
  * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same
  * application.
- * A dynamic shortcut can be deleted with {@link #deleteDynamicShortcut(String)}, and apps
- * can also use {@link #deleteAllDynamicShortcuts()} to delete all dynamic shortcuts.
+ * A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps
+ * can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts.
  *
  * <p>The shortcuts that are currently published by the above APIs are called "dynamic", because
  * they can be removed by the creator application at any time.  The user may "pin" dynamic shortcuts
@@ -61,11 +61,11 @@
  *
  * <h3>Rate limiting</h3>
  *
- * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcut(ShortcutInfo)},
+ * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)},
  * and {@link #updateShortcuts(List)} will be
  * rate-limited.  An application can call these methods at most
  * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
- * which happens at a certain time every day.
+ * which happens every hour.
  *
  * <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time.
  *
@@ -153,10 +153,10 @@
      * @throws IllegalArgumentException if the caller application has already published the
      * max number of dynamic shortcuts.
      */
-    public boolean addDynamicShortcut(@NonNull ShortcutInfo shortcutInfo) {
+    public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
         try {
-            return mService.addDynamicShortcut(
-                    mContext.getPackageName(), shortcutInfo, injectMyUserId());
+            return mService.addDynamicShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -165,9 +165,10 @@
     /**
      * Delete a single dynamic shortcut by ID.
      */
-    public void deleteDynamicShortcut(@NonNull String shortcutId) {
+    public void removeDynamicShortcuts(@NonNull List<String> shortcutIds) {
         try {
-            mService.deleteDynamicShortcut(mContext.getPackageName(), shortcutId, injectMyUserId());
+            mService.removeDynamicShortcuts(mContext.getPackageName(), shortcutIds,
+                    injectMyUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -176,9 +177,9 @@
     /**
      * Delete all dynamic shortcuts from the caller application.
      */
-    public void deleteAllDynamicShortcuts() {
+    public void removeAllDynamicShortcuts() {
         try {
-            mService.deleteAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
+            mService.removeAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 79eff4f..98a8904 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1590,16 +1590,16 @@
             new Key<Integer>("android.control.videoStabilizationMode", int.class);
 
     /**
-     * <p>The amount of additional sesnsitivity boost applied to output images
+     * <p>The amount of additional sensitivity boost applied to output images
      * after RAW sensor data is captured.</p>
      * <p>Some camera devices support additional digital sensitivity boosting in the
      * camera processing pipeline after sensor RAW image is captured.
      * Such a boost will be applied to YUV/JPEG format output images but will not
      * have effect on RAW output formats like RAW_SENSOR, RAW10, RAW12 or RAW_OPAQUE.</p>
-     * <p>This key is optional. Applications can assume there is no boost applied
-     * after RAW is captured if this key is not available.
-     * When this key is available, the sensitivity boost value must be within
-     * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p>
+     * <p>This key will be <code>null</code> for devices that do not support any RAW format
+     * outputs. For devices that do support RAW format outputs, this key will always
+     * present, and if a device does not support post RAW sensitivity boost, it will
+     * list <code>100</code> in this key.</p>
      * <p>If the camera device cannot apply the exact boost requested, it will reduce the
      * boost to the nearest supported value.
      * The final boost value used will be available in the output capture result.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 4f41e1c..ddf90a8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2097,16 +2097,16 @@
             new Key<Integer>("android.control.videoStabilizationMode", int.class);
 
     /**
-     * <p>The amount of additional sesnsitivity boost applied to output images
+     * <p>The amount of additional sensitivity boost applied to output images
      * after RAW sensor data is captured.</p>
      * <p>Some camera devices support additional digital sensitivity boosting in the
      * camera processing pipeline after sensor RAW image is captured.
      * Such a boost will be applied to YUV/JPEG format output images but will not
      * have effect on RAW output formats like RAW_SENSOR, RAW10, RAW12 or RAW_OPAQUE.</p>
-     * <p>This key is optional. Applications can assume there is no boost applied
-     * after RAW is captured if this key is not available.
-     * When this key is available, the sensitivity boost value must be within
-     * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p>
+     * <p>This key will be <code>null</code> for devices that do not support any RAW format
+     * outputs. For devices that do support RAW format outputs, this key will always
+     * present, and if a device does not support post RAW sensitivity boost, it will
+     * list <code>100</code> in this key.</p>
      * <p>If the camera device cannot apply the exact boost requested, it will reduce the
      * boost to the nearest supported value.
      * The final boost value used will be available in the output capture result.</p>
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 18a155d..b542339 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -501,11 +501,8 @@
             Log.d(TAG, "createCaptureSessionByOutputConfiguration");
         }
 
-        // OutputConfiguration objects aren't immutable, make a copy before using.
-        List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>();
-        for (OutputConfiguration output : outputConfigurations) {
-            currentOutputs.add(new OutputConfiguration(output));
-        }
+        // OutputConfiguration objects are immutable, but need to have our own array
+        List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
 
         createCaptureSessionInternal(null, currentOutputs, callback, handler,
                 /*isConstrainedHighSpeed*/false);
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index cd0c474..61b534b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -17,6 +17,8 @@
 
 package android.hardware.camera2.params;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.utils.HashCodeHelpers;
@@ -71,12 +73,12 @@
     public static final int ROTATION_270 = 3;
 
     /**
-     * Invalid surface set ID.
+     * Invalid surface group ID.
      *
      *<p>An {@link OutputConfiguration} with this value indicates that the included surface
-     *doesn't belong to any surface set.</p>
+     *doesn't belong to any surface group.</p>
      */
-    public static final int SURFACE_SET_ID_INVALID = -1;
+    public static final int SURFACE_GROUP_ID_NONE = -1;
 
     /**
      * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
@@ -84,11 +86,47 @@
      * @param surface
      *          A Surface for camera to output to.
      *
-     * <p>This constructor creates a default configuration.</p>
+     * <p>This constructor creates a default configuration, with a surface group ID of
+     * {@value #SURFACE_GROUP_ID_NONE}.</p>
      *
      */
-    public OutputConfiguration(Surface surface) {
-        this(surface, ROTATION_0);
+    public OutputConfiguration(@NonNull Surface surface) {
+        this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
+    }
+
+    /**
+     * Create a new {@link OutputConfiguration} instance with a {@link Surface},
+     * with a surface group ID.
+     *
+     * <p>
+     * A surface group ID is used to identify which surface group this output surface belongs to. A
+     * surface group is a group of output surfaces that are not intended to receive camera output
+     * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
+     * by all the surfaces from the same surface group, therefore may reduce the overall memory
+     * footprint. The application should only set the same set ID for the streams that are not
+     * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
+     * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
+     *
+     * <p>For example, a video chat application that has an adaptive output resolution feature would
+     * need two (or more) output resolutions, to switch resolutions without any output glitches.
+     * However, at any given time, only one output is active to minimize outgoing network bandwidth
+     * and encoding overhead.  To save memory, the application should set the video outputs to have
+     * the same non-negative group ID, so that the camera device can share the same memory region
+     * for the alternating outputs.</p>
+     *
+     * <p>It is not an error to include output streams with the same group ID in the same capture
+     * request, but the resulting memory consumption may be higher than if the two streams were
+     * not in the same surface group to begin with, especially if the outputs have substantially
+     * different dimensions.</p>
+     *
+     * @param surfaceGroupId
+     *          A group ID for this output, used for sharing memory between multiple outputs.
+     * @param surface
+     *          A Surface for camera to output to.
+     *
+     */
+    public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
+        this(surfaceGroupId, surface, ROTATION_0);
     }
 
     /**
@@ -100,9 +138,9 @@
      *          A Surface for camera to output to.
      * @param rotation
      *          The desired rotation to be applied on camera output. Value must be one of
-     *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degree,
+     *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
      *          application should make sure corresponding surface size has width and height
-     *          transposed corresponding to the width and height without rotation. For example,
+     *          transposed relative to the width and height without rotation. For example,
      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
      *          application should set rotation to {@code ROTATION_90} and make sure the
      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
@@ -110,15 +148,43 @@
      * @hide
      */
     @SystemApi
-    public OutputConfiguration(Surface surface, int rotation) {
+    public OutputConfiguration(@NonNull Surface surface, int rotation) {
+        this(SURFACE_GROUP_ID_NONE, surface, rotation);
+    }
+
+
+    /**
+     * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
+     *
+     * <p>This constructor takes an argument for desired camera rotation and for the surface group
+     * ID.  See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</p>
+     *
+     * @param surfaceGroupId
+     *          A group ID for this output, used for sharing memory between multiple outputs.
+     * @param surface
+     *          A Surface for camera to output to.
+     * @param rotation
+     *          The desired rotation to be applied on camera output. Value must be one of
+     *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
+     *          application should make sure corresponding surface size has width and height
+     *          transposed relative to the width and height without rotation. For example,
+     *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
+     *          application should set rotation to {@code ROTATION_90} and make sure the
+     *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
+     *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
+     * @hide
+     */
+    @SystemApi
+    public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
         checkNotNull(surface, "Surface must not be null");
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
-        mSurfaceSetId = SURFACE_SET_ID_INVALID;
+        mSurfaceGroupId = surfaceGroupId;
         mSurface = surface;
         mRotation = rotation;
         mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
         mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
         mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
+        mConfiguredGenerationId = surface.getGenerationId();
     }
 
     /**
@@ -129,35 +195,36 @@
      *
      * @hide
      */
-    @SystemApi
-    public OutputConfiguration(OutputConfiguration other) {
+    public OutputConfiguration(@NonNull OutputConfiguration other) {
         if (other == null) {
             throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
         }
 
         this.mSurface = other.mSurface;
         this.mRotation = other.mRotation;
-        this.mSurfaceSetId = other.mSurfaceSetId;
+        this.mSurfaceGroupId = other.mSurfaceGroupId;
         this.mConfiguredDataspace = other.mConfiguredDataspace;
         this.mConfiguredFormat = other.mConfiguredFormat;
         this.mConfiguredSize = other.mConfiguredSize;
+        this.mConfiguredGenerationId = other.mConfiguredGenerationId;
     }
 
     /**
      * Create an OutputConfiguration from Parcel.
      */
-    private OutputConfiguration(Parcel source) {
+    private OutputConfiguration(@NonNull Parcel source) {
         int rotation = source.readInt();
         int surfaceSetId = source.readInt();
         Surface surface = Surface.CREATOR.createFromParcel(source);
         checkNotNull(surface, "Surface must not be null");
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
-        mSurfaceSetId = surfaceSetId;
+        mSurfaceGroupId = surfaceSetId;
         mSurface = surface;
         mRotation = rotation;
         mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
         mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
         mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
+        mConfiguredGenerationId = mSurface.getGenerationId();
     }
 
     /**
@@ -165,6 +232,7 @@
      *
      * @return the {@link Surface} associated with this {@link OutputConfiguration}.
      */
+    @NonNull
     public Surface getSurface() {
         return mSurface;
     }
@@ -183,35 +251,13 @@
     }
 
     /**
-     * Set the surface set ID to this {@link OutputConfiguration}.
+     * Get the surface group ID associated with this {@link OutputConfiguration}.
      *
-     * <p>
-     * A surface set ID is used to identify which surface set this output surface belongs to. A
-     * surface set is a group of output surfaces that are not intended to receive camera output
-     * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
-     * by all the surfaces from the same surface set, therefore may save the overall memory
-     * footprint. The application should only set the same set ID for the streams that are not
-     * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
-     * surface set. The default value will be {@value #SURFACE_SET_ID_INVALID}.
-     * </p>
-     *
-     * @param setId
+     * @return the surface group ID associated with this {@link OutputConfiguration}.
+     *         The default value is {@value #SURFACE_GROUP_ID_NONE}.
      */
-    public void setSurfaceSetId(int setId) {
-        if (setId < 0) {
-            setId = SURFACE_SET_ID_INVALID;
-        }
-        mSurfaceSetId = setId;
-    }
-
-    /**
-     * Get the surface set Id associated with this {@link OutputConfiguration}.
-     *
-     * @return the surface set Id associated with this {@link OutputConfiguration}.
-     *         Value will be one of ROTATION_[0, 90, 180, 270]
-     */
-    public int getSurfaceSetId() {
-        return mSurfaceSetId;
+    public int getSurfaceGroupId() {
+        return mSurfaceGroupId;
     }
 
     public static final Parcelable.Creator<OutputConfiguration> CREATOR =
@@ -244,7 +290,7 @@
             throw new IllegalArgumentException("dest must not be null");
         }
         dest.writeInt(mRotation);
-        dest.writeInt(mSurfaceSetId);
+        dest.writeInt(mSurfaceGroupId);
         mSurface.writeToParcel(dest, flags);
     }
 
@@ -265,12 +311,13 @@
             return true;
         } else if (obj instanceof OutputConfiguration) {
             final OutputConfiguration other = (OutputConfiguration) obj;
-            return mSurface == other.mSurface &&
-                   mRotation == other.mRotation &&
+            return mRotation == other.mRotation &&
+                   mSurface == other.mSurface &&
+                   mConfiguredGenerationId == other.mConfiguredGenerationId &&
                    mConfiguredSize.equals(other.mConfiguredSize) &&
                    mConfiguredFormat == other.mConfiguredFormat &&
                    mConfiguredDataspace == other.mConfiguredDataspace &&
-                   mSurfaceSetId == other.mSurfaceSetId;
+                   mSurfaceGroupId == other.mSurfaceGroupId;
         }
         return false;
     }
@@ -280,16 +327,20 @@
      */
     @Override
     public int hashCode() {
-        return HashCodeHelpers.hashCode(mSurface.hashCode(), mRotation);
+        return HashCodeHelpers.hashCode(
+            mRotation, mSurface.hashCode(), mConfiguredGenerationId,
+            mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
     }
 
     private static final String TAG = "OutputConfiguration";
     private final Surface mSurface;
     private final int mRotation;
-    private int mSurfaceSetId;
+    private int mSurfaceGroupId;
 
     // The size, format, and dataspace of the surface when OutputConfiguration is created.
     private final Size mConfiguredSize;
     private final int mConfiguredFormat;
     private final int mConfiguredDataspace;
+    // Surface generation ID to distinguish changes to Surface native internals
+    private final int mConfiguredGenerationId;
 }
diff --git a/core/java/android/inputmethodservice/CompactExtractEditLayout.java b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
new file mode 100644
index 0000000..35c54b2
--- /dev/null
+++ b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
@@ -0,0 +1,118 @@
+/*
+ * 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.inputmethodservice;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.annotation.FractionRes;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * A special purpose layout for the editor extract view for tiny (sub 250dp) screens.
+ * The layout is based on sizes proportional to screen pixel size to provide for the
+ * best layout fidelity on varying pixel sizes and densities.
+ *
+ * @hide
+ */
+public class CompactExtractEditLayout extends LinearLayout {
+    private View mInputExtractEditText;
+    private View mInputExtractAccessories;
+    private View mInputExtractAction;
+    private boolean mPerformLayoutChanges;
+
+    public CompactExtractEditLayout(Context context) {
+        super(context);
+    }
+
+    public CompactExtractEditLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CompactExtractEditLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mInputExtractEditText = findViewById(com.android.internal.R.id.inputExtractEditText);
+        mInputExtractAccessories = findViewById(com.android.internal.R.id.inputExtractAccessories);
+        mInputExtractAction = findViewById(com.android.internal.R.id.inputExtractAction);
+
+        if (mInputExtractEditText != null && mInputExtractAccessories != null
+                && mInputExtractAction != null) {
+            mPerformLayoutChanges = true;
+        }
+    }
+
+    private int applyFractionInt(@FractionRes int fraction, int whole) {
+        return Math.round(getResources().getFraction(fraction, whole, whole));
+    }
+
+    private static void setLayoutHeight(View v, int px) {
+        ViewGroup.LayoutParams lp = v.getLayoutParams();
+        lp.height = px;
+        v.setLayoutParams(lp);
+    }
+
+    private static void setLayoutMarginBottom(View v, int px) {
+        ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+        lp.bottomMargin = px;
+        v.setLayoutParams(lp);
+    }
+
+    private void applyProportionalLayout(int screenWidthPx, int screenHeightPx) {
+        if (getResources().getConfiguration().isScreenRound()) {
+            setGravity(Gravity.BOTTOM);
+        }
+        setLayoutHeight(this, applyFractionInt(
+                com.android.internal.R.fraction.input_extract_layout_height, screenHeightPx));
+
+        setPadding(
+                applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_left,
+                        screenWidthPx),
+                0,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_right,
+                        screenWidthPx),
+                0);
+
+        setLayoutMarginBottom(mInputExtractEditText,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_text_margin_bottom,
+                        screenHeightPx));
+
+        setLayoutMarginBottom(mInputExtractAccessories,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_action_margin_bottom,
+                        screenHeightPx));
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mPerformLayoutChanges) {
+            Resources res = getResources();
+            DisplayMetrics dm = res.getDisplayMetrics();
+            int heightPixels = dm.heightPixels;
+            int widthPixels = dm.widthPixels;
+            applyProportionalLayout(widthPixels, heightPixels);
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index cc201bc..085b97c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -68,9 +68,10 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.ImageButton;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -302,7 +303,7 @@
     boolean mExtractViewHidden;
     ExtractEditText mExtractEditText;
     ViewGroup mExtractAccessories;
-    Button mExtractAction;
+    View mExtractAction;
     ExtractedText mExtractedText;
     int mExtractedToken;
     
@@ -1344,7 +1345,7 @@
             mExtractEditText = (ExtractEditText)view.findViewById(
                     com.android.internal.R.id.inputExtractEditText);
             mExtractEditText.setIME(this);
-            mExtractAction = (Button)view.findViewById(
+            mExtractAction = view.findViewById(
                     com.android.internal.R.id.inputExtractAction);
             if (mExtractAction != null) {
                 mExtractAccessories = (ViewGroup)view.findViewById(
@@ -2408,7 +2409,35 @@
                 return getText(com.android.internal.R.string.ime_action_default);
         }
     }
-    
+
+    /**
+     * Return a drawable resource id that can be used as a button icon for the given
+     * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
+     * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
+     * @return Returns a drawable resource id to use.
+     */
+    @DrawableRes
+    private int getIconForImeAction(int imeOptions) {
+        switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
+            case EditorInfo.IME_ACTION_GO:
+                return com.android.internal.R.drawable.ic_input_extract_action_go;
+            case EditorInfo.IME_ACTION_SEARCH:
+                return com.android.internal.R.drawable.ic_input_extract_action_search;
+            case EditorInfo.IME_ACTION_SEND:
+                return com.android.internal.R.drawable.ic_input_extract_action_send;
+            case EditorInfo.IME_ACTION_NEXT:
+                return com.android.internal.R.drawable.ic_input_extract_action_next;
+            case EditorInfo.IME_ACTION_DONE:
+                return com.android.internal.R.drawable.ic_input_extract_action_done;
+            case EditorInfo.IME_ACTION_PREVIOUS:
+                return com.android.internal.R.drawable.ic_input_extract_action_previous;
+            default:
+                return com.android.internal.R.drawable.ic_input_extract_action_return;
+        }
+    }
+
     /**
      * Called when the fullscreen-mode extracting editor info has changed,
      * to determine whether the extracting (extract text and candidates) portion
@@ -2459,10 +2488,20 @@
         if (hasAction) {
             mExtractAccessories.setVisibility(View.VISIBLE);
             if (mExtractAction != null) {
-                if (ei.actionLabel != null) {
-                    mExtractAction.setText(ei.actionLabel);
+                if (mExtractAction instanceof ImageButton) {
+                    ((ImageButton) mExtractAction)
+                            .setImageResource(getIconForImeAction(ei.imeOptions));
+                    if (ei.actionLabel != null) {
+                        mExtractAction.setContentDescription(ei.actionLabel);
+                    } else {
+                        mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
+                    }
                 } else {
-                    mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+                    if (ei.actionLabel != null) {
+                        ((TextView) mExtractAction).setText(ei.actionLabel);
+                    } else {
+                        ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
+                    }
                 }
                 mExtractAction.setOnClickListener(mActionClickListener);
             }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a025337..5d008e3 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2734,7 +2734,7 @@
     private void incCallbackHandlerRefCount() {
         synchronized(sCallbackRefCount) {
             if (sCallbackRefCount.incrementAndGet() == 1) {
-                // TODO - switch this over to a ManagerThread or expire it when done
+                // TODO: switch this to ConnectivityThread
                 HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
                 callbackThread.start();
                 sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
@@ -3093,6 +3093,10 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        synchronized (sNetworkCallback) {
+            sNetworkCallback.remove(networkCallback.networkRequest);
+        }
     }
 
     /**
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 20c2168..2dacf8f 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -120,12 +120,17 @@
      * either a bad network configuration (no internet link) or captive portal.
      *
      * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+     * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
+     *       representing URL that Internet probe was redirect to, if it was redirected,
+     *       or mapping to {@code null} otherwise.
      */
     public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
 
     public static final int VALID_NETWORK = 1;
     public static final int INVALID_NETWORK = 2;
 
+    public static String REDIRECT_URL_KEY = "redirect URL";
+
      /**
      * Sent by the NetworkAgent to ConnectivityService to indicate this network was
      * explicitly selected.  This should be sent before the NetworkInfo is marked
@@ -283,11 +288,12 @@
                 break;
             }
             case CMD_REPORT_NETWORK_STATUS: {
+                String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
                 if (VDBG) {
                     log("CMD_REPORT_NETWORK_STATUS(" +
-                            (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
+                            (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
                 }
-                networkStatus(msg.arg1);
+                networkStatus(msg.arg1, redirectUrl);
                 break;
             }
             case CMD_SAVE_ACCEPT_UNVALIDATED: {
@@ -443,8 +449,12 @@
      *
      * This may be called multiple times as the network status changes and may
      * generate false negatives if we lose ip connectivity before the link is torn down.
+     *
+     * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
+     * @param redirectUrl If the Internet probe was redirected, this is the destination it was
+     *         redirected to, otherwise {@code null}.
      */
-    protected void networkStatus(int status) {
+    protected void networkStatus(int status, String redirectUrl) {
     }
 
     /**
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index cfd0468..1ac9fca 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -228,7 +228,6 @@
      * statistics parameters.
      *
      * @see #setThreadStatsTag(int)
-     * @see #setThreadStatsUid(int)
      */
     public static void tagSocket(Socket socket) throws SocketException {
         SocketTagger.get().tag(socket);
@@ -249,7 +248,6 @@
      * parameters.
      *
      * @see #setThreadStatsTag(int)
-     * @see #setThreadStatsUid(int)
      */
     public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
         SocketTagger.get().tag(socket);
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index 6729347..87a0b70 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -44,6 +44,7 @@
     private final X509TrustManager mTrustManager;
     private final Method mCheckServerTrusted;
     private final Method mIsUserAddedCertificate;
+    private final Method mIsSameTrustConfiguration;
 
     /**
      * Constructs a new X509TrustManagerExtensions wrapper.
@@ -57,6 +58,7 @@
             mTrustManager = null;
             mCheckServerTrusted = null;
             mIsUserAddedCertificate = null;
+            mIsSameTrustConfiguration = null;
             return;
         }
         // Use duck typing if possible.
@@ -80,6 +82,15 @@
             throw new IllegalArgumentException(
                     "Required method isUserAddedCertificate(X509Certificate) missing");
         }
+        // Get the option isSameTrustConfiguration method.
+        Method isSameTrustConfiguration = null;
+        try {
+            isSameTrustConfiguration = tm.getClass().getMethod("isSameTrustConfiguration",
+                    String.class,
+                    String.class);
+        } catch (ReflectiveOperationException ignored) {
+        }
+        mIsSameTrustConfiguration = isSameTrustConfiguration;
     }
 
     /**
@@ -150,6 +161,19 @@
      */
     @SystemApi
     public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
-        return true;
+        if (mIsSameTrustConfiguration == null) {
+            return true;
+        }
+        try {
+            return (Boolean) mIsSameTrustConfiguration.invoke(mTrustManager, hostname1, hostname2);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException("Failed to call isSameTrustConfiguration", e);
+        } catch (InvocationTargetException e) {
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new RuntimeException("isSameTrustConfiguration failed", e.getCause());
+            }
+        }
     }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c452837..773e7dd 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3094,14 +3094,8 @@
             dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA,
                     u.getWifiControllerActivity(), which);
 
-            // Dump Bluetooth scan data, per UID.
-            final long bleScanTimeUs = u.getBluetoothScanTimer().getTotalTimeLocked(
+            dumpTimer(pw, uid, category, BLUETOOTH_MISC_DATA, u.getBluetoothScanTimer(),
                     rawRealtime, which);
-            final int bleScanCount = u.getBluetoothScanTimer().getCountLocked(which);
-            if (bleScanTimeUs != 0 || bleScanCount != 0) {
-                dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA,
-                        bleScanTimeUs / 1000, bleScanCount);
-            }
 
             dumpControllerActivityLine(pw, uid, category, BLUETOOTH_CONTROLLER_DATA,
                     u.getBluetoothControllerActivity(), which);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index ece1228..0a8fdd9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -824,7 +824,9 @@
         }
     }
 
-    /** {@hide} */
+    /**
+     * Return the {@link StorageVolume} that contains the given file, or {@code null} if none.
+     */
     public @Nullable StorageVolume getStorageVolume(File file) {
         return getStorageVolume(getVolumeList(), file);
     }
@@ -836,9 +838,13 @@
 
     /** {@hide} */
     private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
+        if (file == null) {
+            return null;
+        }
         try {
             file = file.getCanonicalFile();
         } catch (IOException ignored) {
+            Slog.d(TAG, "Could not get canonical path for " + file);
             return null;
         }
         for (StorageVolume volume : volumes) {
@@ -1056,8 +1062,8 @@
     }
 
     /**
-     * Return if data stored at the given path will be encrypted while at rest.
-     * This can help apps avoid the overhead of double-encrypting data.
+     * Return if data stored at or under the given path will be encrypted while
+     * at rest. This can help apps avoid the overhead of double-encrypting data.
      */
     public boolean isEncrypted(File file) {
         if (FileUtils.contains(Environment.getDataDirectory(), file)) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 89ac27c9a..de19f81 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -283,7 +283,7 @@
      *
      * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
      * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
-     * is not granted, then atempting to use this action will result in a {@link
+     * is not granted, then attempting to use this action will result in a {@link
      * java.lang.SecurityException}.
      *
      *  @see #EXTRA_OUTPUT
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 638be51..788a20c 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -296,7 +296,8 @@
      * monitoring of encrypted network traffic.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
-     * safeguard against this.
+     * safeguard against this. Add {@link #EXTRA_NUMBER_OF_CERTIFICATES} extra to indicate the
+     * number of certificates.
      * <p>
      * Input: Nothing.
      * <p>
@@ -1369,6 +1370,16 @@
     public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES =
             "android.settings.extra.do_not_disturb_mode_minutes";
 
+    /**
+     * Activity Extra: Number of certificates
+     * <p>
+     * This can be passed as an extra field to the {@link #ACTION_MONITORING_CERT_INFO}
+     * intent to indicate the number of certificates
+     * @hide
+     */
+    public static final String EXTRA_NUMBER_OF_CERTIFICATES =
+            "android.settings.extra.number_of_certificates";
+
     private static final String JID_RESOURCE_PREFIX = "android";
 
     public static final String AUTHORITY = "settings";
@@ -1581,6 +1592,8 @@
     public static final class System extends NameValueTable {
         public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
 
+        private static final float DEFAULT_FONT_SCALE = 1.0f;
+
         /** @hide */
         public static interface Validator {
             public boolean validate(String value);
@@ -2089,9 +2102,9 @@
         public static void getConfigurationForUser(ContentResolver cr, Configuration outConfig,
                 int userHandle) {
             outConfig.fontScale = Settings.System.getFloatForUser(
-                cr, FONT_SCALE, outConfig.fontScale, userHandle);
+                    cr, FONT_SCALE, DEFAULT_FONT_SCALE, userHandle);
             if (outConfig.fontScale < 0) {
-                outConfig.fontScale = 1;
+                outConfig.fontScale = DEFAULT_FONT_SCALE;
             }
             outConfig.setLocales(LocaleList.forLanguageTags(
                     Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle)));
@@ -7793,13 +7806,13 @@
          * ShortcutManager specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
-         * "reset_interval_sec=86400,max_daily_updates=5"
+         * "reset_interval_sec=86400,max_updates_per_interval=1"
          *
          * The following keys are supported:
          *
          * <pre>
          * reset_interval_sec              (long)
-         * max_daily_updates               (int)
+         * max_updates_per_interval        (int)
          * max_icon_dimension_dp           (int, DP)
          * max_icon_dimension_dp_lowram    (int, DP)
          * max_shortcuts                   (int)
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index 19f6887..859e022 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -148,4 +148,15 @@
         NetworkSecurityConfig config = mConfig.getConfigForHostname("");
         return config.getTrustManager().getAcceptedIssuers();
     }
+
+    /**
+     * Returns {@code true} if this trust manager uses the same trust configuration for the provided
+     * hostnames.
+     *
+     * <p>This is required by android.net.http.X509TrustManagerExtensions.
+     */
+    public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
+        return mConfig.getConfigForHostname(hostname1)
+                .equals(mConfig.getConfigForHostname(hostname2));
+    }
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 94ce57a..239f2d0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -683,7 +683,7 @@
                 // interface.
                 int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
                 int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
-                int indentsLen = Math.max(1, Math.min(leftLen, rightLen) - mLineCount);
+                int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
                 int[] indents = new int[indentsLen];
                 for (int i = 0; i < indentsLen; i++) {
                     int leftMargin = mLeftIndents == null ? 0 :
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 3564e11..e93e58d 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -48,6 +48,9 @@
         implements KeyListener {
     /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
 
+    private static final int LINE_FEED = 0x0A;
+    private static final int CARRIAGE_RETURN = 0x0D;
+
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
@@ -110,34 +113,37 @@
         // Initial state
         final int STATE_START = 0;
 
+        // The offset is immediately before line feed.
+        final int STATE_LF = 1;
+
         // The offset is immediately before a KEYCAP.
-        final int STATE_BEFORE_KEYCAP = 1;
+        final int STATE_BEFORE_KEYCAP = 2;
         // The offset is immediately before a variation selector and a KEYCAP.
-        final int STATE_BEFORE_VS_AND_KEYCAP = 2;
+        final int STATE_BEFORE_VS_AND_KEYCAP = 3;
 
         // The offset is immediately before an emoji modifier.
-        final int STATE_BEFORE_EMOJI_MODIFIER = 3;
+        final int STATE_BEFORE_EMOJI_MODIFIER = 4;
         // The offset is immediately before a variation selector and an emoji modifier.
-        final int STATE_BEFORE_VS_AND_EMOJI_MODIFIER = 4;
+        final int STATE_BEFORE_VS_AND_EMOJI_MODIFIER = 5;
 
         // The offset is immediately before a variation selector.
-        final int STATE_BEFORE_VS = 5;
+        final int STATE_BEFORE_VS = 6;
 
         // The offset is immediately before a ZWJ emoji.
-        final int STATE_BEFORE_ZWJ_EMOJI = 6;
+        final int STATE_BEFORE_ZWJ_EMOJI = 7;
         // The offset is immediately before a ZWJ that were seen before a ZWJ emoji.
-        final int STATE_BEFORE_ZWJ = 7;
+        final int STATE_BEFORE_ZWJ = 8;
         // The offset is immediately before a variation selector and a ZWJ that were seen before a
         // ZWJ emoji.
-        final int STATE_BEFORE_VS_AND_ZWJ = 8;
+        final int STATE_BEFORE_VS_AND_ZWJ = 9;
 
         // The number of following RIS code points is odd.
-        final int STATE_ODD_NUMBERED_RIS = 9;
+        final int STATE_ODD_NUMBERED_RIS = 10;
         // The number of following RIS code points is even.
-        final int STATE_EVEN_NUMBERED_RIS = 10;
+        final int STATE_EVEN_NUMBERED_RIS = 11;
 
         // The state machine has been stopped.
-        final int STATE_FINISHED = 11;
+        final int STATE_FINISHED = 12;
 
         int deleteCharCount = 0;  // Char count to be deleted by backspace.
         int lastSeenVSCharCount = 0;  // Char count of previous variation selector.
@@ -152,7 +158,9 @@
             switch (state) {
                 case STATE_START:
                     deleteCharCount = Character.charCount(codePoint);
-                    if (isVariationSelector(codePoint)) {
+                    if (codePoint == LINE_FEED) {
+                        state = STATE_LF;
+                    } else if (isVariationSelector(codePoint)) {
                         state = STATE_BEFORE_VS;
                     } else if (Emoji.isZwjEmoji(codePoint)) {
                         state = STATE_BEFORE_ZWJ_EMOJI;
@@ -166,6 +174,11 @@
                         state = STATE_FINISHED;
                     }
                     break;
+                case STATE_LF:
+                    if (codePoint == CARRIAGE_RETURN) {
+                        ++deleteCharCount;
+                    }
+                    state = STATE_FINISHED;
                 case STATE_ODD_NUMBERED_RIS:
                     if (Emoji.isRegionalIndicatorSymbol(codePoint)) {
                         deleteCharCount += 2; /* Char count of RIS */
diff --git a/core/java/android/view/IDockedStackListener.aidl b/core/java/android/view/IDockedStackListener.aidl
index cbc8dbd..88ac271 100644
--- a/core/java/android/view/IDockedStackListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -42,4 +42,9 @@
      * @param animDuration The duration of the animation for changing the minimized state.
      */
     void onDockedStackMinimizedChanged(boolean minimized, long animDuration);
+
+    /**
+     * Called when window manager repositioned the docked stack after a screen rotation change.
+     */
+    void onDockSideChanged(int newDockSide);
 }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c972476..c4ed94f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -893,6 +893,10 @@
         nSerializeDisplayListTree(mNativeProxy);
     }
 
+    public static boolean copySurfaceInto(Surface surface, Bitmap bitmap) {
+        return nCopySurfaceInto(surface, bitmap);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -1029,4 +1033,6 @@
 
     private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
     private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
+
+    private static native boolean nCopySurfaceInto(Surface surface, Bitmap bitmap);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7e51096..307e700 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10278,7 +10278,8 @@
      */
     @Visibility boolean dispatchVisibilityAggregated(boolean isVisible) {
         final boolean thisVisible = getVisibility() == VISIBLE;
-        if (thisVisible) {
+        // If we're not visible but something is telling us we are, ignore it.
+        if (thisVisible || !isVisible) {
             onVisibilityAggregated(isVisible);
         }
         return thisVisible && isVisible;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a324767..2ce43c8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -549,8 +549,7 @@
                 // Compute surface insets required to draw at specified Z value.
                 // TODO: Use real shadow insets for a constant max Z.
                 if (!attrs.hasManualSurfaceInsets) {
-                    final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
-                    attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+                    attrs.setSurfaceInsets(view, false /*manual*/, true /*preservePrevious*/);
                 }
 
                 CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
@@ -883,10 +882,12 @@
             }
             mWindowAttributes.privateFlags |= compatibleWindowFlag;
 
-            // Restore old surface insets.
-            mWindowAttributes.surfaceInsets.set(
-                    oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
-            mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
+            if (mWindowAttributes.preservePreviousSurfaceInsets) {
+                // Restore old surface insets.
+                mWindowAttributes.surfaceInsets.set(
+                        oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
+                mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
+            }
 
             applyKeepScreenOnFlag(mWindowAttributes);
 
@@ -3373,6 +3374,16 @@
         }
 
         @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            if (msg.what == MSG_REQUEST_KEYBOARD_SHORTCUTS && msg.obj == null) {
+                // Debugging for b/27963013
+                throw new NullPointerException(
+                        "Attempted to call MSG_REQUEST_KEYBOARD_SHORTCUTS with null receiver:");
+            }
+            return super.sendMessageAtTime(msg, uptimeMillis);
+        }
+
+        @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case MSG_INVALIDATE:
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 584233c..e3de810 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1482,6 +1482,16 @@
         public boolean hasManualSurfaceInsets;
 
         /**
+         * Whether the previous surface insets should be used vs. what is currently set. When set
+         * to {@code true}, the view root will ignore surfaces insets in this object and use what
+         * it currently has.
+         *
+         * @see #surfaceInsets
+         * @hide
+         */
+        public boolean preservePreviousSurfaceInsets = true;
+
+        /**
          * The desired bitmap format.  May be one of the constants in
          * {@link android.graphics.PixelFormat}.  Default is OPAQUE.
          */
@@ -1771,6 +1781,17 @@
             return mTitle != null ? mTitle : "";
         }
 
+        /**
+         * Sets the surface insets based on the elevation (visual z position) of the input view.
+         * @hide
+         */
+        public final void setSurfaceInsets(View view, boolean manual, boolean preservePrevious) {
+            final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+            surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+            hasManualSurfaceInsets = manual;
+            preservePreviousSurfaceInsets = preservePrevious;
+        }
+
         /** @hide */
         @SystemApi
         public final void setUserActivityTimeout(long timeout) {
@@ -1822,6 +1843,7 @@
             out.writeInt(surfaceInsets.right);
             out.writeInt(surfaceInsets.bottom);
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
+            out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
             out.writeInt(accessibilityIdOfAnchor);
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
@@ -1874,6 +1896,7 @@
             surfaceInsets.right = in.readInt();
             surfaceInsets.bottom = in.readInt();
             hasManualSurfaceInsets = in.readInt() != 0;
+            preservePreviousSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readInt();
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
@@ -2075,6 +2098,11 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
+            if (preservePreviousSurfaceInsets != o.preservePreviousSurfaceInsets) {
+                preservePreviousSurfaceInsets = o.preservePreviousSurfaceInsets;
+                changes |= SURFACE_INSETS_CHANGED;
+            }
+
             if (needsMenuKey != o.needsMenuKey) {
                 needsMenuKey = o.needsMenuKey;
                 changes |= NEEDS_MENU_KEY_CHANGED;
@@ -2200,11 +2228,15 @@
                 sb.append(" userActivityTimeout=").append(userActivityTimeout);
             }
             if (surfaceInsets.left != 0 || surfaceInsets.top != 0 || surfaceInsets.right != 0 ||
-                    surfaceInsets.bottom != 0 || hasManualSurfaceInsets) {
+                    surfaceInsets.bottom != 0 || hasManualSurfaceInsets
+                    || !preservePreviousSurfaceInsets) {
                 sb.append(" surfaceInsets=").append(surfaceInsets);
                 if (hasManualSurfaceInsets) {
                     sb.append(" (manual)");
                 }
+                if (!preservePreviousSurfaceInsets) {
+                    sb.append(" (!preservePreviousSurfaceInsets)");
+                }
             }
             if (needsMenuKey != NEEDS_MENU_UNSET) {
                 sb.append(" needsMenuKey=");
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index dc433b1..dc4db01 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -20,6 +20,9 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.icu.text.DisplayContext;
+import android.icu.text.LocaleDisplayNames;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -421,39 +424,97 @@
     }
 
     /**
-     * @param context Context will be used for getting Locale and PackageManager.
-     * @param packageName The package name of the IME
-     * @param appInfo The application info of the IME
-     * @return a display name for this subtype. The string resource of the label (mSubtypeNameResId)
-     * may have exactly one %s in it. If there is, the %s part will be replaced with the locale's
-     * display name by the formatter. If there is not, this method returns the string specified by
-     * mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the framework to
-     * generate an appropriate display name.
+     * Returns a display name for this subtype.
+     *
+     * <p>If {@code subtypeNameResId} is specified (!= 0) text generated from that resource will
+     * be returned. The localized string resource of the label should be capitalized for inclusion
+     * in UI lists. The string resource may contain at most one {@code %s}. If present, the
+     * {@code %s} will be replaced with the display name of the subtype locale in the user's locale.
+     *
+     * <p>If {@code subtypeNameResId} is not specified (== 0) the framework returns the display name
+     * of the subtype locale, as capitalized for use in UI lists, in the user's locale.
+     *
+     * @param context {@link Context} will be used for getting {@link Locale} and
+     * {@link android.content.pm.PackageManager}.
+     * @param packageName The package name of the input method.
+     * @param appInfo The {@link ApplicationInfo} of the input method.
+     * @return a display name for this subtype.
      */
+    @NonNull
     public CharSequence getDisplayName(
             Context context, String packageName, ApplicationInfo appInfo) {
-        final Locale locale = getLocaleObject();
-        final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale;
         if (mSubtypeNameResId == 0) {
-            return localeStr;
+            return getLocaleDisplayName(getLocaleFromContext(context), getLocaleObject(),
+                    DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU);
         }
+
         final CharSequence subtypeName = context.getPackageManager().getText(
                 packageName, mSubtypeNameResId, appInfo);
-        if (!TextUtils.isEmpty(subtypeName)) {
-            final String replacementString =
-                    containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
-                            ? getExtraValueOf(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
-                            : localeStr;
-            try {
-                return String.format(
-                        subtypeName.toString(), replacementString != null ? replacementString : "");
-            } catch (IllegalFormatException e) {
-                Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
-                return "";
-            }
-        } else {
-            return localeStr;
+        if (TextUtils.isEmpty(subtypeName)) {
+            return "";
         }
+        final String subtypeNameString = subtypeName.toString();
+        String replacementString;
+        if (containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
+            replacementString = getExtraValueOf(
+                    EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
+        } else {
+            final DisplayContext displayContext;
+            if (TextUtils.equals(subtypeNameString, "%s")) {
+                displayContext = DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU;
+            } else if (subtypeNameString.startsWith("%s")) {
+                displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
+            } else {
+                displayContext = DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE;
+            }
+            replacementString = getLocaleDisplayName(getLocaleFromContext(context),
+                    getLocaleObject(), displayContext);
+        }
+        if (replacementString == null) {
+            replacementString = "";
+        }
+        try {
+            return String.format(subtypeNameString, replacementString);
+        } catch (IllegalFormatException e) {
+            Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
+            return "";
+        }
+    }
+
+    @Nullable
+    private static Locale getLocaleFromContext(@Nullable final Context context) {
+        if (context == null) {
+            return null;
+        }
+        if (context.getResources() == null) {
+            return null;
+        }
+        final Configuration configuration = context.getResources().getConfiguration();
+        if (configuration == null) {
+            return null;
+        }
+        return configuration.getLocales().get(0);
+    }
+
+    /**
+     * @param displayLocale {@link Locale} to be used to display {@code localeToDisplay}
+     * @param localeToDisplay {@link Locale} to be displayed in {@code displayLocale}
+     * @param displayContext context parameter to be used to display {@code localeToDisplay} in
+     * {@code displayLocale}
+     * @return Returns the name of the {@code localeToDisplay} in the user's current locale.
+     */
+    @NonNull
+    private static String getLocaleDisplayName(
+            @Nullable Locale displayLocale, @Nullable Locale localeToDisplay,
+            final DisplayContext displayContext) {
+        if (localeToDisplay == null) {
+            return "";
+        }
+        final Locale nonNullDisplayLocale =
+                displayLocale != null ? displayLocale : Locale.getDefault();
+        return LocaleDisplayNames
+                .getInstance(nonNullDisplayLocale, displayContext)
+                .localeDisplayName(localeToDisplay);
     }
 
     private HashMap<String, String> getExtraValueHashMap() {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0d8d8ed..af46756 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1290,9 +1290,7 @@
 
         // We may wrap that in another view, so we'll need to manually specify
         // the surface insets.
-        final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
-        p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
-        p.hasManualSurfaceInsets = true;
+        p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);
 
         mPopupViewInitialLayoutDirectionInherited =
                 (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
@@ -1581,7 +1579,7 @@
             return true;
         }
 
-        final int spaceAbove = displayFrameTop + anchorTopInScreen - anchorHeight;
+        final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
         if (height <= spaceAbove) {
             // Move everything up.
             if (mOverlapAnchor) {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index e9fa26c..9c9784b 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1347,6 +1347,13 @@
 
         if (d instanceof LayerDrawable) {
             d = ((LayerDrawable) d).findDrawableByLayerId(id);
+            if (d == null) {
+                // If we can't find the requested layer, fall back to setting
+                // the level of the entire drawable. This will break if
+                // progress is set on multiple elements, but the theme-default
+                // drawable will always have all layer IDs present.
+                d = mCurrentDrawable;
+            }
         }
 
         if (d != null) {
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 7220256..24d2c8e 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -58,8 +58,8 @@
 
     private static final String TAG = "RadialTimePickerView";
 
-    private static final int HOURS = 0;
-    private static final int MINUTES = 1;
+    public static final int HOURS = 0;
+    public static final int MINUTES = 1;
     private static final int HOURS_INNER = 2;
 
     private static final int SELECTOR_CIRCLE = 0;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 6d2cea6..a9b7f4e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1856,6 +1856,7 @@
         public static final int LAYOUT_MARGIN_END = 1;
         /** Set width */
         public static final int LAYOUT_WIDTH = 2;
+        public static final int LAYOUT_MARGIN_BOTTOM = 3;
 
         /**
          * @param viewId ID of the view alter
@@ -1898,6 +1899,12 @@
                         target.setLayoutParams(layoutParams);
                     }
                     break;
+                case LAYOUT_MARGIN_BOTTOM:
+                    if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
+                        ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = value;
+                        target.setLayoutParams(layoutParams);
+                    }
+                    break;
                 case LAYOUT_WIDTH:
                     layoutParams.width = value;
                     target.setLayoutParams(layoutParams);
@@ -2870,6 +2877,16 @@
     }
 
     /**
+     * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
+     *
+     * @hide
+     */
+    public void setViewLayoutMarginBottom(int viewId, int bottomMargin) {
+        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM,
+                bottomMargin));
+    }
+
+    /**
      * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
      * @hide
      */
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 4a24e26..0c3892d 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -58,8 +58,8 @@
     private static final long DELAY_COMMIT_MILLIS = 2000;
 
     // Index used by RadialPickerLayout
-    private static final int HOUR_INDEX = 0;
-    private static final int MINUTE_INDEX = 1;
+    private static final int HOUR_INDEX = RadialTimePickerView.HOURS;
+    private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES;
 
     // NOT a real index for the purpose of what's showing.
     private static final int AMPM_INDEX = 2;
@@ -82,6 +82,10 @@
 
     private final Calendar mTempCalendar;
 
+    // Accessibility strings.
+    private final String mSelectHours;
+    private final String mSelectMinutes;
+
     private boolean mIsEnabled = true;
     private boolean mAllowAutoAdvance;
     private int mInitialHourOfDay;
@@ -89,10 +93,6 @@
     private boolean mIs24Hour;
     private boolean mIsAmPmAtStart;
 
-    // Accessibility strings.
-    private String mSelectHours;
-    private String mSelectMinutes;
-
     // Localization data.
     private boolean mHourFormatShowLeadingZero;
     private boolean mHourFormatStartsAtZero;
@@ -520,11 +520,15 @@
         } else {
             flags |= DateUtils.FORMAT_12HOUR;
         }
+
         mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
         mTempCalendar.set(Calendar.MINUTE, getMinute());
-        String selectedDate = DateUtils.formatDateTime(mContext,
+
+        final String selectedTime = DateUtils.formatDateTime(mContext,
                 mTempCalendar.getTimeInMillis(), flags);
-        event.getText().add(selectedDate);
+        final String selectionMode = mRadialTimePickerView.getCurrentItemShowing() == HOUR_INDEX ?
+                mSelectHours : mSelectMinutes;
+        event.getText().add(selectedTime + " " + selectionMode);
     }
 
     /**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f2bf9e1..085e159 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,7 +16,9 @@
 
 package com.android.internal.app;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.VoiceInteractor.PickOptionRequest;
@@ -24,6 +26,7 @@
 import android.app.VoiceInteractor.Prompt;
 import android.content.pm.ComponentInfo;
 import android.os.AsyncTask;
+import android.provider.MediaStore;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -119,37 +122,62 @@
         }
     };
 
+    /**
+     * Get the string resource to be used as a label for the link to the resolver activity for an
+     * action.
+     *
+     * @param action The action to resolve
+     *
+     * @return The string resource to be used as a label
+     */
+    public static @StringRes int getLabelRes(String action) {
+        return ActionTitle.forAction(action).labelRes;
+    }
+
     private enum ActionTitle {
         VIEW(Intent.ACTION_VIEW,
                 com.android.internal.R.string.whichViewApplication,
-                com.android.internal.R.string.whichViewApplicationNamed),
+                com.android.internal.R.string.whichViewApplicationNamed,
+                com.android.internal.R.string.whichViewApplicationLabel),
         EDIT(Intent.ACTION_EDIT,
                 com.android.internal.R.string.whichEditApplication,
-                com.android.internal.R.string.whichEditApplicationNamed),
+                com.android.internal.R.string.whichEditApplicationNamed,
+                com.android.internal.R.string.whichEditApplicationLabel),
         SEND(Intent.ACTION_SEND,
                 com.android.internal.R.string.whichSendApplication,
-                com.android.internal.R.string.whichSendApplicationNamed),
+                com.android.internal.R.string.whichSendApplicationNamed,
+                com.android.internal.R.string.whichSendApplicationLabel),
         SENDTO(Intent.ACTION_SENDTO,
                 com.android.internal.R.string.whichSendToApplication,
-                com.android.internal.R.string.whichSendToApplicationNamed),
+                com.android.internal.R.string.whichSendToApplicationNamed,
+                com.android.internal.R.string.whichSendToApplicationLabel),
         SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
                 com.android.internal.R.string.whichSendApplication,
-                com.android.internal.R.string.whichSendApplicationNamed),
+                com.android.internal.R.string.whichSendApplicationNamed,
+                com.android.internal.R.string.whichSendApplicationLabel),
+        CAPTURE_IMAGE(MediaStore.ACTION_IMAGE_CAPTURE,
+                com.android.internal.R.string.whichImageCaptureApplication,
+                com.android.internal.R.string.whichImageCaptureApplicationNamed,
+                com.android.internal.R.string.whichImageCaptureApplicationLabel),
         DEFAULT(null,
                 com.android.internal.R.string.whichApplication,
-                com.android.internal.R.string.whichApplicationNamed),
+                com.android.internal.R.string.whichApplicationNamed,
+                com.android.internal.R.string.whichApplicationLabel),
         HOME(Intent.ACTION_MAIN,
                 com.android.internal.R.string.whichHomeApplication,
-                com.android.internal.R.string.whichHomeApplicationNamed);
+                com.android.internal.R.string.whichHomeApplicationNamed,
+                com.android.internal.R.string.whichHomeApplicationLabel);
 
         public final String action;
         public final int titleRes;
         public final int namedTitleRes;
+        public final @StringRes int labelRes;
 
-        ActionTitle(String action, int titleRes, int namedTitleRes) {
+        ActionTitle(String action, int titleRes, int namedTitleRes, @StringRes int labelRes) {
             this.action = action;
             this.titleRes = titleRes;
             this.namedTitleRes = namedTitleRes;
+            this.labelRes = labelRes;
         }
 
         public static ActionTitle forAction(String action) {
@@ -640,12 +668,19 @@
                 && mAdapter.mOrigResolveList != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
-            String action = intent.getAction();
+            Intent filterIntent;
 
+            if (intent.getSelector() != null) {
+                filterIntent = intent.getSelector();
+            } else {
+                filterIntent = intent;
+            }
+
+            String action = filterIntent.getAction();
             if (action != null) {
                 filter.addAction(action);
             }
-            Set<String> categories = intent.getCategories();
+            Set<String> categories = filterIntent.getCategories();
             if (categories != null) {
                 for (String cat : categories) {
                     filter.addCategory(cat);
@@ -654,9 +689,9 @@
             filter.addCategory(Intent.CATEGORY_DEFAULT);
 
             int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
-            Uri data = intent.getData();
+            Uri data = filterIntent.getData();
             if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
-                String mimeType = intent.resolveType(this);
+                String mimeType = filterIntent.resolveType(this);
                 if (mimeType != null) {
                     try {
                         filter.addDataType(mimeType);
@@ -760,7 +795,7 @@
                 } else {
                     try {
                         AppGlobals.getPackageManager().setLastChosenActivity(intent,
-                                intent.resolveTypeIfNeeded(getContentResolver()),
+                                intent.resolveType(getContentResolver()),
                                 PackageManager.MATCH_DEFAULT_ONLY,
                                 filter, bestMatch, intent.getComponent());
                     } catch (RemoteException re) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 06542f7..2f80b86 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -516,7 +516,7 @@
                 out.writeInt((int)val);
             } else {
                 int top = ~((int)((val>>32)&0x7fffffff));
-                int bottom = (int)(val&0xfffffff);
+                int bottom = (int)(val&0x0ffffffffL);
                 out.writeInt(top);
                 out.writeInt(bottom);
             }
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 76102af..f941836 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -68,11 +68,8 @@
      * A table of data as stored in a SparseMappingTable.
      */
     public static class Table {
-        // When mSequence is this this our data better be empty
-        private static final int UNINITIALIZED_SEQUENCE = -1;
-
         private SparseMappingTable mParent;
-        private int mSequence = UNINITIALIZED_SEQUENCE;
+        private int mSequence = 1;
         private int[] mTable;
         private int mSize;
 
@@ -119,12 +116,6 @@
          *         but should be considered opaque to the caller.
          */
         public int getOrAddKey(byte id, int count) {
-            // This is the only place we add data to mParent.mLongs, so this is the time
-            // to update our sequence to match there.
-            if (mSequence == UNINITIALIZED_SEQUENCE) {
-                mSequence = mParent.mSequence;
-            }
-
             assertConsistency();
 
             final int idx = binarySearch(id);
@@ -311,7 +302,7 @@
             // Reset our sequence number.  This will make all read/write calls
             // start to fail, and then when we re-allocate it will be re-synced
             // to that of mParent.
-            mSequence = UNINITIALIZED_SEQUENCE;
+            mSequence = mParent.mSequence;
         }
 
         /**
@@ -377,27 +368,19 @@
             //   Original bug: b/27045736
             //   New bug: b/27960286
             if (false) {
-                // Assert that our sequence number has been initialized. If it hasn't
-                // that means someone tried to read or write data without allocating it
-                // since we were created or reset.
-                if (mSequence == UNINITIALIZED_SEQUENCE) {
-                    logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
-                            + " SparseMappingTable.Table.  -- "
-                            + dumpInternalState());
-                    return;
-                }
-
                 // Assert that our sequence number matches mParent's.  If it isn't that means
-                // we have been reset and our
+                // we have been reset and our.  If our sequence is UNITIALIZED_SEQUENCE, then 
+                // it's possible that everything is working fine and we just haven't been
+                // written to since the last resetTable().
                 if (mSequence != mParent.mSequence) {
                     if (mSequence < mParent.mSequence) {
-                        logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
+                        logOrThrow("Sequence mismatch. SparseMappingTable.reset()"
                                 + " called but not Table.resetTable() -- "
                                 + dumpInternalState());
                         return;
                     } else if (mSequence > mParent.mSequence) {
                         logOrThrow("Sequence mismatch. Table.resetTable()"
-                                + " called but not SparseMappingTable.resetTable() -- "
+                                + " called but not SparseMappingTable.reset() -- "
                                 + dumpInternalState());
                         return;
                     }
@@ -494,6 +477,10 @@
         }
     }
 
+    public SparseMappingTable() {
+        mLongs.add(new long[ARRAY_SIZE]);
+    }
+
     /**
      * Wipe out all the data.
      */
@@ -545,6 +532,35 @@
     }
 
     /**
+     * Return a string for debugging.
+     */
+    public String dumpInternalState(boolean includeData) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("SparseMappingTable{");
+        sb.append("mSequence=");
+        sb.append(mSequence);
+        sb.append(" mNextIndex=");
+        sb.append(mNextIndex);
+        sb.append(" mLongs.size=");
+        final int N = mLongs.size();
+        sb.append(N);
+        sb.append("\n");
+        if (includeData) {
+            for (int i=0; i<N; i++) {
+                final long[] array = mLongs.get(i);
+                for (int j=0; j<array.length; j++) {
+                    if (i == N-1 && j == mNextIndex) {
+                        break;
+                    }
+                    sb.append(String.format(" %4d %d 0x%016x %-19d\n", i, j, array[j], array[j]));
+                }
+            }
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
      * Write the long array to the parcel in a compacted form.  Does not allow negative
      * values in the array.
      */
@@ -559,7 +575,7 @@
                 out.writeInt((int)val);
             } else {
                 int top = ~((int)((val>>32)&0x7fffffff));
-                int bottom = (int)(val&0xfffffff);
+                int bottom = (int)(val&0x0ffffffffL);
                 out.writeInt(top);
                 out.writeInt(bottom);
             }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5980ab6..78b5d61 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -43,6 +43,7 @@
 import dalvik.system.DexFile;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
+import dalvik.system.ZygoteHooks;
 
 import libcore.io.IoUtils;
 
@@ -597,6 +598,10 @@
     }
 
     public static void main(String argv[]) {
+        // Mark zygote start. This ensures that thread creation will throw
+        // an error.
+        ZygoteHooks.startZygoteNoThreadCreation();
+
         try {
             Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
             RuntimeInit.enableDdms();
@@ -648,6 +653,8 @@
             // Zygote process unmounts root storage spaces.
             Zygote.nativeUnmountStorageOnInit();
 
+            ZygoteHooks.stopZygoteNoThreadCreation();
+
             if (startSystemServer) {
                 startSystemServer(abiList, socketName);
             }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index f9ac563..3aa7719 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -210,7 +210,6 @@
     private Drawable mResizingBackgroundDrawable;
     private Drawable mCaptionBackgroundDrawable;
     private Drawable mUserCaptionBackgroundDrawable;
-    private Drawable mOriginalBackgroundDrawable;
 
     private float mAvailableWidth;
 
@@ -891,11 +890,6 @@
                 mBackgroundPadding.setEmpty();
             }
             drawableChanged();
-
-            // Make sure we don't reset to the old drawable when finishing resizing.
-            if (mResizeMode != RESIZE_MODE_INVALID) {
-                mOriginalBackgroundDrawable = null;
-            }
         }
     }
 
@@ -1960,9 +1954,6 @@
             updateElevation();
 
             updateColorViews(null /* insets */, false);
-
-            mOriginalBackgroundDrawable = getBackground();
-            setBackgroundDrawable(null);
         }
         mResizeMode = resizeMode;
         getViewRootImpl().requestInvalidateRootRenderNode();
@@ -1974,10 +1965,6 @@
         updateColorViews(null /* insets */, false);
         mResizeMode = RESIZE_MODE_INVALID;
         getViewRootImpl().requestInvalidateRootRenderNode();
-        if (mOriginalBackgroundDrawable != null) {
-            setBackgroundDrawable(mOriginalBackgroundDrawable);
-            mOriginalBackgroundDrawable = null;
-        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index d2ff9bc..fe63267 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -1403,10 +1403,12 @@
     @Override
     public final void setElevation(float elevation) {
         mElevation = elevation;
+        final WindowManager.LayoutParams attrs = getAttributes();
         if (mDecor != null) {
             mDecor.setElevation(elevation);
+            attrs.setSurfaceInsets(mDecor, true /*manual*/, false /*preservePrevious*/);
         }
-        dispatchWindowAttributesChanged(getAttributes());
+        dispatchWindowAttributesChanged(attrs);
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index 78c5e34..e2d8ffc 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -16,13 +16,18 @@
 
 package com.android.internal.widget;
 
+import com.android.internal.R;
+
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
 import android.text.BoringLayout;
 import android.text.Layout;
 import android.text.StaticLayout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.RemotableViewMethod;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -35,7 +40,8 @@
 @RemoteViews.RemoteView
 public class ImageFloatingTextView extends TextView {
 
-    private boolean mHasImage;
+    /** Number of lines from the top to indent */
+    private int mIndentLines;
 
     public ImageFloatingTextView(Context context) {
         this(context, null);
@@ -69,10 +75,16 @@
                 .setEllipsizedWidth(ellipsisWidth)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
-        // we set the endmargin on the first 2 lines. this works just in our case but that's
-        // sufficient for now.
-        int endMargin = (int) (getResources().getDisplayMetrics().density * 52);
-        int[] margins = mHasImage ? new int[] {endMargin, endMargin, 0} : null;
+        // we set the endmargin on the requested number of lines.
+        int endMargin = getContext().getResources().getDimensionPixelSize(
+                R.dimen.notification_content_picture_margin);
+        int[] margins = null;
+        if (mIndentLines > 0) {
+            margins = new int[mIndentLines + 1];
+            for (int i = 0; i < mIndentLines; i++) {
+                margins[i] = endMargin;
+            }
+        }
         if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
             builder.setIndents(margins, null);
         } else {
@@ -84,8 +96,22 @@
 
     @RemotableViewMethod
     public void setHasImage(boolean hasImage) {
-        mHasImage = hasImage;
+        mIndentLines = hasImage ? 2 : 0;
         // The new layout will be automatically created when the text is
         // set again by the notification.
     }
+
+    /**
+     * @param lines the number of lines at the top that should be indented by indentEnd
+     * @return whether a change was made
+     */
+    public boolean setNumIndentLines(int lines) {
+        if (mIndentLines != lines) {
+            mIndentLines = lines;
+            // Invalidate layout.
+            setHint(getHint());
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
new file mode 100644
index 0000000..dc7b7f5
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.widget;
+
+import com.android.internal.R;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews;
+
+/**
+ * A custom-built layout for the Notification.MessagingStyle.
+ *
+ * Evicts children until they all fit.
+ */
+@RemoteViews.RemoteView
+public class MessagingLinearLayout extends ViewGroup {
+
+    /**
+     * Spacing to be applied between views.
+     */
+    private int mSpacing;
+
+    /**
+     * The maximum height allowed.
+     */
+    private int mMaxHeight;
+
+    private int mIndentLines;
+
+    public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.MessagingLinearLayout, 0,
+                0);
+
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            switch (attr) {
+                case R.styleable.MessagingLinearLayout_maxHeight:
+                    mMaxHeight = a.getDimensionPixelSize(i, 0);
+                    break;
+                case R.styleable.MessagingLinearLayout_spacing:
+                    mSpacing = a.getDimensionPixelSize(i, 0);
+                    break;
+            }
+        }
+
+        if (mMaxHeight <= 0) {
+            throw new IllegalStateException(
+                    "MessagingLinearLayout: Must specify positive maxHeight");
+        }
+
+        a.recycle();
+    }
+
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // This is essentially a bottom-up linear layout that only adds children that fit entirely
+        // up to a maximum height.
+
+        switch (MeasureSpec.getMode(heightMeasureSpec)) {
+            case MeasureSpec.AT_MOST:
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                        Math.min(mMaxHeight, MeasureSpec.getSize(heightMeasureSpec)),
+                        MeasureSpec.AT_MOST);
+                break;
+            case MeasureSpec.UNSPECIFIED:
+                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                        mMaxHeight,
+                        MeasureSpec.AT_MOST);
+                break;
+            case MeasureSpec.EXACTLY:
+                break;
+        }
+        final int targetHeight = MeasureSpec.getSize(heightMeasureSpec);
+        final int count = getChildCount();
+
+        for (int i = 0; i < count; ++i) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            lp.hide = true;
+        }
+
+        int totalHeight = mPaddingTop + mPaddingBottom;
+        boolean first = true;
+
+        // Starting from the bottom: we measure every view as if it were the only one. If it still
+        // fits, we take it, otherwise we stop there.
+        for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
+            if (getChildAt(i).getVisibility() == GONE) {
+                continue;
+            }
+            final View child = getChildAt(i);
+            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
+
+            if (child instanceof ImageFloatingTextView) {
+                // Pretend we need the image padding for all views, we don't know which
+                // one will end up needing to do this (might end up not using all the space,
+                // but calculating this exactly would be more expensive).
+                ((ImageFloatingTextView) child).setNumIndentLines(mIndentLines);
+            }
+
+            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+
+            final int childHeight = child.getMeasuredHeight();
+            int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin +
+                    lp.bottomMargin + (first ? 0 : mSpacing));
+            first = false;
+
+            if (newHeight <= targetHeight) {
+                totalHeight = newHeight;
+                lp.hide = false;
+            } else {
+                break;
+            }
+        }
+
+        // Now that we know which views to take, fix up the indents and see what width we get.
+        int measuredWidth = mPaddingLeft + mPaddingRight;
+        int imageLines = mIndentLines;
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (child.getVisibility() == GONE || lp.hide) {
+                continue;
+            }
+
+            if (child instanceof ImageFloatingTextView) {
+                ImageFloatingTextView textChild = (ImageFloatingTextView) child;
+                if (imageLines == 2 && textChild.getLineCount() > 2) {
+                    // HACK: If we need indent for two lines, and they're coming from the same
+                    // view, we need extra spacing to compensate for the lack of margins,
+                    // so add an extra line of indent.
+                    imageLines = 3;
+                }
+                boolean changed = textChild.setNumIndentLines(Math.max(0, imageLines));
+                if (changed) {
+                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+                }
+                imageLines -= textChild.getLineCount();
+            }
+
+            measuredWidth = Math.max(measuredWidth,
+                    child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
+                            + mPaddingLeft + mPaddingRight);
+        }
+
+
+        setMeasuredDimension(
+                resolveSize(Math.max(getSuggestedMinimumWidth(), measuredWidth),
+                        widthMeasureSpec),
+                resolveSize(Math.max(getSuggestedMinimumHeight(), totalHeight),
+                        heightMeasureSpec));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        final int paddingLeft = mPaddingLeft;
+
+        int childTop;
+
+        // Where right end of child should go
+        final int width = right - left;
+        final int childRight = width - mPaddingRight;
+
+        final int layoutDirection = getLayoutDirection();
+        final int count = getChildCount();
+
+        childTop = mPaddingTop;
+
+        boolean first = true;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            if (child.getVisibility() == GONE || lp.hide) {
+                continue;
+            }
+
+            final int childWidth = child.getMeasuredWidth();
+            final int childHeight = child.getMeasuredHeight();
+
+            int childLeft;
+            if (layoutDirection == LAYOUT_DIRECTION_RTL) {
+                childLeft = childRight - childWidth - lp.rightMargin;
+            } else {
+                childLeft = paddingLeft + lp.leftMargin;
+            }
+
+            if (!first) {
+                childTop += mSpacing;
+            }
+
+            childTop += lp.topMargin;
+            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
+
+            childTop += childHeight + lp.bottomMargin;
+
+            first = false;
+        }
+    }
+
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        if (lp.hide) {
+            return true;
+        }
+        return super.drawChild(canvas, child, drawingTime);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(mContext, attrs);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+        LayoutParams copy = new LayoutParams(lp.width, lp.height);
+        if (lp instanceof MarginLayoutParams) {
+            copy.copyMarginsFrom((MarginLayoutParams) lp);
+        }
+        return copy;
+    }
+
+    @RemotableViewMethod
+    /**
+     * Sets how many lines should be indented to avoid a floating image.
+     */
+    public void setNumIndentLines(int numberLines) {
+        mIndentLines = numberLines;
+    }
+
+    public static class LayoutParams extends MarginLayoutParams {
+
+        boolean hide = false;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+    }
+}
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 6e9830e..dc9b656 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -36,7 +36,7 @@
 
 // ----------------------------------------------------------------------------
 
-#define EGL_QCOM_PROTECTED_CONTENT 0x32E0
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
 
 namespace android {
 
@@ -64,19 +64,12 @@
     EGLDisplay dpy = eglGetCurrentDisplay();
     EGLContext ctx = eglGetCurrentContext();
 
-    if (dpy == EGL_NO_DISPLAY) {
-        ALOGI("isProtectedSurface: invalid current EGLDisplay");
-        return false;
-    }
-
-    if (ctx == EGL_NO_CONTEXT) {
-        ALOGI("isProtectedSurface: invalid current EGLContext");
+    if (dpy == EGL_NO_DISPLAY || ctx == EGL_NO_CONTEXT) {
         return false;
     }
 
     EGLint isProtected = EGL_FALSE;
-    // TODO: Change the enum value below when an extension is ratified.
-    eglQueryContext(dpy, ctx, EGL_QCOM_PROTECTED_CONTENT, &isProtected);
+    eglQueryContext(dpy, ctx, EGL_PROTECTED_CONTENT_EXT, &isProtected);
 
     return isProtected;
 }
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index e5c4a2d..0de6549 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -175,7 +175,7 @@
 
     PathParser::ParseResult result;
     PathData data;
-    PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+    PathParser::getPathDataFromAsciiString(&data, &result, pathString, stringLength);
     if (result.failureOccurred) {
         doThrowIAE(env, result.failureMessage.c_str());
     }
@@ -343,7 +343,7 @@
 }
 
 static const JNINativeMethod gMethods[] = {
-        {"nCreateRenderer", "!(J)J", (void*)createTree},
+        {"nCreateTree", "!(J)J", (void*)createTree},
         {"nSetRendererViewportSize", "!(JFF)V", (void*)setTreeViewportSize},
         {"nSetRootAlpha", "!(JF)Z", (void*)setRootAlpha},
         {"nGetRootAlpha", "!(J)F", (void*)getRootAlpha},
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 90d69d2..5c961d9 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -370,10 +370,6 @@
             retVal = 0;
             break;
 
-        case CONTEXT_HUB_LOAD_OS:
-            retVal = 0;
-            break;
-
         default:
             retVal = -1;
             break;
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
index 0c867f1..53669a8 100644
--- a/core/jni/android_util_PathParser.cpp
+++ b/core/jni/android_util_PathParser.cpp
@@ -34,7 +34,7 @@
     SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
 
     PathParser::ParseResult result;
-    PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
+    PathParser::parseAsciiStringForSkPath(skPath, &result, pathString, strLength);
     env->ReleaseStringUTFChars(inputPathStr, pathString);
     if (result.failureOccurred) {
         doThrowIAE(env, result.failureMessage.c_str());
@@ -56,7 +56,7 @@
     const char* pathString = env->GetStringUTFChars(inputStr, NULL);
     PathData* pathData = new PathData();
     PathParser::ParseResult result;
-    PathParser::getPathDataFromString(pathData, &result, pathString, strLength);
+    PathParser::getPathDataFromAsciiString(pathData, &result, pathString, strLength);
     env->ReleaseStringUTFChars(inputStr, pathString);
     if (!result.failureOccurred) {
         return reinterpret_cast<jlong>(pathData);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d65209..faa4192 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -663,6 +663,14 @@
     proxy->setContentDrawBounds(left, top, right, bottom);
 }
 
+static jboolean android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
+        jobject clazz, jobject jsurface, jobject jbitmap) {
+    SkBitmap bitmap;
+    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+    return RenderProxy::copySurfaceInto(surface, &bitmap);
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -768,6 +776,8 @@
     { "nRemoveFrameMetricsObserver",
             "(JJ)V",
             (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
+    { "nCopySurfaceInto", "(Landroid/view/Surface;Landroid/graphics/Bitmap;)Z",
+                (void*)android_view_ThreadedRenderer_copySurfaceInto },
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9a2e39c7..778f797 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -420,6 +420,7 @@
     <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
     <protected-broadcast android:name="com.android.server.action.UPDATE_TWILIGHT_STATE" />
+    <protected-broadcast android:name="com.android.server.action.RESET_TWILIGHT_AUTO" />
     <protected-broadcast android:name="com.android.server.device_idle.STEP_IDLE_STATE" />
     <protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
     <protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
diff --git a/core/res/res/drawable-hdpi/ic_launcher_android.png b/core/res/res/drawable-hdpi/ic_launcher_android.png
index cce5187..2e9b196 100644
--- a/core/res/res/drawable-hdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-hdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_launcher_android.png b/core/res/res/drawable-ldpi/ic_launcher_android.png
index 628a8de..245e4b7 100644
--- a/core/res/res/drawable-ldpi/ic_launcher_android.png
+++ b/core/res/res/drawable-ldpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 6a97d5b..baacd4f 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_launcher_android.png b/core/res/res/drawable-xhdpi/ic_launcher_android.png
index b1097d6..00b69a5 100644
--- a/core/res/res/drawable-xhdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-xhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
new file mode 100644
index 0000000..ad05cd5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable/ic_input_extract_action_done.xml b/core/res/res/drawable/ic_input_extract_action_done.xml
new file mode 100644
index 0000000..f6e872e
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_done.xml
@@ -0,0 +1,19 @@
+<!--
+     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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M18,32.34L9.66,24l-2.83,2.83L18,38l24,-24 -2.83,-2.83z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_go.xml b/core/res/res/drawable/ic_input_extract_action_go.xml
new file mode 100644
index 0000000..edbc826
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_go.xml
@@ -0,0 +1,19 @@
+<!--
+     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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M6,22h28.34l-7.17,-7.17L30,12l12,12 -12,12 -2.83,-2.83L34.34,26H6z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_next.xml b/core/res/res/drawable/ic_input_extract_action_next.xml
new file mode 100644
index 0000000..ffef346
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_next.xml
@@ -0,0 +1,19 @@
+<!--
+     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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M23.17,14.83L30.34,22H2v4h28.34l-7.17,7.17L26,36l12,-12 -12,-12 -2.83,2.83zM40,12v24h4V12h-4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_previous.xml b/core/res/res/drawable/ic_input_extract_action_previous.xml
new file mode 100644
index 0000000..89777b0
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_previous.xml
@@ -0,0 +1,19 @@
+<!--
+     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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M22.83,14.83L15.66,22H44v4H15.66l7.17,7.17L20,36 8,24l12,-12 2.83,2.83zM6,12v24H2V12h4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_return.xml b/core/res/res/drawable/ic_input_extract_action_return.xml
new file mode 100644
index 0000000..cb2de5a
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_return.xml
@@ -0,0 +1,19 @@
+<!--
+     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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M38,14v8H11.66l7.17,-7.17L16,12 4,24l12,12 2.83,-2.83L11.66,26H42V14z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_search.xml b/core/res/res/drawable/ic_input_extract_action_search.xml
new file mode 100644
index 0000000..dcbcdbf
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_search.xml
@@ -0,0 +1,19 @@
+<!--
+     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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M31,28h-1.59l-0.55,-0.55C30.82,25.18 32,22.23 32,19c0,-7.18 -5.82,-13 -13,-13S6,11.82 6,19s5.82,13 13,13c3.23,0 6.18,-1.18 8.45,-3.13l0.55,0.55L28,31l10,9.98L40.98,38 31,28zM19,28c-4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9 9,4.03 9,9 -4.03,9 -9,9z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_send.xml b/core/res/res/drawable/ic_input_extract_action_send.xml
new file mode 100644
index 0000000..6494bee5
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_send.xml
@@ -0,0 +1,24 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M4.02,42L46,24 4.02,6 4,20l30,4 -30,4z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/input_extract_action_bg_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
new file mode 100644
index 0000000..9c6a6c3
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_material_dark.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/input_extract_action_bg_pressed_material_dark"
+        android:state_pressed="true"/>
+    <item android:drawable="@drawable/input_extract_action_bg_normal_material_dark"/>
+</selector>
diff --git a/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
new file mode 100644
index 0000000..8449978
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+    <solid android:color="@color/material_deep_teal_200"/>
+</shape>
diff --git a/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
new file mode 100644
index 0000000..adade104
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+    <solid android:color="@color/material_deep_teal_100"/>
+</shape>
diff --git a/core/res/res/layout-watch/input_method_extract_view.xml b/core/res/res/layout-watch/input_method_extract_view.xml
new file mode 100644
index 0000000..e3cd2ce
--- /dev/null
+++ b/core/res/res/layout-watch/input_method_extract_view.xml
@@ -0,0 +1,55 @@
+<?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.
+-->
+<android.inputmethodservice.CompactExtractEditLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="center_vertical"
+    android:baselineAligned="false">
+
+    <android.inputmethodservice.ExtractEditText
+        android:id="@id/inputExtractEditText"
+        android:layout_width="0dp"
+        android:layout_height="24dp"
+        android:background="@null"
+        android:singleLine="true"
+        android:inputType="text"
+        android:layout_weight="1"
+        android:fontFamily="sans-serif-condensed-light"
+        android:textColor="@color/primary_text_default_material_dark"
+        android:textColorHighlight="@color/accent_material_dark"
+        android:textSize="18dp"
+        android:cursorVisible="false"
+        android:gravity="bottom|right"
+        />
+
+    <FrameLayout
+        android:id="@id/inputExtractAccessories"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="8dp"
+        android:visibility="visible">
+        <ImageButton
+            android:id="@id/inputExtractAction"
+            android:layout_width="@dimen/input_extract_action_button_width"
+            android:layout_height="@dimen/input_extract_action_button_width"
+            android:background="@drawable/input_extract_action_bg_material_dark"
+            android:padding="4dp"
+            android:scaleType="centerInside" />
+    </FrameLayout>
+</android.inputmethodservice.CompactExtractEditLayout>
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
new file mode 100644
index 0000000..7d718e0
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -0,0 +1,80 @@
+<?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
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:tag="messaging"
+    >
+    <include layout="@layout/notification_template_header" />
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="top"
+            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:clipToPadding="false"
+            android:orientation="vertical">
+
+        <LinearLayout
+            android:id="@+id/notification_main_column"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top"
+            android:paddingStart="@dimen/notification_content_margin_start"
+            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:minHeight="@dimen/notification_min_content_height"
+            android:clipToPadding="false"
+            android:orientation="vertical"
+            >
+            <include layout="@layout/notification_template_part_line1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <com.android.internal.widget.MessagingLinearLayout
+                android:id="@+id/notification_messaging"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingBottom="@dimen/notification_content_margin_bottom"
+                android:spacing="@dimen/notification_messaging_spacing"
+                android:maxHeight="212dp">
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text0"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text1"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text2"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text3"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text4"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text5"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+                <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text6"
+                    style="@style/Widget.Material.Notification.MessagingText"
+                    />
+            </com.android.internal.widget.MessagingLinearLayout>
+        </LinearLayout>
+        <include layout="@layout/notification_material_action_list" />
+    </LinearLayout>
+    <include layout="@layout/notification_template_right_icon" />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index 15ccc67..b652127 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -16,9 +16,9 @@
   -->
 
 <ImageView android:id="@+id/right_icon" xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="40dp"
-    android:layout_height="40dp"
-    android:layout_marginEnd="16dp"
+    android:layout_width="@dimen/notification_large_icon_width"
+    android:layout_height="@dimen/notification_large_icon_width"
+    android:layout_marginEnd="@dimen/notification_content_margin_end"
     android:layout_marginTop="36dp"
     android:layout_gravity="top|end"
     android:scaleType="centerCrop"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 4b8640c..5850e50 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -72,7 +72,9 @@
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
-              android:layout_height="match_parent"
+              android:layout_height="wrap_content"
+              android:background="@color/white"
+              android:elevation="8dp"
               android:layout_alwaysShow="true"
               android:text="@string/noApplications"
               android:padding="32dp"
diff --git a/core/res/res/values-round-watch/dimens.xml b/core/res/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000..a12f499
--- /dev/null
+++ b/core/res/res/values-round-watch/dimens.xml
@@ -0,0 +1,27 @@
+<?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>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_height" type="fraction">25.2%</item>
+    <item name="input_extract_layout_padding_left" type="fraction">7.5%</item>
+    <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+    <item name="input_extract_layout_padding_right" type="fraction">21.4%</item>
+    <item name="input_extract_text_margin_bottom" type="fraction">5.5%</item>
+    <item name="input_extract_action_margin_bottom" type="fraction">2.1%</item>
+    <item name="input_extract_action_button_width" type="dimen">32dp</item>
+    <item name="input_extract_action_button_height" type="dimen">32dp</item>
+</resources>
diff --git a/core/res/res/values-w170dp-notround-watch/dimens.xml b/core/res/res/values-w170dp-notround-watch/dimens.xml
new file mode 100644
index 0000000..c91cbc1
--- /dev/null
+++ b/core/res/res/values-w170dp-notround-watch/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_padding_right" type="fraction">7.5%</item>
+</resources>
diff --git a/core/res/res/values-watch/dimens.xml b/core/res/res/values-watch/dimens.xml
new file mode 100644
index 0000000..f103aa9
--- /dev/null
+++ b/core/res/res/values-watch/dimens.xml
@@ -0,0 +1,27 @@
+<?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>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_height" type="fraction">17.5%</item>
+    <item name="input_extract_layout_padding_left" type="fraction">3.6%</item>
+    <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+    <item name="input_extract_layout_padding_right" type="fraction">2.5%</item>
+    <item name="input_extract_text_margin_bottom" type="fraction">0%</item>
+    <item name="input_extract_action_margin_bottom" type="fraction">0%</item>
+    <item name="input_extract_action_button_width" type="dimen">24dp</item>
+    <item name="input_extract_action_button_height" type="dimen">24dp</item>
+</resources>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 756a94b..6d6065f 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -18,6 +18,7 @@
     <style name="Theme.Dialog.AppError" parent="Theme.Micro.Dialog.AppError" />
     <style name="Theme.Holo.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
     <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.InputMethod" parent="Theme.Micro.InputMethod" />
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
     <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
 </resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 61753b1..66509fb 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -19,14 +19,16 @@
     <style name="Theme.DeviceDefault.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Micro.InputMethod"  />
+    <style name="Theme.DeviceDefault.Panel" parent="Theme.Micro.Panel"  />
     <style name="Theme.DeviceDefault.Light" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Micro.Light.Panel"  />
     <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
     <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
-
 </resources>
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0ed1f13..a320ef6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8129,6 +8129,11 @@
         <attr name="maxCollapsedHeightSmall" format="dimension" />
     </declare-styleable>
 
+    <declare-styleable name="MessagingLinearLayout">
+        <attr name="maxHeight" />
+        <attr name="spacing" />
+    </declare-styleable>
+
     <declare-styleable name="ResolverDrawerLayout_LayoutParams">
         <attr name="layout_alwaysShow" format="boolean" />
         <attr name="layout_ignoreOffset" format="boolean" />
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 7399fa9..c8ca116 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -75,7 +75,9 @@
     <color name="material_grey_100">#fff5f5f5</color>
     <color name="material_grey_50">#fffafafa</color>
 
+    <color name="material_deep_teal_100">#ffb2dfdb</color>
     <color name="material_deep_teal_200">#ff80cbc4</color>
+    <color name="material_deep_teal_300">#ff4db6ac</color>
     <color name="material_deep_teal_500">#ff009688</color>
 
     <color name="material_blue_grey_800">#ff37474f</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cb551e8..aada05d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -520,12 +520,6 @@
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
 
-    <!-- Wifi HAL supported PNO -->
-    <bool translatable="false" name="config_wifi_hal_pno_enable">false</bool>
-
-    <!-- Wifi SSID white list (can't be enabled if config_wifi_hal_pno_enable is not) -->
-    <bool translatable="false" name="config_wifi_ssid_white_list_enable">true</bool>
-
     <!-- Idle Receive current for wifi radio. 0 by default-->
     <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index dd54d57..9178305 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -162,9 +162,9 @@
     <dimen name="notification_min_height">92dp</dimen>
 
     <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_width">64dp</dimen>
+    <dimen name="notification_large_icon_width">40dp</dimen>
     <!-- The width of the big icons in notifications. -->
-    <dimen name="notification_large_icon_height">64dp</dimen>
+    <dimen name="notification_large_icon_height">40dp</dimen>
 
     <!-- The minimum width of the app name in the header if it shrinks -->
     <dimen name="notification_header_shrink_min_width">72dp</dimen>
@@ -181,6 +181,9 @@
     <!-- The margin of the content to an image-->
     <dimen name="notification_content_image_margin_end">8dp</dimen>
 
+    <!-- The spacing between messages in Notification.MessagingStyle -->
+    <dimen name="notification_messaging_spacing">6dp</dimen>
+
     <!-- Preferred width of the search view. -->
     <dimen name="search_view_preferred_width">320dip</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 96731cf..fa799c9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -349,18 +349,18 @@
     <!-- SSL CA cert notification --> <skip />
     <!-- Shows up when there is a user SSL CA Cert installed on the
          device.  Indicates to the user that SSL traffic can be intercepted.  [CHAR LIMIT=NONE] -->
-    <string name="ssl_ca_cert_warning">Network may be monitored</string>
-    <!-- Content text for a notification. The Title of the notification is "ssl_ca_cert_warning",
-         i.e. "Network may be monitored". This says that an unknown party is doing the monitoring.
-         [CHAR LIMIT=100]-->
+    <plurals name="ssl_ca_cert_warning">
+        <item quantity="one">Certificate authority installed</item>
+        <item quantity="other">Certificate authorities installed</item>
+    </plurals>
+    <!-- Content text for a notification. The Title of the notification is "ssl_ca_cert_warning".
+         This says that an unknown party is doing the monitoring. [CHAR LIMIT=100]-->
     <string name="ssl_ca_cert_noti_by_unknown">By an unknown third party</string>
-    <!-- Content text for a notification. The Title of the notification is "ssl_ca_cert_warning",
-        i.e. "Network may be monitored". This indicates that an unspecified administrator is doing
-        the monitoring. [CHAR LIMIT=100]-->
+    <!-- Content text for a notification. The Title of the notification is "ssl_ca_cert_warning".
+         This indicates that an unspecified administrator is doing the monitoring. [CHAR LIMIT=100]-->
     <string name="ssl_ca_cert_noti_by_administrator">By your work profile administrator</string>
-    <!-- Content text for a notification. The Title of the notification is "ssl_ca_cert_warning",
-         i.e. "Network may be monitored". This indicates who is doing the monitoring.
-         [CHAR LIMIT=100]-->
+    <!-- Content text for a notification. The Title of the notification is "ssl_ca_cert_warning".
+         This indicates who is doing the monitoring. [CHAR LIMIT=100]-->
     <string name="ssl_ca_cert_noti_managed">By <xliff:g id="managing_domain">%s</xliff:g></string>
 
     <!-- Work profile deleted notification--> <skip />
@@ -2552,21 +2552,29 @@
     <!-- Title of intent resolver dialog when selecting an application to run
          and a previously used application is known. -->
     <string name="whichApplicationNamed">Complete action using %1$s</string>
+    <!-- Generic label for a link to a intent resolver. -->
+    <string name="whichApplicationLabel">Complete action</string>
     <!-- Title of intent resolver dialog when selecting a viewer application to run. -->
     <string name="whichViewApplication">Open with</string>
     <!-- Title of intent resolver dialog when selecting a viewer application to run
          and a previously used application is known. -->
     <string name="whichViewApplicationNamed">Open with %1$s</string>
+    <!-- Label for a link to a intent resolver dialog to view something -->
+    <string name="whichViewApplicationLabel">Open</string>
     <!-- Title of intent resolver dialog when selecting an editor application to run. -->
     <string name="whichEditApplication">Edit with</string>
     <!-- Title of intent resolver dialog when selecting an editor application to run
          and a previously used application is known. -->
     <string name="whichEditApplicationNamed">Edit with %1$s</string>
+    <!-- Label for a link to a intent resolver dialog when selecting an editor application -->
+    <string name="whichEditApplicationLabel">Edit</string>
     <!-- Title of intent resolver dialog when selecting a sharing application to run. -->
     <string name="whichSendApplication">Share with</string>
     <!-- Title of intent resolver dialog when selecting a sharing application to run
          and a previously used application is known. -->
     <string name="whichSendApplicationNamed">Share with %1$s</string>
+    <!-- Label for a link to a intent resolver dialog to sharing something -->
+    <string name="whichSendApplicationLabel">Share</string>
     <!-- Title of intent resolver dialog when selecting an application to run to
          send content to a specific recipient. Often used for email. -->
     <string name="whichSendToApplication">Send using</string>
@@ -2574,11 +2582,23 @@
          send content to a specific recipient and a previously used application is known.
          Often used for email. -->
     <string name="whichSendToApplicationNamed">Send using %1$s</string>
+    <!-- Label for a link to a intent resolver dialog to send content to a specific recipient. -->
+    <string name="whichSendToApplicationLabel">Send</string>
     <!-- Title of intent resolver dialog when selecting a HOME application to run. -->
     <string name="whichHomeApplication">Select a Home app</string>
     <!-- Title of intent resolver dialog when selecting a HOME application to run
          and a previously used application is known. -->
     <string name="whichHomeApplicationNamed">Use %1$s as Home</string>
+    <!-- Label for a link to a intent resolver dialog when selecting a HOME -->
+    <string name="whichHomeApplicationLabel">Capture image</string>
+    <!-- Option to always use the selected application resolution in the future. See the "Complete action using" dialog title-->
+    <!-- Title of intent resolver dialog when capturing an image. -->
+    <string name="whichImageCaptureApplication">Capture image with</string>
+    <!-- Title of intent resolver dialog when capturing an image
+         and a previously used application is known. -->
+    <string name="whichImageCaptureApplicationNamed">Capture image with %1$s</string>
+    <!-- Label for a link to a intent resolver dialog when capturing an image -->
+    <string name="whichImageCaptureApplicationLabel">Capture image</string>
     <!-- Option to always use the selected application resolution in the future. See the "Complete action using" dialog title-->
     <string name="alwaysUse">Use by default for this action.</string>
     <!-- Title of the list of alternate options to complete an action shown when the
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2420c1a..8a33406 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -456,6 +456,14 @@
 
     <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
 
+    <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Light.TextView">
+        <item name="layout_width">match_parent</item>
+        <item name="layout_height">wrap_content</item>
+        <item name="ellipsize">end</item>
+        <item name="visibility">gone</item>
+        <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
+    </style>
+
     <!-- Widget Styles -->
 
     <style name="Material"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 29c6951..e37f8d5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -390,9 +390,7 @@
   <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
   <java-symbol type="integer" name="config_wifi_no_network_periodic_scan_interval" />
   <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
-  <java-symbol type="bool" name="config_wifi_hal_pno_enable" />
   <java-symbol type="integer" name="config_windowOutsetBottom" />
-  <java-symbol type="bool" name="config_wifi_ssid_white_list_enable" />
   <java-symbol type="integer" name="db_connection_pool_size" />
   <java-symbol type="integer" name="db_journal_size_limit" />
   <java-symbol type="integer" name="db_wal_autocheckpoint" />
@@ -1097,7 +1095,6 @@
   <java-symbol type="string" name="ssl_ca_cert_noti_by_unknown" />
   <java-symbol type="string" name="ssl_ca_cert_noti_by_administrator" />
   <java-symbol type="string" name="ssl_ca_cert_noti_managed" />
-  <java-symbol type="string" name="ssl_ca_cert_warning" />
   <java-symbol type="string" name="work_profile_deleted" />
   <java-symbol type="string" name="work_profile_deleted_description" />
   <java-symbol type="string" name="work_profile_deleted_details" />
@@ -1117,6 +1114,7 @@
   <java-symbol type="plurals" name="matches_found" />
   <java-symbol type="plurals" name="restr_pin_countdown" />
   <java-symbol type="plurals" name="pinpuk_attempts" />
+  <java-symbol type="plurals" name="ssl_ca_cert_warning" />
 
   <java-symbol type="array" name="carrier_properties" />
   <java-symbol type="array" name="config_sms_enabled_locking_shift_tables" />
@@ -1388,7 +1386,6 @@
 
   <java-symbol type="xml" name="password_kbd_qwerty" />
   <java-symbol type="xml" name="autotext" />
-  <java-symbol type="xml" name="eri" />
   <java-symbol type="xml" name="password_kbd_numeric" />
   <java-symbol type="xml" name="password_kbd_qwerty_shifted" />
   <java-symbol type="xml" name="password_kbd_symbols" />
@@ -2201,14 +2198,22 @@
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
   <java-symbol type="string" name="whichApplicationNamed" />
+  <java-symbol type="string" name="whichApplicationLabel" />
   <java-symbol type="string" name="whichViewApplication" />
   <java-symbol type="string" name="whichViewApplicationNamed" />
+  <java-symbol type="string" name="whichViewApplicationLabel" />
   <java-symbol type="string" name="whichEditApplication" />
   <java-symbol type="string" name="whichEditApplicationNamed" />
+  <java-symbol type="string" name="whichEditApplicationLabel" />
   <java-symbol type="string" name="whichSendApplication" />
   <java-symbol type="string" name="whichSendApplicationNamed" />
+  <java-symbol type="string" name="whichSendApplicationLabel" />
   <java-symbol type="string" name="whichSendToApplication" />
   <java-symbol type="string" name="whichSendToApplicationNamed" />
+  <java-symbol type="string" name="whichSendToApplicationLabel" />
+  <java-symbol type="string" name="whichImageCaptureApplication" />
+  <java-symbol type="string" name="whichImageCaptureApplicationNamed" />
+  <java-symbol type="string" name="whichImageCaptureApplicationLabel" />
   <java-symbol type="attr" name="lightY" />
   <java-symbol type="attr" name="lightZ" />
   <java-symbol type="attr" name="lightRadius" />
@@ -2238,6 +2243,7 @@
   <java-symbol type="array" name="networks_not_clear_data" />
   <java-symbol type="bool" name="config_switch_phone_on_voice_reg_state_change" />
   <java-symbol type="string" name="whichHomeApplicationNamed" />
+  <java-symbol type="string" name="whichHomeApplicationLabel" />
   <java-symbol type="bool" name="config_sms_force_7bit_encoding" />
   <java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
   <java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
@@ -2496,6 +2502,8 @@
   <java-symbol type="bool" name="config_strongAuthRequiredOnBoot" />
 
   <java-symbol type="layout" name="app_anr_dialog" />
+  <java-symbol type="layout" name="notification_template_material_messaging" />
+
   <java-symbol type="id" name="aerr_wait" />
 
   <java-symbol type="id" name="notification_content_container" />
@@ -2525,11 +2533,34 @@
   <java-symbol type="string" name="carrier_app_notification_text" />
   <java-symbol type="string" name="negative_duration" />
 
+  <java-symbol type="dimen" name="notification_messaging_spacing" />
+
   <!-- WallpaperManager config -->
   <java-symbol type="string" name="config_wallpaperCropperPackage" />
 
   <java-symbol type="id" name="textSpacerNoTitle" />
   <java-symbol type="id" name="titleDividerNoCustom" />
 
+  <java-symbol type="id" name="notification_messaging" />
+
   <java-symbol type="bool" name="config_sustainedPerformanceModeSupported" />
+
+  <!-- Wearable input extract edit view -->
+  <java-symbol type="drawable" name="ic_input_extract_action_go" />
+  <java-symbol type="drawable" name="ic_input_extract_action_search" />
+  <java-symbol type="drawable" name="ic_input_extract_action_send" />
+  <java-symbol type="drawable" name="ic_input_extract_action_next" />
+  <java-symbol type="drawable" name="ic_input_extract_action_done" />
+  <java-symbol type="drawable" name="ic_input_extract_action_previous" />
+  <java-symbol type="drawable" name="ic_input_extract_action_return" />
+
+  <java-symbol type="fraction" name="input_extract_layout_height" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_left" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_left_no_action" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_right" />
+  <java-symbol type="fraction" name="input_extract_text_margin_bottom" />
+  <java-symbol type="fraction" name="input_extract_action_margin_bottom" />
+
+  <java-symbol type="dimen" name="input_extract_action_button_width" />
+  <java-symbol type="dimen" name="input_extract_action_button_height" />
 </resources>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 478d66c..25a6e00 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -83,4 +83,18 @@
         <item name="fontFamily">sans-serif-condensed-light</item>
         <item name="textColor">@color/micro_text_light</item>
     </style>
+
+   <style name="Theme.Micro.Panel" parent="Theme.Material.Panel"  />
+   <style name="Theme.Micro.Light.Panel" parent="Theme.Material.Light.Panel"  />
+
+    <!-- Default theme for material style input methods, which is used by the
+         {@link android.inputmethodservice.InputMethodService} class.
+         This inherits from Theme.Panel, but sets up IME appropriate animations
+         and a few custom attributes. -->
+    <style name="Theme.Micro.InputMethod" parent="Theme.Micro.Panel">
+        <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+        <item name="imeFullscreenBackground">#1e282c</item>
+        <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+        <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
+    </style>
 </resources>
diff --git a/core/res/res/xml/eri.xml b/core/res/res/xml/eri.xml
deleted file mode 100644
index cd66f14..0000000
--- a/core/res/res/xml/eri.xml
+++ /dev/null
@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** 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.
-*/
--->
-
-<!-- Note that IconMode can be only 0, ON or 1, FLASHING
-     The icon is turned OFF if then IconIndex = 1 -->
-
-<EriFile VersionNumber="1357"
-         NumberOfEriEntries="12"
-         EriFileType="1">
-
-         <CallPromptId Id="0"
-                       CallPromptText="CallPromptId0"/>
-
-         <CallPromptId Id="1"
-                       CallPromptText="CallPromptId1"/>
-
-         <CallPromptId Id="2"
-                       CallPromptText="CallPromptId2"/>
-
-         <EriInfo RoamingIndicator="64"
-                  IconIndex="1"
-                  IconMode="0"
-                  EriText="T-CDMA 64"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="65"
-                  IconIndex="65"
-                  IconMode="0"
-                  EriText="T-CDMA 65"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="66"
-                  IconIndex="1"
-                  IconMode="0"
-                  EriText="T-CDMA Ext 66"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="67"
-                  IconIndex="67"
-                  IconMode="0"
-                  EriText="T-CDMA Ext 67"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="68"
-                  IconIndex="68"
-                  IconMode="0"
-                  EriText="T-CDMA Roam 68"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="69"
-                  IconIndex="69"
-                  IconMode="1"
-                  EriText="T-CDMA Ext 69"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="70"
-                  IconIndex="70"
-                  IconMode="1"
-                  EriText="T-CDMA Roam 70"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="71"
-                  IconIndex="1"
-                  IconMode="0"
-                  EriText="T-CDMA Ext 71"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="72"
-                  IconIndex="72"
-                  IconMode="0"
-                  EriText="T-CDMA Ext 72"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="73"
-                  IconIndex="73"
-                  IconMode="0"
-                  EriText="T-CDMA Roam 73"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="74"
-                  IconIndex="74"
-                  IconMode="1"
-                  EriText="T-CDMA Ext 74"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-         <EriInfo RoamingIndicator="75"
-                  IconIndex="75"
-                  IconMode="1"
-                  EriText="T-CDMA Roam 75"
-                  CallPromptId="0"
-                  AlertId="0"/>
-
-</EriFile>
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
new file mode 100644
index 0000000..fd57baa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app.procstats;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.mockito.Mockito;
+
+/**
+ * Provides test cases for SparseMappingTable.
+ */
+public class SparseMappingTableTest extends TestCase {
+    private static final String TAG = "SparseMappingTableTest";
+
+    final byte ID1 = 1;
+    final byte ID2 = 2;
+
+    final long VALUE1 = 100;
+    final long VALUE2 = 10000000000L;
+
+    /**
+     * Test the parceling and unparceling logic when there is no data.
+     */
+    @SmallTest
+    public void testParcelingEmpty() throws Exception  {
+        final SparseMappingTable data = new SparseMappingTable();
+        final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+        final Parcel dataParcel = Parcel.obtain();
+        data.writeToParcel(dataParcel);
+
+        final Parcel tableParcel = Parcel.obtain();
+        table.writeToParcel(tableParcel);
+
+        dataParcel.setDataPosition(0);
+        final SparseMappingTable data1 = new SparseMappingTable();
+        data1.readFromParcel(dataParcel);
+        Assert.assertEquals(0, dataParcel.dataAvail());
+        dataParcel.recycle();
+
+        tableParcel.setDataPosition(0);
+        final SparseMappingTable.Table table1 = new SparseMappingTable.Table(data1);
+        table1.readFromParcel(tableParcel);
+        Assert.assertEquals(0, tableParcel.dataAvail());
+        tableParcel.recycle();
+    }
+
+    /**
+     * Test the parceling and unparceling logic.
+     */
+    @SmallTest
+    public void testParceling() throws Exception  {
+        int key;
+        final SparseMappingTable data = new SparseMappingTable();
+        final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+        key = table.getOrAddKey(ID1, 1);
+        table.setValue(key, VALUE1);
+
+        key = table.getOrAddKey(ID2, 1);
+        table.setValue(key, VALUE2);
+
+        final Parcel dataParcel = Parcel.obtain();
+        data.writeToParcel(dataParcel);
+
+        final Parcel tableParcel = Parcel.obtain();
+        table.writeToParcel(tableParcel);
+
+        dataParcel.setDataPosition(0);
+        final SparseMappingTable data1 = new SparseMappingTable();
+        data1.readFromParcel(dataParcel);
+        Assert.assertEquals(0, dataParcel.dataAvail());
+        dataParcel.recycle();
+
+        tableParcel.setDataPosition(0);
+        final SparseMappingTable.Table table1 = new SparseMappingTable.Table(data1);
+        table1.readFromParcel(tableParcel);
+        Assert.assertEquals(0, tableParcel.dataAvail());
+        tableParcel.recycle();
+
+        key = table1.getKey(ID1);
+        Assert.assertEquals(VALUE1, table1.getValue(key));
+
+        key = table1.getKey(ID2);
+        Assert.assertEquals(VALUE2, table1.getValue(key));
+    }
+
+
+    /**
+     * Test that after resetting you can still read data, you just get no values.
+     */
+    @SmallTest
+    public void testParcelingWithReset() throws Exception  {
+        int key;
+        final SparseMappingTable data = new SparseMappingTable();
+        final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+        key = table.getOrAddKey(ID1, 1);
+        table.setValue(key, VALUE1);
+
+        data.reset();
+        table.resetTable();
+
+        key = table.getOrAddKey(ID2, 1);
+        table.setValue(key, VALUE2);
+
+        Log.d(TAG, "before: " + data.dumpInternalState(true));
+        Log.d(TAG, "before: " + table.dumpInternalState());
+
+        final Parcel dataParcel = Parcel.obtain();
+        data.writeToParcel(dataParcel);
+
+        final Parcel tableParcel = Parcel.obtain();
+        table.writeToParcel(tableParcel);
+
+        dataParcel.setDataPosition(0);
+        final SparseMappingTable data1 = new SparseMappingTable();
+        data1.readFromParcel(dataParcel);
+        Assert.assertEquals(0, dataParcel.dataAvail());
+        dataParcel.recycle();
+
+        tableParcel.setDataPosition(0);
+        final SparseMappingTable.Table table1 = new SparseMappingTable.Table(data1);
+        table1.readFromParcel(tableParcel);
+        Assert.assertEquals(0, tableParcel.dataAvail());
+        tableParcel.recycle();
+
+        key = table1.getKey(ID1);
+        Assert.assertEquals(SparseMappingTable.INVALID_KEY, key);
+
+        key = table1.getKey(ID2);
+        Assert.assertEquals(VALUE2, table1.getValue(key));
+
+        Log.d(TAG, " after: " + data1.dumpInternalState(true));
+        Log.d(TAG, " after: " + table1.dumpInternalState());
+    }
+
+    /**
+     * Test that it fails if you reset the data and not the table.
+     *
+     * Resetting the table and not the data is basically okay. The data in the
+     * SparseMappingTable will be leaked.
+     */
+    @SmallTest
+    public void testResetDataOnlyFails() throws Exception {
+        int key;
+        final SparseMappingTable data = new SparseMappingTable();
+        final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+        key = table.getOrAddKey(ID1, 1);
+        table.setValue(key, VALUE1);
+
+        Assert.assertEquals(VALUE1, table.getValue(key));
+
+        data.reset();
+
+        try {
+            table.getValue(key);
+            throw new Exception("Exception not thrown after mismatched reset calls.");
+        } catch (RuntimeException ex) {
+            // Good
+        }
+    }
+
+    /**
+     * Test that trying to get data that you didn't add fails correctly.
+     */
+    @SmallTest
+    public void testInvalidKey() throws Exception {
+        int key;
+        final SparseMappingTable data = new SparseMappingTable();
+        final SparseMappingTable.Table table = new SparseMappingTable.Table(data);
+
+        key = table.getKey(ID1);
+
+        // The key should be INVALID_KEY
+        Assert.assertEquals(SparseMappingTable.INVALID_KEY, key);
+
+        // If you get the value with getValueForId you get 0.
+        Assert.assertEquals(0, table.getValueForId(ID1));
+    }
+}
+
+
+
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index d412d7c..8a7d39b 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -111,6 +111,20 @@
         <group gid="media" />
     </permission>
 
+    <!-- These are permissions that were mapped to gids but we need
+         to keep them here until an upgrade from L to the current
+         version is to be supported. These permissions are built-in
+         and in L were not stored in packages.xml as a result if they
+         are not defined here while parsing packages.xml we would
+         ignore these permissions being granted to apps and not
+         propagate the granted state. From N we are storing the
+         built-in permissions in packages.xml as the saved storage
+         is negligible (one tag with the permission) compared to
+         the fragility as one can remove a built-in permission which
+         no longer needs to be mapped to gids and break grant propagation. -->
+    <permission name="android.permission.READ_EXTERNAL_STORAGE" />
+    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
     <!-- ================================================================== -->
     <!-- ================================================================== -->
     <!-- ================================================================== -->
diff --git a/docs/html/guide/topics/manifest/application-element.jd b/docs/html/guide/topics/manifest/application-element.jd
index 5600b5c..887b4ea 100644
--- a/docs/html/guide/topics/manifest/application-element.jd
+++ b/docs/html/guide/topics/manifest/application-element.jd
@@ -472,6 +472,8 @@
 {@link android.os.StrictMode.VmPolicy.Builder#detectCleartextNetwork() StrictMode.VmPolicy.Builder.detectCleartextNetwork()}.
 
 <p>This attribute was added in API level 23.</p>
+
+<p>This flag is ignored on Android N and above if an Android Network Security Config is present.</p>
 </dd>
 
 <dt><a name="vmSafeMode"></a>{@code android:vmSafeMode}</dt>
diff --git a/graphics/java/android/graphics/PixelCopy.java b/graphics/java/android/graphics/PixelCopy.java
new file mode 100644
index 0000000..c599126
--- /dev/null
+++ b/graphics/java/android/graphics/PixelCopy.java
@@ -0,0 +1,104 @@
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.ThreadedRenderer;
+
+/**
+ * Provides a mechanisms to issue pixel copy requests to allow for copy
+ * operations from {@link Surface} to {@link Bitmap}
+ *
+ * @hide
+ */
+public final class PixelCopy {
+    /**
+     * Contains the result of a pixel copy request
+     */
+    public static final class Response {
+        /**
+         * Indicates whether or not the copy request completed successfully.
+         * If this is true, then {@link #bitmap} contains the result of the copy.
+         * If this is false, {@link #bitmap} is unmodified from the originally
+         * passed destination.
+         *
+         * For example a request might fail if the source is protected content
+         * so copies are not allowed. Similarly if the source has nothing to
+         * copy from, because either no frames have been produced yet or because
+         * it has already been destroyed, then this will be false.
+         */
+        public boolean success;
+
+        /**
+         * The output bitmap. This is always the same object that was passed
+         * to request() as the 'dest' bitmap. If {@link #success} is true this
+         * contains a copy of the pixels of the source object. If {@link #success}
+         * is false then this is unmodified.
+         */
+        @NonNull
+        public Bitmap bitmap;
+    }
+
+    public interface OnPixelCopyFinished {
+        /**
+         * Callback for when a pixel copy request has completed. This will be called
+         * regardless of whether the copy succeeded or failed.
+         *
+         * @param response Contains the result of the copy request which includes
+         * whether or not the copy was successful.
+         */
+        void onPixelCopyFinished(PixelCopy.Response response);
+    }
+
+    /**
+     * Requests for the display content of a {@link SurfaceView} to be copied
+     * into a provided {@link Bitmap}.
+     *
+     * The contents of the source will be scaled to fit exactly inside the bitmap.
+     * The pixel format of the source buffer will be converted, as part of the copy,
+     * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+     * in the SurfaceView's Surface will be used as the source of the copy.
+     *
+     * @param source The source from which to copy
+     * @param dest The destination of the copy. The source will be scaled to
+     * match the width, height, and format of this bitmap.
+     * @param listener Callback for when the pixel copy request completes
+     * @param listenerThread The callback will be invoked on this Handler when
+     * the copy is finished.
+     */
+    public static void request(@NonNull SurfaceView source, @NonNull Bitmap dest,
+            @NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
+        request(source.getHolder().getSurface(), dest, listener, listenerThread);
+    }
+
+    /**
+     * Requests a copy of the pixels from a {@link Surface} to be copied into
+     * a provided {@link Bitmap}.
+     *
+     * The contents of the source will be scaled to fit exactly inside the bitmap.
+     * The pixel format of the source buffer will be converted, as part of the copy,
+     * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+     * in the Surface will be used as the source of the copy.
+     *
+     * @param source The source from which to copy
+     * @param dest The destination of the copy. The source will be scaled to
+     * match the width, height, and format of this bitmap.
+     * @param listener Callback for when the pixel copy request completes
+     * @param listenerThread The callback will be invoked on this Handler when
+     * the copy is finished.
+     */
+    public static void request(@NonNull Surface source, @NonNull Bitmap dest,
+            @NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
+        // TODO: Make this actually async and fast and cool and stuff
+        final PixelCopy.Response response = new PixelCopy.Response();
+        response.success = ThreadedRenderer.copySurfaceInto(source, dest);
+        response.bitmap = dest;
+        listenerThread.post(new Runnable() {
+            @Override
+            public void run() {
+                listener.onPixelCopyFinished(response);
+            }
+        });
+    }
+}
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 832b9c3..98082ca 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -16,8 +16,18 @@
 
 package android.graphics;
 
-public class PixelFormat
-{
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class PixelFormat {
+
+    /** @hide */
+    @IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Opacity {}
+
     /* these constants need to match those in hardware/hardware.h */
 
     public static final int UNKNOWN     = 0;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f15aff7..2886f0d 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -19,6 +19,7 @@
 import android.content.res.AssetManager;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.LruCache;
 import android.util.SparseArray;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -63,6 +64,11 @@
     private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
             new LongSparseArray<SparseArray<Typeface>>(3);
 
+    /**
+     * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
+     */
+    private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
+
     static Typeface sDefaultTypeface;
     static Map<String, Typeface> sSystemFontMap;
     static FontFamily[] sFallbackFonts;
@@ -176,22 +182,50 @@
 
     /**
      * Create a new typeface from the specified font data.
-     * @param mgr The application's asset manager
-     * @param path  The file name of the font data in the assets directory
+     *
+     * @param mgr  The application's asset manager
+     * @param path The file name of the font data in the assets directory
      * @return The new typeface.
      */
     public static Typeface createFromAsset(AssetManager mgr, String path) {
         if (sFallbackFonts != null) {
-            FontFamily fontFamily = new FontFamily();
-            if (fontFamily.addFontFromAsset(mgr, path)) {
-                FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families);
+            synchronized (sDynamicTypefaceCache) {
+                final String key = createAssetUid(mgr, path);
+                Typeface typeface = sDynamicTypefaceCache.get(key);
+                if (typeface != null) return typeface;
+
+                FontFamily fontFamily = new FontFamily();
+                if (fontFamily.addFontFromAsset(mgr, path)) {
+                    FontFamily[] families = { fontFamily };
+                    typeface = createFromFamiliesWithDefault(families);
+                    sDynamicTypefaceCache.put(key, typeface);
+                    return typeface;
+                }
             }
         }
         throw new RuntimeException("Font asset not found " + path);
     }
 
     /**
+     * Creates a unique id for a given AssetManager and asset path.
+     *
+     * @param mgr  AssetManager instance
+     * @param path The path for the asset.
+     * @return Unique id for a given AssetManager and asset path.
+     */
+    private static String createAssetUid(final AssetManager mgr, String path) {
+        final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
+        final StringBuilder builder = new StringBuilder();
+        final int size = pkgs.size();
+        for (int i = 0; i < size; i++) {
+            builder.append(pkgs.valueAt(i));
+            builder.append("-");
+        }
+        builder.append(path);
+        return builder.toString();
+    }
+
+    /**
      * Create a new typeface from the specified font file.
      *
      * @param path The path to the font data.
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e31cb51..cf5c563 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -16,7 +16,14 @@
 
 package android.graphics.drawable;
 
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.AttrRes;
 import android.annotation.ColorInt;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.ActivityInfo.Config;
@@ -47,17 +54,12 @@
 import android.util.Xml;
 import android.view.View;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.Collection;
 
-import com.android.internal.R;
-
 /**
  * A Drawable is a general abstraction for "something that can be drawn."  Most
  * often you will deal with Drawable as the type of resource retrieved for
@@ -148,7 +150,7 @@
      *
      * @param canvas The canvas to draw into
      */
-    public abstract void draw(Canvas canvas);
+    public abstract void draw(@NonNull Canvas canvas);
 
     /**
      * Specify a bounding rectangle for the Drawable. This is where the drawable
@@ -176,7 +178,7 @@
      * Specify a bounding rectangle for the Drawable. This is where the drawable
      * will draw when its draw() method is called.
      */
-    public void setBounds(Rect bounds) {
+    public void setBounds(@NonNull Rect bounds) {
         setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
     }
 
@@ -188,7 +190,7 @@
      * @param bounds Rect to receive the drawable's bounds (allocated by the
      *               caller).
      */
-    public final void copyBounds(Rect bounds) {
+    public final void copyBounds(@NonNull Rect bounds) {
         bounds.set(mBounds);
     }
 
@@ -200,6 +202,7 @@
      *
      * @return A copy of the drawable's bounds
      */
+    @NonNull
     public final Rect copyBounds() {
         return new Rect(mBounds);
     }
@@ -219,6 +222,7 @@
      * @see #copyBounds()
      * @see #copyBounds(android.graphics.Rect)
      */
+    @NonNull
     public final Rect getBounds() {
         if (mBounds == ZERO_BOUNDS_RECT) {
             mBounds = new Rect();
@@ -237,6 +241,7 @@
      *
      * @return The dirty bounds of this drawable
      */
+    @NonNull
     public Rect getDirtyBounds() {
         return getBounds();
     }
@@ -354,8 +359,8 @@
      *
      * @see #getCallback()
      */
-    public final void setCallback(Callback cb) {
-        mCallback = new WeakReference<Callback>(cb);
+    public final void setCallback(@Nullable Callback cb) {
+        mCallback = cb != null ? new WeakReference<>(cb) : null;
     }
 
     /**
@@ -366,11 +371,9 @@
      *
      * @see #setCallback(android.graphics.drawable.Drawable.Callback)
      */
+    @Nullable
     public Callback getCallback() {
-        if (mCallback != null) {
-            return mCallback.get();
-        }
-        return null;
+        return mCallback != null ? mCallback.get() : null;
     }
 
     /**
@@ -399,7 +402,7 @@
      *
      * @see Callback#scheduleDrawable
      */
-    public void scheduleSelf(Runnable what, long when) {
+    public void scheduleSelf(@NonNull Runnable what, long when) {
         final Callback callback = getCallback();
         if (callback != null) {
             callback.scheduleDrawable(this, what, when);
@@ -415,7 +418,7 @@
      *
      * @see Callback#unscheduleDrawable
      */
-    public void unscheduleSelf(Runnable what) {
+    public void unscheduleSelf(@NonNull Runnable what) {
         final Callback callback = getCallback();
         if (callback != null) {
             callback.unscheduleDrawable(this, what);
@@ -429,7 +432,7 @@
      *         {@link android.view.View#LAYOUT_DIRECTION_RTL}
      * @see #setLayoutDirection(int)
      */
-    public int getLayoutDirection() {
+    public @View.ResolvedLayoutDir int getLayoutDirection() {
         return mLayoutDirection;
     }
 
@@ -471,7 +474,7 @@
      * Specify an alpha value for the drawable. 0 means fully transparent, and
      * 255 means fully opaque.
      */
-    public abstract void setAlpha(int alpha);
+    public abstract void setAlpha(@IntRange(from=0,to=255) int alpha);
 
     /**
      * Gets the current alpha value for the drawable. 0 means fully transparent,
@@ -480,6 +483,7 @@
      * The default return value is 255 if the class does not override this method to return a value
      * specific to its use of alpha.
      */
+    @IntRange(from=0,to=255)
     public int getAlpha() {
         return 0xFF;
     }
@@ -493,7 +497,7 @@
      * Drawables draw is private implementation detail, and not something apps
      * should rely upon.
      */
-    public void setXfermode(Xfermode mode) {
+    public void setXfermode(@Nullable Xfermode mode) {
         // Base implementation drops it on the floor for compatibility. Whee!
     }
 
@@ -596,7 +600,7 @@
      *
      * @return the current color filter, or {@code null} if none set
      */
-    public ColorFilter getColorFilter() {
+    public @Nullable ColorFilter getColorFilter() {
         return null;
     }
 
@@ -633,7 +637,7 @@
      * @param outRect the rect to populate with the hotspot bounds
      * @see #setHotspotBounds(int, int, int, int)
      */
-    public void getHotspotBounds(Rect outRect) {
+    public void getHotspotBounds(@NonNull Rect outRect) {
         outRect.set(getBounds());
     }
 
@@ -681,7 +685,7 @@
      * of the Drawable to change (hence requiring an invalidate), otherwise
      * returns false.
      */
-    public boolean setState(final int[] stateSet) {
+    public boolean setState(@NonNull final int[] stateSet) {
         if (!Arrays.equals(mStateSet, stateSet)) {
             mStateSet = stateSet;
             return onStateChange(stateSet);
@@ -696,7 +700,7 @@
      * Some drawables may modify their imagery based on the selected state.
      * @return An array of resource Ids describing the current state.
      */
-    public int[] getState() {
+    public @NonNull int[] getState() {
         return mStateSet;
     }
 
@@ -713,7 +717,7 @@
      *         {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
      *         currently in use.
      */
-    public Drawable getCurrent() {
+    public @NonNull Drawable getCurrent() {
         return this;
     }
 
@@ -733,7 +737,7 @@
      * of the Drawable to change (hence requiring an invalidate), otherwise
      * returns false.
      */
-    public final boolean setLevel(int level) {
+    public final boolean setLevel(@IntRange(from=0,to=10000) int level) {
         if (mLevel != level) {
             mLevel = level;
             return onLevelChange(level);
@@ -746,7 +750,7 @@
      *
      * @return int Current level, from 0 (minimum) to 10000 (maximum).
      */
-    public final int getLevel() {
+    public final @IntRange(from=0,to=10000) int getLevel() {
         return mLevel;
     }
 
@@ -843,7 +847,7 @@
      *
      * @see android.graphics.PixelFormat
      */
-    public abstract int getOpacity();
+    public abstract @PixelFormat.Opacity int getOpacity();
 
     /**
      * Return the appropriate opacity value for two source opacities.  If
@@ -860,7 +864,8 @@
      *
      * @see #getOpacity
      */
-    public static int resolveOpacity(int op1, int op2) {
+    public static @PixelFormat.Opacity int resolveOpacity(@PixelFormat.Opacity int op1,
+            @PixelFormat.Opacity int op2) {
         if (op1 == op2) {
             return op1;
         }
@@ -889,7 +894,7 @@
      * report, else a Region holding the parts of the Drawable's bounds that
      * are transparent.
      */
-    public Region getTransparentRegion() {
+    public @Nullable Region getTransparentRegion() {
         return null;
     }
 
@@ -902,7 +907,10 @@
      * if it looks the same and there is no need to redraw it since its
      * last state.
      */
-    protected boolean onStateChange(int[] state) { return false; }
+    protected boolean onStateChange(int[] state) {
+        return false;
+    }
+
     /** Override this in your subclass to change appearance if you vary based
      *  on level.
      * @return Returns true if the level change has caused the appearance of
@@ -910,12 +918,17 @@
      * if it looks the same and there is no need to redraw it since its
      * last level.
      */
-    protected boolean onLevelChange(int level) { return false; }
+    protected boolean onLevelChange(int level) {
+        return false;
+    }
+
     /**
      * Override this in your subclass to change appearance if you vary based on
      * the bounds.
      */
-    protected void onBoundsChange(Rect bounds) {}
+    protected void onBoundsChange(Rect bounds) {
+        // Stub method.
+    }
 
     /**
      * Returns the drawable's intrinsic width.
@@ -990,7 +1003,7 @@
      *
      * @hide
      */
-    public Insets getOpticalInsets() {
+    public @NonNull Insets getOpticalInsets() {
         return Insets.NONE;
     }
 
@@ -1024,7 +1037,7 @@
      * @see ConstantState
      * @see #getConstantState()
      */
-    public Drawable mutate() {
+    public @NonNull Drawable mutate() {
         return this;
     }
 
@@ -1130,9 +1143,10 @@
         AttributeSet attrs = Xml.asAttributeSet(parser);
 
         int type;
-        while ((type=parser.next()) != XmlPullParser.START_TAG &&
-                type != XmlPullParser.END_DOCUMENT) {
-            // Empty loop
+        //noinspection StatementWithEmptyBody
+        while ((type=parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Empty loop.
         }
 
         if (type != XmlPullParser.START_TAG) {
@@ -1226,8 +1240,9 @@
      * @throws XmlPullParserException
      * @throws IOException
      */
-    void inflateWithAttributes(@NonNull Resources r, @NonNull XmlPullParser parser,
-            @NonNull TypedArray attrs, int visibleAttr) throws XmlPullParserException, IOException {
+    void inflateWithAttributes(@NonNull @SuppressWarnings("unused") Resources r,
+            @NonNull @SuppressWarnings("unused") XmlPullParser parser, @NonNull TypedArray attrs,
+            @AttrRes int visibleAttr) throws XmlPullParserException, IOException {
         mVisible = attrs.getBoolean(visibleAttr, mVisible);
     }
 
@@ -1258,8 +1273,7 @@
          * @return a new drawable object based on this constant state
          * @see {@link #newDrawable(Resources)}
          */
-        @NonNull
-        public abstract Drawable newDrawable();
+        public abstract @NonNull Drawable newDrawable();
 
         /**
          * Creates a new Drawable instance from its constant state using the
@@ -1273,8 +1287,7 @@
          *            be displayed
          * @return a new drawable object based on this constant state
          */
-        @NonNull
-        public Drawable newDrawable(@Nullable Resources res) {
+        public @NonNull Drawable newDrawable(@Nullable Resources res) {
             return newDrawable();
         }
 
@@ -1292,8 +1305,8 @@
          *              displayed
          * @return a new drawable object based on this constant state
          */
-        @NonNull
-        public Drawable newDrawable(@Nullable Resources res, @Nullable Theme theme) {
+        public @NonNull Drawable newDrawable(@Nullable Resources res,
+                @Nullable @SuppressWarnings("unused") Theme theme) {
             return newDrawable(res);
         }
 
@@ -1307,12 +1320,12 @@
          * @return Total pixel count
          * @hide
          */
-        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
+        public int addAtlasableBitmaps(@NonNull Collection<Bitmap> atlasList) {
             return 0;
         }
 
         /** @hide */
-        protected final boolean isAtlasable(Bitmap bitmap) {
+        protected final boolean isAtlasable(@Nullable Bitmap bitmap) {
             return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888;
         }
 
@@ -1331,7 +1344,7 @@
      * @see ConstantState
      * @see Drawable#mutate()
      */
-    public ConstantState getConstantState() {
+    public @Nullable ConstantState getConstantState() {
         return null;
     }
 
@@ -1349,8 +1362,8 @@
      * Ensures the tint filter is consistent with the current tint color and
      * mode.
      */
-    PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint,
-            PorterDuff.Mode tintMode) {
+    @Nullable PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,
+            @Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode) {
         if (tint == null || tintMode == null) {
             return null;
         }
@@ -1369,8 +1382,8 @@
      * Obtains styled attributes from the theme, if available, or unstyled
      * resources if the theme is null.
      */
-    static TypedArray obtainAttributes(
-            Resources res, Theme theme, AttributeSet set, int[] attrs) {
+    static @NonNull TypedArray obtainAttributes(@NonNull Resources res, @Nullable Theme theme,
+            @NonNull AttributeSet set, @NonNull int[] attrs) {
         if (theme == null) {
             return res.obtainAttributes(set, attrs);
         }
@@ -1422,8 +1435,6 @@
         final int rounded = Math.round(result);
         if (rounded != 0) {
             return rounded;
-        } else if (pixels == 0) {
-            return 0;
         } else if (pixels > 0) {
             return 1;
         } else {
@@ -1444,7 +1455,7 @@
      * @param cause the exception to re-throw
      * @throws RuntimeException
      */
-    static void rethrowAsRuntimeException(Exception cause) throws RuntimeException {
+    static void rethrowAsRuntimeException(@NonNull Exception cause) throws RuntimeException {
         final RuntimeException e = new RuntimeException(cause);
         e.setStackTrace(new StackTraceElement[0]);
         throw e;
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index e75fb98..0e45780 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -534,13 +534,17 @@
     public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
             @NonNull AttributeSet attrs, @Nullable Theme theme)
             throws XmlPullParserException, IOException {
-        if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererRefBase != null) {
+        if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) {
             // This VD has been used to display other VD resource content, clean up.
-            mVectorState.mRootGroup = new VGroup();
-            if (mVectorState.mNativeRendererRefBase != null) {
-                mVectorState.mNativeRendererRefBase.release();
+            if (mVectorState.mRootGroup != null) {
+                // Remove child nodes' reference to tree
+                mVectorState.mRootGroup.setTree(null);
             }
-            mVectorState.createNativeRenderer(mVectorState.mRootGroup.mNativePtr);
+            mVectorState.mRootGroup = new VGroup();
+            if (mVectorState.mNativeTree != null) {
+                mVectorState.mNativeTree.release();
+            }
+            mVectorState.createNativeTree(mVectorState.mRootGroup);
         }
         final VectorDrawableState state = mVectorState;
         state.setDensity(Drawable.resolveDensity(r, 0));
@@ -734,7 +738,7 @@
         Insets mOpticalInsets = Insets.NONE;
         String mRootName = null;
         VGroup mRootGroup;
-        VirtualRefBasePtr mNativeRendererRefBase = null;
+        VirtualRefBasePtr mNativeTree = null;
 
         int mDensity = DisplayMetrics.DENSITY_DEFAULT;
         final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -755,7 +759,7 @@
                 mTintMode = copy.mTintMode;
                 mAutoMirrored = copy.mAutoMirrored;
                 mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
-                createNativeRenderer(mRootGroup.mNativePtr);
+                createNativeTree(mRootGroup);
 
                 mBaseWidth = copy.mBaseWidth;
                 mBaseHeight = copy.mBaseHeight;
@@ -770,15 +774,16 @@
             }
         }
 
-        private void createNativeRenderer(long rootGroupPtr) {
-            mNativeRendererRefBase = new VirtualRefBasePtr(nCreateRenderer(rootGroupPtr));
+        private void createNativeTree(VGroup rootGroup) {
+            mNativeTree = new VirtualRefBasePtr(nCreateTree(rootGroup.mNativePtr));
+            mRootGroup.setTree(mNativeTree);
         }
 
         long getNativeRenderer() {
-            if (mNativeRendererRefBase == null) {
+            if (mNativeTree == null) {
                 return 0;
             }
-            return mNativeRendererRefBase.get();
+            return mNativeTree.get();
         }
 
         public boolean canReuseCache() {
@@ -817,7 +822,7 @@
 
         public VectorDrawableState() {
             mRootGroup = new VGroup();
-            createNativeRenderer(mRootGroup.mNativePtr);
+            createNativeTree(mRootGroup);
         }
 
         @Override
@@ -881,16 +886,16 @@
          * has changed.
          */
         public boolean setAlpha(float alpha) {
-            return nSetRootAlpha(mNativeRendererRefBase.get(), alpha);
+            return nSetRootAlpha(mNativeTree.get(), alpha);
         }
 
         @SuppressWarnings("unused")
         public float getAlpha() {
-            return nGetRootAlpha(mNativeRendererRefBase.get());
+            return nGetRootAlpha(mNativeTree.get());
         }
     }
 
-    static class VGroup implements VObject {
+    static class VGroup extends VObject {
         private static final int ROTATE_INDEX = 0;
         private static final int PIVOT_X_INDEX = 1;
         private static final int PIVOT_Y_INDEX = 2;
@@ -984,11 +989,18 @@
         public void addChild(VObject child) {
             nAddChild(mNativePtr, child.getNativePtr());
             mChildren.add(child);
-
             mIsStateful |= child.isStateful();
         }
 
         @Override
+        public void setTree(VirtualRefBasePtr treeRoot) {
+            super.setTree(treeRoot);
+            for (int i = 0; i < mChildren.size(); i++) {
+                mChildren.get(i).setTree(treeRoot);
+            }
+        }
+
+        @Override
         public long getNativePtr() {
             return mNativePtr;
         }
@@ -1101,79 +1113,93 @@
         /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
         @SuppressWarnings("unused")
         public float getRotation() {
-            return nGetRotation(mNativePtr);
+            return isTreeValid() ? nGetRotation(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setRotation(float rotation) {
-            nSetRotation(mNativePtr, rotation);
+            if (isTreeValid()) {
+                nSetRotation(mNativePtr, rotation);
+            }
         }
 
         @SuppressWarnings("unused")
         public float getPivotX() {
-            return nGetPivotX(mNativePtr);
+            return isTreeValid() ? nGetPivotX(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setPivotX(float pivotX) {
-            nSetPivotX(mNativePtr, pivotX);
+            if (isTreeValid()) {
+                nSetPivotX(mNativePtr, pivotX);
+            }
         }
 
         @SuppressWarnings("unused")
         public float getPivotY() {
-            return nGetPivotY(mNativePtr);
+            return isTreeValid() ? nGetPivotY(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setPivotY(float pivotY) {
-            nSetPivotY(mNativePtr, pivotY);
+            if (isTreeValid()) {
+                nSetPivotY(mNativePtr, pivotY);
+            }
         }
 
         @SuppressWarnings("unused")
         public float getScaleX() {
-            return nGetScaleX(mNativePtr);
+            return isTreeValid() ? nGetScaleX(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setScaleX(float scaleX) {
-            nSetScaleX(mNativePtr, scaleX);
+            if (isTreeValid()) {
+                nSetScaleX(mNativePtr, scaleX);
+            }
         }
 
         @SuppressWarnings("unused")
         public float getScaleY() {
-            return nGetScaleY(mNativePtr);
+            return isTreeValid() ? nGetScaleY(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setScaleY(float scaleY) {
-            nSetScaleY(mNativePtr, scaleY);
+            if (isTreeValid()) {
+                nSetScaleY(mNativePtr, scaleY);
+            }
         }
 
         @SuppressWarnings("unused")
         public float getTranslateX() {
-            return nGetTranslateX(mNativePtr);
+            return isTreeValid() ? nGetTranslateX(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setTranslateX(float translateX) {
-            nSetTranslateX(mNativePtr, translateX);
+            if (isTreeValid()) {
+                nSetTranslateX(mNativePtr, translateX);
+            }
         }
 
         @SuppressWarnings("unused")
         public float getTranslateY() {
-            return nGetTranslateY(mNativePtr);
+            return isTreeValid() ? nGetTranslateY(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         public void setTranslateY(float translateY) {
-            nSetTranslateY(mNativePtr, translateY);
+            if (isTreeValid()) {
+                nSetTranslateY(mNativePtr, translateY);
+            }
         }
     }
 
     /**
      * Common Path information for clip path and normal path.
      */
-    static abstract class VPath implements VObject {
+    static abstract class VPath extends VObject {
         protected PathParser.PathData mPathData = null;
 
         String mPathName;
@@ -1203,7 +1229,9 @@
         @SuppressWarnings("unused")
         public void setPathData(PathParser.PathData pathData) {
             mPathData.setPathData(pathData);
-            nSetPathData(getNativePtr(), mPathData.getNativePtr());
+            if (isTreeValid()) {
+                nSetPathData(getNativePtr(), mPathData.getNativePtr());
+            }
         }
     }
 
@@ -1549,97 +1577,120 @@
         /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
         @SuppressWarnings("unused")
         int getStrokeColor() {
-            return nGetStrokeColor(mNativePtr);
+            return isTreeValid() ? nGetStrokeColor(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setStrokeColor(int strokeColor) {
             mStrokeColors = null;
-            nSetStrokeColor(mNativePtr, strokeColor);
+            if (isTreeValid()) {
+                nSetStrokeColor(mNativePtr, strokeColor);
+            }
         }
 
         @SuppressWarnings("unused")
         float getStrokeWidth() {
-            return nGetStrokeWidth(mNativePtr);
+            return isTreeValid() ? nGetStrokeWidth(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setStrokeWidth(float strokeWidth) {
-            nSetStrokeWidth(mNativePtr, strokeWidth);
+            if (isTreeValid()) {
+                nSetStrokeWidth(mNativePtr, strokeWidth);
+            }
         }
 
         @SuppressWarnings("unused")
         float getStrokeAlpha() {
-            return nGetStrokeAlpha(mNativePtr);
+            return isTreeValid() ? nGetStrokeAlpha(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setStrokeAlpha(float strokeAlpha) {
-            nSetStrokeAlpha(mNativePtr, strokeAlpha);
+            if (isTreeValid()) {
+                nSetStrokeAlpha(mNativePtr, strokeAlpha);
+            }
         }
 
         @SuppressWarnings("unused")
         int getFillColor() {
-            return nGetFillColor(mNativePtr);
+            return isTreeValid() ? nGetFillColor(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setFillColor(int fillColor) {
             mFillColors = null;
-            nSetFillColor(mNativePtr, fillColor);
+            if (isTreeValid()) {
+                nSetFillColor(mNativePtr, fillColor);
+            }
         }
 
         @SuppressWarnings("unused")
         float getFillAlpha() {
-            return nGetFillAlpha(mNativePtr);
+            return isTreeValid() ? nGetFillAlpha(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setFillAlpha(float fillAlpha) {
-            nSetFillAlpha(mNativePtr, fillAlpha);
+            if (isTreeValid()) {
+                nSetFillAlpha(mNativePtr, fillAlpha);
+            }
         }
 
         @SuppressWarnings("unused")
         float getTrimPathStart() {
-            return nGetTrimPathStart(mNativePtr);
+            return isTreeValid() ? nGetTrimPathStart(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setTrimPathStart(float trimPathStart) {
-            nSetTrimPathStart(mNativePtr, trimPathStart);
+            if (isTreeValid()) {
+                nSetTrimPathStart(mNativePtr, trimPathStart);
+            }
         }
 
         @SuppressWarnings("unused")
         float getTrimPathEnd() {
-            return nGetTrimPathEnd(mNativePtr);
+            return isTreeValid() ? nGetTrimPathEnd(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setTrimPathEnd(float trimPathEnd) {
-            nSetTrimPathEnd(mNativePtr, trimPathEnd);
+            if (isTreeValid()) {
+                nSetTrimPathEnd(mNativePtr, trimPathEnd);
+            }
         }
 
         @SuppressWarnings("unused")
         float getTrimPathOffset() {
-            return nGetTrimPathOffset(mNativePtr);
+            return isTreeValid() ? nGetTrimPathOffset(mNativePtr) : 0;
         }
 
         @SuppressWarnings("unused")
         void setTrimPathOffset(float trimPathOffset) {
-            nSetTrimPathOffset(mNativePtr, trimPathOffset);
+            if (isTreeValid()) {
+                nSetTrimPathOffset(mNativePtr, trimPathOffset);
+            }
         }
     }
 
-    interface VObject {
-        long getNativePtr();
-        void inflate(Resources r, AttributeSet attrs, Theme theme);
-        boolean canApplyTheme();
-        void applyTheme(Theme t);
-        boolean onStateChange(int[] state);
-        boolean isStateful();
+    abstract static class VObject {
+        VirtualRefBasePtr mTreePtr = null;
+        boolean isTreeValid() {
+            return mTreePtr != null && mTreePtr.get() != 0;
+        }
+        void setTree(VirtualRefBasePtr ptr) {
+            mTreePtr = ptr;
+        }
+        abstract long getNativePtr();
+        abstract void inflate(Resources r, AttributeSet attrs, Theme theme);
+        abstract boolean canApplyTheme();
+        abstract void applyTheme(Theme t);
+        abstract boolean onStateChange(int[] state);
+        abstract boolean isStateful();
     }
 
-    private static native long nCreateRenderer(long rootGroupPtr);
+    private static native long nCreateTree(long rootGroupPtr);
     private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
             float viewportHeight);
     private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 6913f43..715c875 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -34,9 +34,7 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 #include <utils/Timers.h>
-#ifdef __ANDROID__
-#include <cutils/trace.h>
-#endif
+#include <utils/Trace.h>
 
 #include <assert.h>
 #include <dirent.h>
@@ -54,14 +52,6 @@
     _rc; })
 #endif
 
-#ifdef __ANDROID__
-#define MY_TRACE_BEGIN(x) ATRACE_BEGIN(x)
-#define MY_TRACE_END() ATRACE_END()
-#else
-#define MY_TRACE_BEGIN(x)
-#define MY_TRACE_END()
-#endif
-
 using namespace android;
 
 static const bool kIsDebug = false;
@@ -623,7 +613,7 @@
     ResTable* sharedRes = NULL;
     bool shared = true;
     bool onlyEmptyResources = true;
-    MY_TRACE_BEGIN(ap.path.string());
+    ATRACE_NAME(ap.path.string());
     Asset* idmap = openIdmapLocked(ap);
     size_t nextEntryIdx = mResources->getTableCount();
     ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
@@ -703,8 +693,6 @@
     if (idmap != NULL) {
         delete idmap;
     }
-    MY_TRACE_END();
-
     return onlyEmptyResources;
 }
 
@@ -752,6 +740,7 @@
 
 void AssetManager::updateResourceParamsLocked() const
 {
+    ATRACE_CALL();
     ResTable* res = mResources;
     if (!res) {
         return;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1ccc59a..15cb684 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <algorithm>
 #include <limits>
 #include <memory>
 #include <type_traits>
@@ -5810,6 +5811,10 @@
     return NULL;
 }
 
+static bool compareResTableConfig(const ResTable_config& a, const ResTable_config& b) {
+    return a.compare(b) < 0;
+}
+
 void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
         bool ignoreAndroidPackage, bool includeSystemConfigs) const {
     const size_t packageCount = mPackageGroups.size();
@@ -5840,17 +5845,11 @@
                     ResTable_config cfg;
                     memset(&cfg, 0, sizeof(ResTable_config));
                     cfg.copyFromDtoH(config->config);
-                    // only insert unique
-                    const size_t N = configs->size();
-                    size_t n;
-                    for (n = 0; n < N; n++) {
-                        if (0 == (*configs)[n].compare(cfg)) {
-                            break;
-                        }
-                    }
-                    // if we didn't find it
-                    if (n == N) {
-                        configs->add(cfg);
+
+                    auto iter = std::lower_bound(configs->begin(), configs->end(), cfg,
+                                                 compareResTableConfig);
+                    if (iter == configs->end() || iter->compare(cfg) != 0) {
+                        configs->insertAt(cfg, std::distance(configs->begin(), iter));
                     }
                 }
             }
@@ -5858,6 +5857,10 @@
     }
 }
 
+static bool compareString8AndCString(const String8& str, const char* cStr) {
+    return strcmp(str.string(), cStr) < 0;
+}
+
 void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
 {
     Vector<ResTable_config> configs;
@@ -5872,15 +5875,11 @@
     char locale[RESTABLE_MAX_LOCALE_LEN];
     for (size_t i=0; i<I; i++) {
         configs[i].getBcp47Locale(locale);
-        const size_t J = locales->size();
-        size_t j;
-        for (j=0; j<J; j++) {
-            if (0 == strcmp(locale, (*locales)[j].string())) {
-                break;
-            }
-        }
-        if (j == J) {
-            locales->add(String8(locale));
+
+        auto iter = std::lower_bound(locales->begin(), locales->end(), locale,
+                                     compareString8AndCString);
+        if (iter == locales->end() || strcmp(iter->string(), locale) != 0) {
+            locales->insertAt(String8(locale), std::distance(locales->begin(), iter));
         }
     }
 }
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 7cd7fb5..b8b4625 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -39,8 +39,20 @@
  */
 #include "data/basic/basic_arsc.h"
 
+/**
+ * Include a binary library resource table.
+ *
+ * Package: com.android.test.basic
+ */
 #include "data/lib/lib_arsc.h"
 
+/**
+ * Include a system resource table.
+ *
+ * Package: android
+ */
+#include "data/system/system_arsc.h"
+
 TEST(ResTableTest, shouldLoadSuccessfully) {
     ResTable table;
     ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -324,4 +336,25 @@
     ASSERT_EQ(uint32_t(600), val.data);
 }
 
+TEST(ResTableTest, GetConfigurationsReturnsUniqueList) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable_config configSv;
+    memset(&configSv, 0, sizeof(configSv));
+    configSv.language[0] = 's';
+    configSv.language[1] = 'v';
+
+    Vector<ResTable_config> configs;
+    table.getConfigurations(&configs);
+
+    EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
+
+    Vector<String8> locales;
+    table.getLocales(&locales);
+
+    EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
+}
+
 } // namespace
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ac80d88..ff9be16 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -21,7 +21,7 @@
 enum { MAY_NOT_BE_BAG = false };
 
 static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
-    return memcmp(&a, &b, sizeof(a)) == 0;
+    return a.compare(b) == 0;
 }
 
 static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index 27f25fe..6a31fb8 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -33,6 +33,12 @@
     };
 }
 
+namespace integer {
+    enum {
+        number = 0x01030000, // sv
+    };
+}
+
 } // namespace R
 } // namespace android
 
diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
new file mode 100644
index 0000000..b97bdb6
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <public type="integer" name="number" id="0x01030000" />
+    <integer name="number">1</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
index 215ecae..b0dab6b 100644
--- a/libs/androidfw/tests/data/system/system_arsc.h
+++ b/libs/androidfw/tests/data/system/system_arsc.h
@@ -1,8 +1,8 @@
 unsigned char system_arsc[] = {
-  0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x0c, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, 0x03, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
   0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -25,26 +25,33 @@
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
-  0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
-  0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
-  0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x78, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x5e, 0x00,
+  0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x70, 0x00,
+  0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x84, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
   0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
   0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
   0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
   0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
   0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
-  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
-  0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+  0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -55,15 +62,27 @@
   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
   0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
-  0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00,
+  0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
-  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
-  0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+  0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01,
+  0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x60, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+  0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
-unsigned int system_arsc_len = 792;
+unsigned int system_arsc_len = 1016;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0606b0b..717a1e6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -84,6 +84,7 @@
     Properties.cpp \
     PropertyValuesHolder.cpp \
     PropertyValuesAnimatorSet.cpp \
+    Readback.cpp \
     RenderBufferCache.cpp \
     RenderNode.cpp \
     RenderProperties.cpp \
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index bd71e0d..4d65782 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -226,7 +226,7 @@
         // Set to 0 so that the animate() basically instantly finishes
         mStartTime = 0;
     }
-    if (mDuration < 0 || mDuration > 50000) {
+    if (mDuration < 0) {
         ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
     }
 }
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d1ff670..9d4eccc 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -285,6 +285,7 @@
 void JankTracker::reset() {
     mData->jankTypeCounts.fill(0);
     mData->frameCounts.fill(0);
+    mData->slowFrameCounts.fill(0);
     mData->totalFrameCount = 0;
     mData->jankFrameCount = 0;
     mData->statStartTime = systemTime(CLOCK_MONOTONIC);
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 7e85333..2179f14 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -162,7 +162,7 @@
             || verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
 }
 
-void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
+void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
         const char* pathStr, size_t strLen) {
     if (pathStr == NULL) {
         result->failureOccurred = true;
@@ -171,7 +171,16 @@
     }
 
     size_t start = 0;
-    size_t end = 1;
+    // Skip leading spaces.
+    while (isspace(pathStr[start]) && start < strLen) {
+        start++;
+    }
+    if (start == strLen) {
+        result->failureOccurred = true;
+        result->failureMessage = "Path string cannot be empty.";
+        return;
+    }
+    size_t end = start + 1;
 
     while (end < strLen) {
         end = nextStart(pathStr, strLen, end);
@@ -226,9 +235,9 @@
     ALOGD("points are : %s", os.str().c_str());
 }
 
-void PathParser::parseStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
+void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
     PathData pathData;
-    getPathDataFromString(&pathData, result, pathStr, strLen);
+    getPathDataFromAsciiString(&pathData, result, pathStr, strLen);
     if (result->failureOccurred) {
         return;
     }
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 180a7a3..5578e8d 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -39,9 +39,9 @@
     /**
      * Parse the string literal and create a Skia Path. Return true on success.
      */
-    ANDROID_API static void parseStringForSkPath(SkPath* outPath, ParseResult* result,
+    ANDROID_API static void parseAsciiStringForSkPath(SkPath* outPath, ParseResult* result,
             const char* pathStr, size_t strLength);
-    ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
+    ANDROID_API static void getPathDataFromAsciiString(PathData* outData, ParseResult* result,
             const char* pathStr, size_t strLength);
     static void dump(const PathData& data);
     static bool isVerbValid(char verb);
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 0000000..d7df77c
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+#include "Caches.h"
+#include "Image.h"
+#include "GlopBuilder.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "utils/GLUtils.h"
+
+#include <GLES2/gl2.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+bool Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
+        Surface& surface, SkBitmap* bitmap) {
+    // TODO: Clean this up and unify it with LayerRenderer::copyLayer,
+    // of which most of this is copied from.
+    renderThread.eglManager().initialize();
+
+    Caches& caches = Caches::getInstance();
+    RenderState& renderState = renderThread.renderState();
+    int destWidth = bitmap->width();
+    int destHeight = bitmap->height();
+    if (destWidth > caches.maxTextureSize
+                || destHeight > caches.maxTextureSize) {
+        ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d",
+                destWidth, destHeight, caches.maxTextureSize);
+        return false;
+    }
+    GLuint fbo = renderState.createFramebuffer();
+    if (!fbo) {
+        ALOGW("Could not obtain an FBO");
+        return false;
+    }
+
+    SkAutoLockPixels alp(*bitmap);
+
+    GLuint texture;
+
+    GLenum format;
+    GLenum type;
+
+    switch (bitmap->colorType()) {
+        case kAlpha_8_SkColorType:
+            format = GL_ALPHA;
+            type = GL_UNSIGNED_BYTE;
+            break;
+        case kRGB_565_SkColorType:
+            format = GL_RGB;
+            type = GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case kARGB_4444_SkColorType:
+            format = GL_RGBA;
+            type = GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case kN32_SkColorType:
+        default:
+            format = GL_RGBA;
+            type = GL_UNSIGNED_BYTE;
+            break;
+    }
+
+    renderState.bindFramebuffer(fbo);
+
+    // TODO: Use layerPool or something to get this maybe? But since we
+    // need explicit format control we can't currently.
+
+    // Setup the rendertarget
+    glGenTextures(1, &texture);
+    caches.textureState().activateTexture(0);
+    caches.textureState().bindTexture(texture);
+    glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight,
+            0, format, type, nullptr);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+            GL_TEXTURE_2D, texture, 0);
+
+    // Setup the source
+    sp<GraphicBuffer> sourceBuffer;
+    sp<Fence> sourceFence;
+    // FIXME: Waiting on an API from libgui for this
+    // surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
+    if (!sourceBuffer.get()) {
+        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+        return false;
+    }
+    int err = sourceFence->wait(500 /* ms */);
+    if (err != NO_ERROR) {
+        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+        return false;
+    }
+    Image sourceImage(sourceBuffer);
+    if (!sourceImage.getTexture()) {
+        ALOGW("Failed to make an EGLImage from the GraphicBuffer");
+        return false;
+    }
+    Texture sourceTexture(caches);
+    sourceTexture.wrap(sourceImage.getTexture(),
+            sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
+
+    {
+        // Draw & readback
+        renderState.setViewport(destWidth, destHeight);
+        renderState.scissor().setEnabled(false);
+        renderState.blend().syncEnabled();
+        renderState.stencil().disable();
+
+        Rect destRect(destWidth, destHeight);
+        Glop glop;
+        GlopBuilder(renderState, caches, &glop)
+                .setRoundRectClipState(nullptr)
+                .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
+                .setFillLayer(sourceTexture, nullptr, 1.0f, SkXfermode::kSrc_Mode,
+                        Blend::ModeOrderSwap::NoSwap)
+                .setTransform(Matrix4::identity(), TransformFlags::None)
+                .setModelViewMapUnitToRect(destRect)
+                .build();
+        Matrix4 ortho;
+        ortho.loadOrtho(destWidth, destHeight);
+        renderState.render(glop, ortho);
+
+        glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+                type, bitmap->getPixels());
+    }
+
+    // Cleanup
+    caches.textureState().deleteTexture(texture);
+    renderState.deleteFramebuffer(fbo);
+
+    GL_CHECKPOINT(MODERATE);
+
+    return true;
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
new file mode 100644
index 0000000..ea03c82
--- /dev/null
+++ b/libs/hwui/Readback.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "renderthread/RenderThread.h"
+
+#include <SkBitmap.h>
+#include <gui/Surface.h>
+
+namespace android {
+namespace uirenderer {
+
+class Readback {
+public:
+    static bool copySurfaceInto(renderthread::RenderThread& renderThread,
+            Surface& surface, SkBitmap* bitmap);
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index adfe45c..ac17ed2 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -96,7 +96,7 @@
 Path::Path(const char* pathStr, size_t strLength) {
     PathParser::ParseResult result;
     Data data;
-    PathParser::getPathDataFromString(&data, &result, pathStr, strLength);
+    PathParser::getPathDataFromAsciiString(&data, &result, pathStr, strLength);
     mStagingProperties.setData(data);
 }
 
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index e4c7ed7..691cfa01 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -40,11 +40,11 @@
 namespace uirenderer {
 
 namespace VectorDrawable {
-#define VD_SET_PROP_WITH_FLAG(field, value, flag) (VD_SET_PROP_AND_NOTIFY(field, value) ? (flag = true, true) : false)
-#define VD_SET_PROP(field, value) (value != field ? (field = value, true) : false)
-#define VD_SET_PROP_AND_NOTIFY(field, value) ({ bool retVal = VD_SET_PROP(field, value);\
+#define VD_SET_PRIMITIVE_FIELD_WITH_FLAG(field, value, flag) (VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, (value)) ? ((flag) = true, true) : false)
+#define VD_SET_PROP(field, value) ((value) != (field) ? ((field) = (value), true) : false)
+#define VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, value) ({ bool retVal = VD_SET_PROP((mPrimitiveFields.field), (value));\
     onPropertyChanged(); retVal;})
-#define UPDATE_SKPROP(field, value) ({bool retVal = (field != value); if (field != value) SkRefCnt_SafeAssign(field, value); retVal;})
+#define UPDATE_SKPROP(field, value) ({bool retVal = ((field) != (value)); if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); retVal;})
 
 /* A VectorDrawable is composed of a tree of nodes.
  * Each node can be a group node, or a path.
@@ -248,49 +248,49 @@
             return mPrimitiveFields.strokeWidth;
         }
         void setStrokeWidth(float strokeWidth) {
-            VD_SET_PROP_AND_NOTIFY(strokeWidth, strokeWidth);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
         }
         SkColor getStrokeColor() const{
             return mPrimitiveFields.strokeColor;
         }
         void setStrokeColor(SkColor strokeColor) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.strokeColor, strokeColor);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeColor, strokeColor);
         }
         float getStrokeAlpha() const{
             return mPrimitiveFields.strokeAlpha;
         }
         void setStrokeAlpha(float strokeAlpha) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.strokeAlpha, strokeAlpha);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeAlpha, strokeAlpha);
         }
         SkColor getFillColor() const {
             return mPrimitiveFields.fillColor;
         }
         void setFillColor(SkColor fillColor) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.fillColor, fillColor);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillColor, fillColor);
         }
         float getFillAlpha() const{
             return mPrimitiveFields.fillAlpha;
         }
         void setFillAlpha(float fillAlpha) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.fillAlpha, fillAlpha);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(fillAlpha, fillAlpha);
         }
         float getTrimPathStart() const{
             return mPrimitiveFields.trimPathStart;
         }
         void setTrimPathStart(float trimPathStart) {
-            VD_SET_PROP_WITH_FLAG(mPrimitiveFields.trimPathStart, trimPathStart, mTrimDirty);
+            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathStart, trimPathStart, mTrimDirty);
         }
         float getTrimPathEnd() const{
             return mPrimitiveFields.trimPathEnd;
         }
         void setTrimPathEnd(float trimPathEnd) {
-            VD_SET_PROP_WITH_FLAG(mPrimitiveFields.trimPathEnd, trimPathEnd, mTrimDirty);
+            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathEnd, trimPathEnd, mTrimDirty);
         }
         float getTrimPathOffset() const{
             return mPrimitiveFields.trimPathOffset;
         }
         void setTrimPathOffset(float trimPathOffset) {
-            VD_SET_PROP_WITH_FLAG(mPrimitiveFields.trimPathOffset, trimPathOffset, mTrimDirty);
+            VD_SET_PRIMITIVE_FIELD_WITH_FLAG(trimPathOffset, trimPathOffset, mTrimDirty);
         }
 
         float getStrokeMiterLimit() const {
@@ -425,43 +425,43 @@
             return mPrimitiveFields.rotate;
         }
         void setRotation(float rotation) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.rotate, rotation);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(rotate, rotation);
         }
         float getPivotX() const {
             return mPrimitiveFields.pivotX;
         }
         void setPivotX(float pivotX) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.pivotX, pivotX);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotX, pivotX);
         }
         float getPivotY() const {
             return mPrimitiveFields.pivotY;
         }
         void setPivotY(float pivotY) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.pivotY, pivotY);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(pivotY, pivotY);
         }
         float getScaleX() const {
             return mPrimitiveFields.scaleX;
         }
         void setScaleX(float scaleX) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.scaleX, scaleX);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleX, scaleX);
         }
         float getScaleY() const {
             return mPrimitiveFields.scaleY;
         }
         void setScaleY(float scaleY) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.scaleY, scaleY);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(scaleY, scaleY);
         }
         float getTranslateX() const {
             return mPrimitiveFields.translateX;
         }
         void setTranslateX(float translateX) {
-            VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.translateX, translateX);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateX, translateX);
         }
         float getTranslateY() const {
             return mPrimitiveFields.translateY;
         }
         void setTranslateY(float translateY) {
-            VD_SET_PROP_AND_NOTIFY(translateY, translateY);
+            VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(translateY, translateY);
         }
         void updateProperties(float rotate, float pivotX, float pivotY,
                 float scaleX, float scaleY, float translateX, float translateY) {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1116383..096093c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -19,6 +19,7 @@
 #include "DeferredLayerUpdater.h"
 #include "DisplayList.h"
 #include "LayerRenderer.h"
+#include "Readback.h"
 #include "Rect.h"
 #include "renderthread/CanvasContext.h"
 #include "renderthread/RenderTask.h"
@@ -604,6 +605,20 @@
     post(task);
 }
 
+CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread,
+        Surface* surface, SkBitmap* bitmap) {
+    return (void*) Readback::copySurfaceInto(*args->thread,
+            *args->surface, args->bitmap);
+}
+
+bool RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
+    SETUP_TASK(copySurfaceInto);
+    args->bitmap = bitmap;
+    args->surface = surface.get();
+    args->thread = &RenderThread::getInstance();
+    return (bool) staticPostAndWait(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ecc296b..98aace0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -126,6 +126,8 @@
     ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     ANDROID_API long getDroppedFrameReportCount();
 
+    ANDROID_API static bool copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/libs/hwui/tests/microbench/PathParserBench.cpp b/libs/hwui/tests/microbench/PathParserBench.cpp
index 4186539..b43c4c3 100644
--- a/libs/hwui/tests/microbench/PathParserBench.cpp
+++ b/libs/hwui/tests/microbench/PathParserBench.cpp
@@ -31,7 +31,7 @@
     size_t length = strlen(sPathString);
     PathParser::ParseResult result;
     while (state.KeepRunning()) {
-        PathParser::parseStringForSkPath(&skPath, &result, sPathString, length);
+        PathParser::parseAsciiStringForSkPath(&skPath, &result, sPathString, length);
         benchmark::DoNotOptimize(&result);
         benchmark::DoNotOptimize(&skPath);
     }
@@ -43,7 +43,7 @@
     PathData outData;
     PathParser::ParseResult result;
     while (state.KeepRunning()) {
-        PathParser::getPathDataFromString(&outData, &result, sPathString, length);
+        PathParser::getPathDataFromAsciiString(&outData, &result, sPathString, length);
         benchmark::DoNotOptimize(&result);
         benchmark::DoNotOptimize(&outData);
     }
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 796169e..83b485f 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -234,9 +234,10 @@
     {"3e...3", false}, // Not starting with a verb and ill-formatted float
     {"L.M.F.A.O", false}, // No floats following verbs
     {"m 1 1", true}, // Valid path data
-    {"z", true}, // Valid path data
+    {"\n \t   z", true}, // Valid path data with leading spaces
     {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
-    {"f 4 5", false} // Invalid verb
+    {"f 4 5", false}, // Invalid verb
+    {"\r      ", false} // Empty string
 };
 
 
@@ -250,7 +251,7 @@
         // Test generated path data against the given data.
         PathData pathData;
         size_t length = strlen(testData.pathString);
-        PathParser::getPathDataFromString(&pathData, &result, testData.pathString, length);
+        PathParser::getPathDataFromAsciiString(&pathData, &result, testData.pathString, length);
         EXPECT_EQ(testData.pathData, pathData);
     }
 
@@ -258,7 +259,7 @@
         PathParser::ParseResult result;
         PathData pathData;
         SkPath skPath;
-        PathParser::getPathDataFromString(&pathData, &result,
+        PathParser::getPathDataFromAsciiString(&pathData, &result,
                 stringPath.stringPath, strlen(stringPath.stringPath));
         EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
     }
@@ -274,13 +275,13 @@
     }
 }
 
-TEST(PathParser, parseStringForSkPath) {
+TEST(PathParser, parseAsciiStringForSkPath) {
     for (TestData testData: sTestDataSet) {
         PathParser::ParseResult result;
         size_t length = strlen(testData.pathString);
         // Check the return value as well as the SkPath generated.
         SkPath actualPath;
-        PathParser::parseStringForSkPath(&actualPath, &result, testData.pathString, length);
+        PathParser::parseAsciiStringForSkPath(&actualPath, &result, testData.pathString, length);
         bool hasValidData = !result.failureOccurred;
         EXPECT_EQ(hasValidData, testData.pathData.verbs.size() > 0);
         SkPath expectedPath;
@@ -291,7 +292,7 @@
     for (StringPath stringPath : sStringPaths) {
         PathParser::ParseResult result;
         SkPath skPath;
-        PathParser::parseStringForSkPath(&skPath, &result, stringPath.stringPath,
+        PathParser::parseAsciiStringForSkPath(&skPath, &result, stringPath.stringPath,
                 strlen(stringPath.stringPath));
         EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
     }
@@ -390,5 +391,40 @@
         EXPECT_EQ(matrixAndScale.matrixScale, actualMatrixScale);
     }
 }
+
+TEST(VectorDrawable, groupProperties) {
+    //TODO: Also need to test property sync and dirty flag when properties change.
+    VectorDrawable::Group group;
+    VectorDrawable::Group::GroupProperties* properties = group.mutateProperties();
+    // Test default values, change values through setters and verify the change through getters.
+    EXPECT_EQ(0.0f, properties->getTranslateX());
+    properties->setTranslateX(1.0f);
+    EXPECT_EQ(1.0f, properties->getTranslateX());
+
+    EXPECT_EQ(0.0f, properties->getTranslateY());
+    properties->setTranslateY(1.0f);
+    EXPECT_EQ(1.0f, properties->getTranslateY());
+
+    EXPECT_EQ(0.0f, properties->getRotation());
+    properties->setRotation(1.0f);
+    EXPECT_EQ(1.0f, properties->getRotation());
+
+    EXPECT_EQ(1.0f, properties->getScaleX());
+    properties->setScaleX(0.0f);
+    EXPECT_EQ(0.0f, properties->getScaleX());
+
+    EXPECT_EQ(1.0f, properties->getScaleY());
+    properties->setScaleY(0.0f);
+    EXPECT_EQ(0.0f, properties->getScaleY());
+
+    EXPECT_EQ(0.0f, properties->getPivotX());
+    properties->setPivotX(1.0f);
+    EXPECT_EQ(1.0f, properties->getPivotX());
+
+    EXPECT_EQ(0.0f, properties->getPivotY());
+    properties->setPivotY(1.0f);
+    EXPECT_EQ(1.0f, properties->getPivotY());
+
+}
 }; // namespace uirenderer
 }; // namespace android
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index a5550ec..4504e69 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -876,22 +876,24 @@
      * Calling {@link #startRecording()} following a {@link #stop()} will reset
      * the frame count to 0.
      *
-     * @param timestamp a reference to a non-null AudioTimestamp instance.
+     * @param outTimestamp a caller provided non-null AudioTimestamp instance,
+     *        which is updated with the AudioRecord frame delivery information upon success.
      * @param timebase one of
      *        {@link AudioTimestamp#TIMEBASE_BOOTTIME AudioTimestamp.TIMEBASE_BOOTTIME} or
-     *        {@link AudioTimestamp#TIMEBASE_MONOTONIC AudioTimestamp.TIMEBASE_MONOTONIC}.
+     *        {@link AudioTimestamp#TIMEBASE_MONOTONIC AudioTimestamp.TIMEBASE_MONOTONIC},
+     *        used to select the clock for the AudioTimestamp time.
      * @return {@link #SUCCESS} if a timestamp is available,
      *         or {@link #ERROR_INVALID_OPERATION} if a timestamp not available.
      */
-     public int getTimestamp(@NonNull AudioTimestamp timestamp,
+     public int getTimestamp(@NonNull AudioTimestamp outTimestamp,
              @AudioTimestamp.Timebase int timebase)
      {
-         if (timestamp == null ||
+         if (outTimestamp == null ||
                  (timebase != AudioTimestamp.TIMEBASE_BOOTTIME
                  && timebase != AudioTimestamp.TIMEBASE_MONOTONIC)) {
              throw new IllegalArgumentException();
          }
-         return native_get_timestamp(timestamp, timebase);
+         return native_get_timestamp(outTimestamp, timebase);
      }
 
     /**
@@ -1725,7 +1727,7 @@
     private native final void native_enableDeviceCallback();
     private native final void native_disableDeviceCallback();
 
-    private native final int native_get_timestamp(@NonNull AudioTimestamp timestamp,
+    private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
             @AudioTimestamp.Timebase int timebase);
 
     //---------------------------------------------------------
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 03dc699..0b64876 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -369,8 +369,7 @@
     }
 
     /**
-     * Returns the extras associated with this TV input.
-     * @hide
+     * Returns domain-specific extras associated with this TV input.
      */
     public Bundle getExtras() {
         return mExtras;
@@ -847,11 +846,12 @@
         }
 
         /**
-         * Sets the extras associated with this TV input.
+         * Sets domain-specific extras associated with this TV input.
          *
-         * @param extras The extras associated with this TV input.
+         * @param extras Domain-specific extras associated with this TV input. Keys <em>must</em> be
+         *            a scoped name, i.e. prefixed with a package name you own, so that different
+         *            developers will not create conflicting keys.
          * @return This Builder object to allow for chaining of calls to builder methods.
-         * @hide
          */
         public Builder setExtras(Bundle extras) {
             this.mExtras = extras;
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
index d388388..9096aeb 100644
--- a/media/jni/soundpool/SoundPoolThread.h
+++ b/media/jni/soundpool/SoundPoolThread.h
@@ -47,7 +47,7 @@
     void write(SoundPoolMsg msg);
 
 private:
-    static const size_t maxMessages = 5;
+    static const size_t maxMessages = 128;
 
     static int beginThread(void* arg);
     int run();
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 3197abd..568e200 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
+LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/packages/DocumentsUI/res/color/item_root_icon.xml b/packages/DocumentsUI/res/color/item_root_icon.xml
index 0aa2c13..e1d7e61 100644
--- a/packages/DocumentsUI/res/color/item_root_icon.xml
+++ b/packages/DocumentsUI/res/color/item_root_icon.xml
@@ -15,5 +15,10 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@*android:color/secondary_text_material_light" />
+    <item
+        android:state_activated="false"
+        android:color="@*android:color/secondary_text_material_light" />
+    <item
+        android:state_activated="true"
+        android:color="@color/root_activated_color" />
 </selector>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index f6fe47b..3373c23 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -147,17 +147,13 @@
         }
     }
 
-    // Hamburger if drawer is present, else root icon, or sad nullness.
+    // Hamburger if drawer is present, else sad nullness.
     private @Nullable Drawable getActionBarIcon() {
         if (mDrawer.isPresent()) {
             return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger);
         } else {
-            RootInfo root = mEnv.getCurrentRoot();
-            if (root != null) {
-                return root.loadToolbarIcon(mToolbar.getContext());
-            }
+            return null;
         }
-        return null;
     }
 
     void revealRootsDrawer(boolean open) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 8fcd9d1..babde99 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -17,7 +17,6 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.Shared.TAG;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
 import android.content.ClipData;
@@ -45,6 +44,8 @@
  * Provides support for gather a list of quick-viewable files into a quick view intent.
  */
 final class QuickViewIntentBuilder {
+
+    private static final String TAG = "QuickViewIntentBuilder";
     private static final int MAX_CLIP_ITEMS = 1000;
 
     private final DocumentInfo mDocument;
@@ -127,8 +128,18 @@
         for (int i = 0; i < siblingIds.length; i++) {
             cursor = mModel.getItem(siblingIds[i]);
 
+            if (cursor == null) {
+                if (DEBUG) Log.d(TAG,
+                        "Unable to obtain cursor for sibling document, modelId: "
+                        + siblingIds[i]);
+                continue;
+            }
+
             mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
             if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+                if (DEBUG) Log.d(TAG,
+                        "Skipping directory, not supported by quick view. modelId: "
+                        + siblingIds[i]);
                 continue;
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index aa9f356..fba25ab 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -351,7 +351,10 @@
     private boolean handleViewItem(String id) {
         final Cursor cursor = mModel.getItem(id);
 
-        assert(cursor != null);
+        if (cursor == null) {
+            Log.w(TAG, "Can't view item. Can't obtain cursor for modeId" + id);
+            return false;
+        }
 
         final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
         final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
@@ -465,11 +468,14 @@
         public boolean onBeforeItemStateChange(String modelId, boolean selected) {
             if (selected) {
                 final Cursor cursor = mModel.getItem(modelId);
-
-                assert(cursor != null);
+                if (cursor == null) {
+                    Log.w(TAG, "Can't obtain cursor for modelId: " + modelId);
+                    return false;
+                }
 
                 final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
                 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+
                 return mTuner.canSelectType(docMimeType, docFlags);
             }
             return true;
@@ -479,7 +485,7 @@
         public void onItemStateChanged(String modelId, boolean selected) {
             final Cursor cursor = mModel.getItem(modelId);
             if (cursor == null) {
-                Log.e(TAG, "Model returned null cursor for document: " + modelId
+                Log.w(TAG, "Model returned null cursor for document: " + modelId
                         + ". Ignoring state changed event.");
                 return;
             }
@@ -1104,6 +1110,10 @@
         List<String> enabled = new ArrayList<String>();
         for (String id : mAdapter.getModelIds()) {
             Cursor cursor = getModel().getItem(id);
+            if (cursor == null) {
+                Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id);
+                continue;
+            }
             String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
             int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
             if (isDocumentEnabled(docMimeType, docFlags)) {
@@ -1192,7 +1202,10 @@
             String id = getModelId(v);
             if (id != null) {
                 Cursor dstCursor = mModel.getItem(id);
-                assert(dstCursor != null);
+                if (dstCursor == null) {
+                    Log.w(TAG, "Invalid destination. Can't obtain cursor for modelId: " + id);
+                    return null;
+                }
                 return DocumentInfo.fromDirectoryCursor(dstCursor);
             }
 
@@ -1265,8 +1278,10 @@
         }
 
         final Cursor cursor = mModel.getItem(modelId);
-
-        assert(cursor != null);
+        if (cursor == null) {
+            Log.w(TAG, "Undraggable document. Can't obtain cursor for modelId " + modelId);
+            return Collections.EMPTY_LIST;
+        }
 
         return Lists.newArrayList(
                 DocumentInfo.fromDirectoryCursor(cursor));
@@ -1395,7 +1410,7 @@
                 // Handle range selection adjustments. Extending the selection will adjust the
                 // bounds of the in-progress range selection. Each time an unshifted navigation
                 // event is received, the range selection is restarted.
-                if (shouldExtendSelection(event)) {
+                if (shouldExtendSelection(doc, event)) {
                     if (!mSelectionManager.isRangeSelectionActive()) {
                         // Start a range selection if one isn't active
                         mSelectionManager.startRangeSelection(doc.getAdapterPosition());
@@ -1432,9 +1447,22 @@
             return false;
         }
 
-        private boolean shouldExtendSelection(KeyEvent event) {
-            return Events.isNavigationKeyCode(event.getKeyCode()) &&
-                    event.isShiftPressed();
+        private boolean shouldExtendSelection(DocumentHolder doc, KeyEvent event) {
+            if (!Events.isNavigationKeyCode(event.getKeyCode()) || !event.isShiftPressed()) {
+                return false;
+            }
+
+            // TODO: Combine this method with onBeforeItemStateChange, as both of them are almost
+            // the same, and responsible for the same thing (whether to select or not).
+            final Cursor cursor = mModel.getItem(doc.modelId);
+            if (cursor == null) {
+                Log.w(TAG, "Couldn't obtain cursor for modelId: " + doc.modelId);
+                return false;
+            }
+
+            final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+            final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+            return mTuner.canSelectType(docMimeType, docFlags);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
index ac05c05..f274df3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -20,6 +20,7 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.database.Cursor;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
@@ -476,9 +477,9 @@
             List<String> index = new ArrayList<>(itemCount);
             for (int i = 0; i < itemCount; i++) {
                 String modelId = mAdapter.getModelId(i);
-                if (modelId != null) {
-                    String title =
-                            getCursorString(mModel.getItem(modelId), Document.COLUMN_DISPLAY_NAME);
+                Cursor cursor = mModel.getItem(modelId);
+                if (modelId != null && cursor != null) {
+                    String title = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
                     // Perform case-insensitive search.
                     index.add(title.toLowerCase());
                 } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index 016cc9e..e6217b2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -195,6 +195,10 @@
      */
     private static final class FilesTuner extends FragmentTuner {
 
+        // We use this to keep track of whether a model has been previously loaded or not so we can
+        // open the drawer on empty directories on first launch
+        private boolean mModelPreviousLoaded;
+
         public FilesTuner(Context context, State state) {
             super(context, state);
         }
@@ -230,10 +234,12 @@
         @Override
         void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
             // When launched into empty root, open drawer.
-            if (model.isEmpty() && !mState.hasInitialLocationChanged() && !isSearch) {
+            if (model.isEmpty() && !mState.hasInitialLocationChanged() && !isSearch
+                    && !mModelPreviousLoaded) {
                 // This noops on layouts without drawer, so no need to guard.
                 ((BaseActivity) mContext).setRootsDrawerOpen(true);
             }
+            mModelPreviousLoaded = true;
         }
 
         @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
index 3642b01..e2a28ad9a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
@@ -37,6 +37,7 @@
 import com.android.documentsui.model.DocumentInfo;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -403,11 +404,18 @@
 
     public @Nullable Cursor getItem(String modelId) {
         Integer pos = mPositions.get(modelId);
-        if (pos != null) {
-            mCursor.moveToPosition(pos);
-            return mCursor;
+        if (pos == null) {
+            if (DEBUG) Log.d(TAG, "Unabled to find cursor position for modelId: " + modelId);
+            return null;
         }
-        return null;
+
+        if (!mCursor.moveToPosition(pos)) {
+            if (DEBUG) Log.d(TAG,
+                    "Unabled to move cursor to position " + pos + " for modelId: " + modelId);
+            return null;
+        }
+
+        return mCursor;
     }
 
     boolean isEmpty() {
@@ -424,8 +432,11 @@
         final List<DocumentInfo> docs =  new ArrayList<>(size);
         for (String modelId: items.getAll()) {
             final Cursor cursor = getItem(modelId);
-            assert(cursor != null);
-
+            if (cursor == null) {
+                Log.w(TAG,
+                        "Skipping document. Unabled to obtain cursor for modelId: " + modelId);
+                continue;
+            }
             docs.add(DocumentInfo.fromDirectoryCursor(cursor));
         }
         return docs;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index 1285b34..35d8988 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -378,8 +378,8 @@
      * @param pos The anchor position for the selection range.
      */
     void startRangeSelection(int pos) {
-      attemptSelect(mAdapter.getModelId(pos));
-      setSelectionRangeBegin(pos);
+        attemptSelect(mAdapter.getModelId(pos));
+        setSelectionRangeBegin(pos);
     }
 
     /**
@@ -1268,6 +1268,11 @@
             notifySelectionChanged();
         }
 
+        @Override
+        public boolean onBeforeItemStateChange(String id, boolean nextState) {
+            return notifyBeforeItemStateChange(id, nextState);
+        }
+
         private class ViewScroller implements Runnable {
             /**
              * The number of milliseconds of scrolling at which scroll speed continues to increase.
@@ -1655,7 +1660,9 @@
                         if (id != null) {
                             // The adapter inserts items for UI layout purposes that aren't associated
                             // with files.  Those will have a null model ID.  Don't select them.
-                            mSelection.add(id);
+                            if (canSelect(id)) {
+                                mSelection.add(id);
+                            }
                         }
                         if (isPossiblePositionNearestOrigin(column, columnStartIndex, columnEndIndex,
                                 row, rowStartIndex, rowEndIndex)) {
@@ -1669,6 +1676,21 @@
         }
 
         /**
+         * @return True if the item is selectable.
+         */
+        private boolean canSelect(String id) {
+            // TODO: Simplify the logic, so the check whether we can select is done in one place.
+            // Consider injecting FragmentTuner, or move the checks from MultiSelectManager to
+            // Selection.
+            for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) {
+                if (!listener.onBeforeItemStateChange(id, true)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /**
          * @return Returns true if the position is the nearest to the origin, or, in the case of the
          *     lower-right corner, whether it is possible that the position is the nearest to the
          *     origin. See comment below for reasoning for this special case.
@@ -1700,6 +1722,7 @@
          */
         static interface OnSelectionChangedListener {
             public void onSelectionChanged(Set<String> updatedSelection);
+            public boolean onBeforeItemStateChange(String id, boolean nextState);
         }
 
         void addOnSelectionChangedListener(OnSelectionChangedListener listener) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 31ce837..3a86a51 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -139,11 +139,13 @@
     };
 
     public static DocumentInfo fromDirectoryCursor(Cursor cursor) {
+        assert(cursor != null);
         final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
         return fromCursor(cursor, authority);
     }
 
     public static DocumentInfo fromCursor(Cursor cursor, String authority) {
+        assert(cursor != null);
         final DocumentInfo info = new DocumentInfo();
         info.updateFromCursor(cursor, authority);
         return info;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 0709652..d54bdfd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -334,15 +334,6 @@
         }
     }
 
-    public Drawable loadToolbarIcon(Context context) {
-        if (derivedIcon != 0) {
-            return IconUtils.applyTintAttr(context, derivedIcon,
-                    android.R.attr.colorControlNormal);
-        } else {
-            return IconUtils.loadPackageIcon(context, authority, icon);
-        }
-    }
-
     @Override
     public boolean equals(Object o) {
         if (o == null) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
index 0c0e0b7..cc119fe 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
@@ -76,6 +76,11 @@
                     public void onSelectionChanged(Set<String> updatedSelection) {
                         lastSelection = updatedSelection;
                     }
+
+                    @Override
+                    public boolean onBeforeItemStateChange(String id, boolean nextState) {
+                        return true;
+                    }
                 });
     }
 
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 00d31a7..90dd440 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -16,6 +16,7 @@
 
 package com.android.mtp;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
@@ -27,6 +28,7 @@
 import android.mtp.MtpDeviceInfo;
 import android.mtp.MtpEvent;
 import android.mtp.MtpObjectInfo;
+import android.mtp.MtpStorageInfo;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
@@ -80,9 +82,7 @@
             }
         }
 
-        if (rawDevice == null) {
-            throw new IOException("Not found USB device: " + deviceId);
-        }
+        ensureNotNull(rawDevice, "Not found USB device: " + deviceId);
 
         if (!mManager.hasPermission(rawDevice)) {
             mManager.grantPermission(rawDevice);
@@ -93,10 +93,9 @@
 
         final MtpDevice device = new MtpDevice(rawDevice);
 
-        final UsbDeviceConnection connection = mManager.openDevice(rawDevice);
-        if (connection == null) {
-            throw new IOException("Failed to open a USB connection.");
-        }
+        final UsbDeviceConnection connection = ensureNotNull(
+                mManager.openDevice(rawDevice),
+                "Failed to open a USB connection.");
 
         if (!device.open(connection)) {
             // We cannot open connection when another application use the device.
@@ -104,13 +103,11 @@
         }
 
         // Handle devices that fail to obtain storages just after opening a MTP session.
-        final int[] storageIds = device.getStorageIds();
-        if (storageIds == null) {
-            throw new IOException("Not found MTP storages in the device.");
-        }
+        final int[] storageIds = ensureNotNull(
+                device.getStorageIds(),
+                "Not found MTP storages in the device.");
 
         mDevices.put(deviceId, device);
-
         return createDeviceRecord(rawDevice);
     }
 
@@ -133,11 +130,9 @@
     MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            final MtpObjectInfo info = device.getObjectInfo(objectHandle);
-            if (info == null) {
-                throw new IOException("Failed to get object info: " + objectHandle);
-            }
-            return info;
+            return ensureNotNull(
+                    device.getObjectInfo(objectHandle),
+                    "Failed to get object info: " + objectHandle);
         }
     }
 
@@ -145,12 +140,9 @@
             throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            final int[] handles =
-                    device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
-            if (handles == null) {
-                throw new IOException("Failed to fetch object handles.");
-            }
-            return handles;
+            return ensureNotNull(
+                    device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle),
+                    "Failed to fetch object handles.");
         }
     }
 
@@ -158,7 +150,9 @@
             throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            return device.getObject(objectHandle, expectedSize);
+            return ensureNotNull(
+                    device.getObject(objectHandle, expectedSize),
+                    "Failed to fetch object bytes");
         }
     }
 
@@ -181,7 +175,9 @@
     byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            return device.getThumbnail(objectHandle);
+            return ensureNotNull(
+                    device.getThumbnail(objectHandle),
+                    "Failed to obtain thumbnail bytes");
         }
     }
 
@@ -216,7 +212,7 @@
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
             final int result = (int) device.getParent(objectHandle);
-            if (result < 0) {
+            if (result == 0xffffffff) {
                 throw new FileNotFoundException("Not found parent object");
             }
             return result;
@@ -227,7 +223,9 @@
             throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            device.importFile(objectHandle, target);
+            if (!device.importFile(objectHandle, target)) {
+                throw new IOException("Failed to import file to FD");
+            }
         }
     }
 
@@ -243,26 +241,25 @@
     }
 
     private synchronized MtpDevice getDevice(int deviceId) throws IOException {
-        final MtpDevice device = mDevices.get(deviceId);
-        if (device == null) {
-            throw new IOException("USB device " + deviceId + " is not opened.");
-        }
-        return device;
+        return ensureNotNull(
+                mDevices.get(deviceId),
+                "USB device " + deviceId + " is not opened.");
     }
 
     private MtpRoot[] getRoots(int deviceId) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
-            final int[] storageIds = device.getStorageIds();
-            if (storageIds == null) {
-                throw new IOException("Failed to obtain storage IDs.");
-            }
-            final MtpRoot[] results = new MtpRoot[storageIds.length];
+            final int[] storageIds =
+                    ensureNotNull(device.getStorageIds(), "Failed to obtain storage IDs.");
+            final ArrayList<MtpRoot> roots = new ArrayList<>();
             for (int i = 0; i < storageIds.length; i++) {
-                results[i] = new MtpRoot(
-                        device.getDeviceId(), device.getStorageInfo(storageIds[i]));
+                final MtpStorageInfo info = device.getStorageInfo(storageIds[i]);
+                if (info == null) {
+                    continue;
+                }
+                roots.add(new MtpRoot(device.getDeviceId(), info));
             }
-            return results;
+            return roots.toArray(new MtpRoot[roots.size()]);
         }
     }
 
@@ -313,4 +310,12 @@
         }
         return false;
     }
+
+    private static <T> T ensureNotNull(@Nullable T t, String errorMessage) throws IOException {
+        if (t != null) {
+            return t;
+        } else {
+            throw new IOException(errorMessage);
+        }
+    }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index e7aebdd..c411186 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -320,6 +320,8 @@
                 mFileProvider, new RemotePrintDocument.RemoteAdapterDeathObserver() {
             @Override
             public void onDied() {
+                Log.w(LOG_TAG, "Printing app died unexpectedly");
+
                 // If we are finishing or we are in a state that we do not need any
                 // data from the printing app, then no need to finish.
                 if (isFinishing() || (isFinalState(mState) && !mPrintedDocument.isUpdating())) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 084acac..985fe3c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -804,7 +804,7 @@
     <string name="disabled_by_admin">Disabled by administrator</string>
 
     <!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
-    <string name="home">Home</string>
+    <string name="home">Settings Home</string>
 
     <string-array name="battery_labels" translatable="false">
         <item>0%</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index d353f31..8881034 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -534,6 +534,7 @@
         Comparator<AppEntry> mRebuildComparator;
         ArrayList<AppEntry> mRebuildResult;
         ArrayList<AppEntry> mLastAppList;
+        boolean mRebuildForeground;
 
         Session(Callbacks callbacks) {
             mCallbacks = callbacks;
@@ -572,6 +573,11 @@
 
         // Creates a new list of app entries with the given filter and comparator.
         public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+            return rebuild(filter, comparator, true);
+        }
+
+        public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
+                boolean foreground) {
             synchronized (mRebuildSync) {
                 synchronized (mEntriesMap) {
                     mRebuildingSessions.add(this);
@@ -579,6 +585,7 @@
                     mRebuildAsync = false;
                     mRebuildFilter = filter;
                     mRebuildComparator = comparator;
+                    mRebuildForeground = foreground;
                     mRebuildResult = null;
                     if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
                         Message msg = mBackgroundHandler.obtainMessage(
@@ -620,10 +627,12 @@
                 mRebuildRequested = false;
                 mRebuildFilter = null;
                 mRebuildComparator = null;
+                if (mRebuildForeground) {
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+                    mRebuildForeground = false;
+                }
             }
 
-            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
             if (filter != null) {
                 filter.init();
             }
@@ -640,7 +649,10 @@
                 if (filter == null || filter.filterApp(entry)) {
                     synchronized (mEntriesMap) {
                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
-                        entry.ensureLabel(mContext);
+                        if (comparator != null) {
+                            // Only need the label if we are going to be sorting.
+                            entry.ensureLabel(mContext);
+                        }
                         if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
                         filteredApps.add(entry);
                         if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
@@ -648,7 +660,9 @@
                 }
             }
 
-            Collections.sort(filteredApps, comparator);
+            if (comparator != null) {
+                Collections.sort(filteredApps, comparator);
+            }
 
             synchronized (mRebuildSync) {
                 if (!mRebuildRequested) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index ff70190..bcbc6ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -216,6 +216,8 @@
         if (sDashboardCategories == null) {
             sTileCache = new HashMap<>();
             sConfigTracker = new InterestingConfigChanges();
+            // Apply initial current config.
+            sConfigTracker.applyNewConfig(getResources());
             sDashboardCategories = TileUtils.getCategories(this, sTileCache);
         }
         return sDashboardCategories;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 640399f..8dc247a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1324,6 +1324,9 @@
     }
 
     private static void appendSettingToCursor(MatrixCursor cursor, Setting setting) {
+        if (setting == null) {
+            return;
+        }
         final int columnCount = cursor.getColumnCount();
 
         String[] values =  new String[columnCount];
diff --git a/packages/Shell/res/layout/confirm_repeat.xml b/packages/Shell/res/layout/confirm_repeat.xml
index d12f467..ad90af1 100644
--- a/packages/Shell/res/layout/confirm_repeat.xml
+++ b/packages/Shell/res/layout/confirm_repeat.xml
@@ -38,5 +38,5 @@
         android:id="@android:id/checkbox"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/bugreport_confirm_repeat" />
+        android:text="@string/bugreport_confirm_dont_repeat" />
 </LinearLayout>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 5d90189..95e36fd 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -20,8 +20,6 @@
     <string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
     <!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
     <string name="bugreport_finished_title">Bug report <xliff:g id="id">#%d</xliff:g> captured</string>
-    <!-- Title of notification indicating a bugreport has been successfully captured, but screenshot is not finished yet. [CHAR LIMIT=50] -->
-    <string name="bugreport_finished_pending_screenshot_title">Bug report <xliff:g id="id">#%d</xliff:g> captured but screenshot pending</string>
     <!-- Title of notification indicating a bugreport is being updated before it can be shared. [CHAR LIMIT=50] -->
     <string name="bugreport_updating_title">Adding details to the bug report</string>
     <!-- Content notification indicating a bugreport is being updated before it can be shared, asking the user to wait [CHAR LIMIT=50] -->
@@ -37,9 +35,9 @@
     <string name="bugreport_finished_pending_screenshot_text" product="default">Tap to share your bug report without a screenshot or wait for the screenshot to finish</string>
 
     <!-- Body of dialog informing user about contents of a bugreport. [CHAR LIMIT=NONE] -->
-    <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, including personal and private information.  Only share bug reports with apps and people you trust.</string>
-    <!-- Checkbox that indicates this dialog should be shown again when the next bugreport is taken. [CHAR LIMIT=50] -->
-    <string name="bugreport_confirm_repeat">Show this message next time</string>
+    <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, which may include data you consider sensitive (such as app-usage and location data). Only share bug reports with people and apps you trust.</string>
+    <!-- Checkbox that indicates this dialog should not be shown again when the next bugreport is taken. [CHAR LIMIT=50] -->
+    <string name="bugreport_confirm_dont_repeat">Don\'t show again</string>
 
     <!-- Title for documents backend that offers bugreports. -->
     <string name="bugreport_storage_title">Bug reports</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportPrefs.java b/packages/Shell/src/com/android/shell/BugreportPrefs.java
index 3748e89..93690d4 100644
--- a/packages/Shell/src/com/android/shell/BugreportPrefs.java
+++ b/packages/Shell/src/com/android/shell/BugreportPrefs.java
@@ -22,22 +22,24 @@
 /**
  * Preferences related to bug reports.
  */
-public class BugreportPrefs {
-    private static final String PREFS_BUGREPORT = "bugreports";
+final class BugreportPrefs {
+    static final String PREFS_BUGREPORT = "bugreports";
 
     private static final String KEY_WARNING_STATE = "warning-state";
 
-    public static final int STATE_UNKNOWN = 0;
-    public static final int STATE_SHOW = 1;
-    public static final int STATE_HIDE = 2;
+    static final int STATE_UNKNOWN = 0;
+    // Shows the warning dialog.
+    static final int STATE_SHOW = 1;
+    // Skips the warning dialog.
+    static final int STATE_HIDE = 2;
 
-    public static int getWarningState(Context context, int def) {
+    static int getWarningState(Context context, int def) {
         final SharedPreferences prefs = context.getSharedPreferences(
                 PREFS_BUGREPORT, Context.MODE_PRIVATE);
         return prefs.getInt(KEY_WARNING_STATE, def);
     }
 
-    public static void setWarningState(Context context, int value) {
+    static void setWarningState(Context context, int value) {
         final SharedPreferences prefs = context.getSharedPreferences(
                 PREFS_BUGREPORT, Context.MODE_PRIVATE);
         prefs.edit().putInt(KEY_WARNING_STATE, value).apply();
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7ca7614..502eed1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -17,7 +17,8 @@
 package com.android.shell;
 
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-import static com.android.shell.BugreportPrefs.STATE_SHOW;
+import static com.android.shell.BugreportPrefs.STATE_HIDE;
+import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
 import static com.android.shell.BugreportPrefs.getWarningState;
 
 import java.io.BufferedOutputStream;
@@ -220,6 +221,7 @@
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
         if (intent != null) {
             // Handle it in a separate thread.
             final Message msg = mMainHandler.obtainMessage();
@@ -297,6 +299,7 @@
                 return;
             }
             final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+            Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel));
             final Intent intent;
             if (parcel instanceof Intent) {
                 // The real intent was passed to BugreportReceiver, which delegated to the service.
@@ -707,7 +710,8 @@
             for (int i = 0; i < mProcesses.size(); i++) {
                 final BugreportInfo info = mProcesses.valueAt(i);
                 if (info.finished) {
-                    Log.d(TAG, "Not updating progress because share notification was already sent");
+                    Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot"
+                            + " because share notification was already sent");
                     continue;
                 }
                 updateProgress(info);
@@ -846,7 +850,15 @@
     private static Intent buildSendIntent(Context context, BugreportInfo info) {
         // Files are kept on private storage, so turn into Uris that we can
         // grant temporary permissions for.
-        final Uri bugreportUri = getUri(context, info.bugreportFile);
+        final Uri bugreportUri;
+        try {
+            bugreportUri = getUri(context, info.bugreportFile);
+        } catch (IllegalArgumentException e) {
+            // Should not happen on production, but happens when a Shell is sideloaded and
+            // FileProvider cannot find a configured root for it.
+            Log.wtf(TAG, "Could not get URI for " + info.bugreportFile, e);
+            return null;
+        }
 
         final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
         final String mimeType = "application/vnd.android.bugreport";
@@ -907,10 +919,16 @@
         addDetailsToZipFile(mContext, info);
 
         final Intent sendIntent = buildSendIntent(mContext, info);
+        if (sendIntent == null) {
+            Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
+            stopProgress(id);
+            return;
+        }
+
         final Intent notifIntent;
 
         // Send through warning dialog by default
-        if (getWarningState(mContext, STATE_SHOW) == STATE_SHOW) {
+        if (getWarningState(mContext, STATE_UNKNOWN) != STATE_HIDE) {
             notifIntent = buildWarningIntent(mContext, sendIntent);
         } else {
             notifIntent = sendIntent;
@@ -939,15 +957,10 @@
         shareIntent.putExtra(EXTRA_ID, info.id);
         shareIntent.putExtra(EXTRA_INFO, info);
 
-        final String title, content;
-        if (takingScreenshot) {
-            title = context.getString(R.string.bugreport_finished_pending_screenshot_title,
-                    info.id);
-            content = context.getString(R.string.bugreport_finished_pending_screenshot_text);
-        } else {
-            title = context.getString(R.string.bugreport_finished_title, info.id);
-            content = context.getString(R.string.bugreport_finished_text);
-        }
+        final String title = context.getString(R.string.bugreport_finished_title, info.id);
+        final String content = takingScreenshot ?
+                context.getString(R.string.bugreport_finished_pending_screenshot_text)
+                : context.getString(R.string.bugreport_finished_text);
         final Notification.Builder builder = newBaseNotification(context)
                 .setContentTitle(title)
                 .setTicker(title)
@@ -1165,6 +1178,52 @@
         }
     }
 
+    /**
+     * Dumps an intent, extracting the relevant extras.
+     */
+    static String dumpIntent(Intent intent) {
+        if (intent == null) {
+            return "NO INTENT";
+        }
+        String action = intent.getAction();
+        if (action == null) {
+            // Happens when BugreportReceiver calls startService...
+            action = "no action";
+        }
+        final StringBuilder buffer = new StringBuilder(action).append(" extras: ");
+        addExtra(buffer, intent, EXTRA_ID);
+        addExtra(buffer, intent, EXTRA_PID);
+        addExtra(buffer, intent, EXTRA_MAX);
+        addExtra(buffer, intent, EXTRA_NAME);
+        addExtra(buffer, intent, EXTRA_DESCRIPTION);
+        addExtra(buffer, intent, EXTRA_BUGREPORT);
+        addExtra(buffer, intent, EXTRA_SCREENSHOT);
+        addExtra(buffer, intent, EXTRA_INFO);
+
+        if (intent.hasExtra(EXTRA_ORIGINAL_INTENT)) {
+            buffer.append(SHORT_EXTRA_ORIGINAL_INTENT).append(": ");
+            final Intent originalIntent = intent.getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+            buffer.append(dumpIntent(originalIntent));
+        } else {
+            buffer.append("no ").append(SHORT_EXTRA_ORIGINAL_INTENT);
+        }
+
+        return buffer.toString();
+    }
+
+    private static final String SHORT_EXTRA_ORIGINAL_INTENT =
+            EXTRA_ORIGINAL_INTENT.substring(EXTRA_ORIGINAL_INTENT.lastIndexOf('.') + 1);
+
+    private static void addExtra(StringBuilder buffer, Intent intent, String name) {
+        final String shortName = name.substring(name.lastIndexOf('.') + 1);
+        if (intent.hasExtra(name)) {
+            buffer.append(shortName).append('=').append(intent.getExtra(name));
+        } else {
+            buffer.append("no ").append(shortName);
+        }
+        buffer.append(", ");
+    }
+
     private static boolean setSystemProperty(String key, String value) {
         try {
             if (DEBUG) Log.v(TAG, "Setting system property " + key + " to " + value);
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index cbd17bf..f6e558f 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -20,6 +20,7 @@
 import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
 import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
 import static com.android.shell.BugreportProgressService.getFileExtra;
+import static com.android.shell.BugreportProgressService.dumpIntent;
 
 import java.io.File;
 
@@ -51,7 +52,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        Log.d(TAG, "onReceive: " + intent);
+        Log.d(TAG, "onReceive(): " + dumpIntent(intent));
         // Clean up older bugreports in background
         cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE);
 
diff --git a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
index a1d879a..da33a65 100644
--- a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
+++ b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
@@ -59,7 +59,7 @@
         ap.mNegativeButtonListener = this;
 
         mConfirmRepeat = (CheckBox) ap.mView.findViewById(android.R.id.checkbox);
-        mConfirmRepeat.setChecked(getWarningState(this, STATE_UNKNOWN) == STATE_SHOW);
+        mConfirmRepeat.setChecked(getWarningState(this, STATE_UNKNOWN) != STATE_SHOW);
 
         setupAlert();
     }
@@ -68,7 +68,7 @@
     public void onClick(DialogInterface dialog, int which) {
         if (which == AlertDialog.BUTTON_POSITIVE) {
             // Remember confirm state, and launch target
-            setWarningState(this, mConfirmRepeat.isChecked() ? STATE_SHOW : STATE_HIDE);
+            setWarningState(this, mConfirmRepeat.isChecked() ? STATE_HIDE : STATE_SHOW);
             startActivity(mSendIntent);
         }
 
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 3b53055..537e4c5 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -17,7 +17,14 @@
 package com.android.shell;
 
 import static android.test.MoreAsserts.assertContainsRegex;
+
 import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
+import static com.android.shell.BugreportPrefs.PREFS_BUGREPORT;
+import static com.android.shell.BugreportPrefs.STATE_HIDE;
+import static com.android.shell.BugreportPrefs.STATE_SHOW;
+import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
+import static com.android.shell.BugreportPrefs.getWarningState;
+import static com.android.shell.BugreportPrefs.setWarningState;
 import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
 import static com.android.shell.BugreportProgressService.EXTRA_ID;
 import static com.android.shell.BugreportProgressService.EXTRA_MAX;
@@ -48,12 +55,12 @@
 import java.util.zip.ZipOutputStream;
 
 import libcore.io.Streams;
+
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningServiceInfo;
 import android.app.Instrumentation;
 import android.app.NotificationManager;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -62,7 +69,6 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.text.TextUtils;
@@ -70,7 +76,6 @@
 import android.util.Log;
 
 import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
-import com.android.shell.BugreportProgressService;
 
 /**
  * Integration tests for {@link BugreportReceiver}.
@@ -131,9 +136,6 @@
     private static final boolean RENAMED_SCREENSHOTS = true;
     private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
 
-    private static final boolean PENDING_SCREENSHOT = true;
-    private static final boolean NOT_PENDING_SCREENSHOT = false;
-
     private String mDescription;
 
     private String mPlainTextPath;
@@ -171,7 +173,7 @@
         }
         mDescription = sb.toString();
 
-        BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_HIDE);
+        setWarningState(mContext, STATE_HIDE);
     }
 
     public void testProgress() throws Exception {
@@ -432,7 +434,7 @@
         sendBugreportStarted(ID2, PID2, NAME2, 1000);
 
         sendBugreportFinished(ID, mZipPath, mScreenshotPath);
-        Bundle extras = acceptBugreportAndGetSharedIntent(ID, PENDING_SCREENSHOT);
+        Bundle extras = acceptBugreportAndGetSharedIntent(ID);
 
         detailsUi = new DetailsUi(mUiBot, ID2);
         detailsUi.assertName(NAME2);
@@ -503,9 +505,29 @@
         assertServiceNotRunning();
     }
 
-    public void testBugreportFinished_withWarning() throws Exception {
-        // Explicitly shows the warning.
-        BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
+    public void testBugreportFinished_withWarningFirstTime() throws Exception {
+        bugreportFinishedWithWarningTest(null);
+    }
+
+    public void testBugreportFinished_withWarningUnknownState() throws Exception {
+        bugreportFinishedWithWarningTest(STATE_UNKNOWN);
+    }
+
+    public void testBugreportFinished_withWarningShowAgain() throws Exception {
+        bugreportFinishedWithWarningTest(STATE_SHOW);
+    }
+
+    private void bugreportFinishedWithWarningTest(Integer propertyState) throws Exception {
+        if (propertyState == null) {
+            // Clear properties
+            mContext.getSharedPreferences(PREFS_BUGREPORT, Context.MODE_PRIVATE)
+                    .edit().clear().commit();
+            // Sanity check...
+            assertEquals("Did not reset properties", STATE_UNKNOWN,
+                    getWarningState(mContext, STATE_UNKNOWN));
+        } else {
+            setWarningState(mContext, propertyState);
+        }
 
         // Send notification and click on share.
         sendBugreportFinished(NO_ID, mPlainTextPath, null);
@@ -513,10 +535,16 @@
 
         // Handle the warning
         mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
-        // TODO: get ok and showMessageAgain from the dialog reference above
-        UiObject showMessageAgain =
-                mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_repeat));
-        mUiBot.click(showMessageAgain, "show-message-again");
+        // TODO: get ok and dontShowAgain from the dialog reference above
+        UiObject dontShowAgain =
+                mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_dont_repeat));
+        final boolean firstTime = propertyState == null || propertyState == STATE_UNKNOWN;
+        if (firstTime) {
+            assertTrue("Checkbox should be checked by default", dontShowAgain.isChecked());
+        } else {
+            assertFalse("Checkbox should not be checked", dontShowAgain.isChecked());
+            mUiBot.click(dontShowAgain, "dont-show-again");
+        }
         UiObject ok = mUiBot.getVisibleObject(mContext.getString(com.android.internal.R.string.ok));
         mUiBot.click(ok, "ok");
 
@@ -526,8 +554,8 @@
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
 
         // Make sure it's hidden now.
-        int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
-        assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
+        int newState = getWarningState(mContext, STATE_UNKNOWN);
+        assertEquals("Didn't change state", STATE_HIDE, newState);
     }
 
     public void testShareBugreportAfterServiceDies() throws Exception {
@@ -623,7 +651,7 @@
     private Bundle sendBugreportFinishedAndGetSharedIntent(int id, String bugreportPath,
             String screenshotPath) {
         sendBugreportFinished(id, bugreportPath, screenshotPath);
-        return acceptBugreportAndGetSharedIntent(id, NOT_PENDING_SCREENSHOT);
+        return acceptBugreportAndGetSharedIntent(id);
     }
 
     /**
@@ -632,11 +660,7 @@
      * @return extras sent in the shared intent.
      */
     private Bundle acceptBugreportAndGetSharedIntent(int id) {
-        return acceptBugreportAndGetSharedIntent(id, NOT_PENDING_SCREENSHOT);
-    }
-
-    private Bundle acceptBugreportAndGetSharedIntent(int id, boolean pendingScreenshot) {
-        acceptBugreport(id, pendingScreenshot);
+        acceptBugreport(id);
         mUiBot.chooseActivity(UI_NAME);
         return mListener.getExtras();
     }
@@ -652,13 +676,7 @@
      * Accepts the notification to share the finished bugreport.
      */
     private void acceptBugreport(int id) {
-        acceptBugreport(id, NOT_PENDING_SCREENSHOT);
-    }
-
-    private void acceptBugreport(int id, boolean pendingScreenshot) {
-        final int res = pendingScreenshot ? R.string.bugreport_finished_pending_screenshot_title
-                : R.string.bugreport_finished_title;
-        mUiBot.clickOnNotification(mContext.getString(res, id));
+        mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, id));
     }
 
     /**
@@ -889,8 +907,8 @@
     }
 
     private String getPath(String file) {
-        File rootDir = new ContextWrapper(mContext).getFilesDir();
-        File dir = new File(rootDir, BUGREPORTS_DIR);
+        final File rootDir = mContext.getFilesDir();
+        final File dir = new File(rootDir, BUGREPORTS_DIR);
         if (!dir.exists()) {
             Log.i(TAG, "Creating directory " + dir);
             assertTrue("Could not create directory " + dir, dir.mkdir());
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f5854f5..c248adf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -159,6 +159,9 @@
     <!-- DND access -->
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
 
+    <!-- It's like, reality, but, you know, virtual -->
+    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
@@ -235,6 +238,19 @@
                     android:value="com.android.settings.category.system" />
         </activity>
 
+        <activity-alias android:name=".DemoMode"
+                  android:targetActivity=".tuner.TunerActivity"
+                  android:icon="@drawable/tuner"
+                  android:theme="@style/TunerSettings"
+                  android:label="@string/demo_mode"
+                  android:process=":tuner"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.settings.action.DEMO_MODE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity-alias>
+
         <!-- Service used by secondary users to register themselves with the system user. -->
         <service android:name=".recents.RecentsSystemUserService"
             android:exported="false"
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
index cfaf018..70e5451 100644
--- a/packages/SystemUI/res/layout/docked_stack_divider.xml
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -24,6 +24,11 @@
         android:id="@+id/docked_divider_background"
         android:background="@color/docked_divider_background"/>
 
+    <com.android.systemui.stackdivider.MinimizedDockShadow
+        style="@style/DockedDividerMinimizedShadow"
+        android:id="@+id/minimized_dock_shadow"
+        android:alpha="0"/>">
+
     <com.android.systemui.stackdivider.DividerHandleView
         style="@style/DockedDividerHandle"
         android:id="@+id/docked_divider_handle"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 9dd3ad2..55d7fab 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -33,7 +33,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
-            android:gravity="center" />
+            android:gravity="center"
+            android:importantForAccessibility="yes"
+            android:focusable="true" />
 
         <TextView
             android:id="@android:id/edit"
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index b711faa..82cba58 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -31,4 +31,8 @@
         <item name="android:layout_height">96dp</item>
     </style>
 
+    <style name="DockedDividerMinimizedShadow">
+        <item name="android:layout_width">8dp</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 18fc419..d26fb06 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -151,7 +151,9 @@
 
     <color name="docked_divider_background">#ff000000</color>
     <color name="docked_divider_handle">#ffffff</color>
-    <drawable name="forced_resizable_background">#40000000</drawable>
+    <drawable name="forced_resizable_background">#59000000</drawable>
+    <color name="minimize_dock_shadow_start">#60000000</color>
+    <color name="minimize_dock_shadow_end">#00000000</color>
 
     <color name="default_remote_input_background">@*android:color/notification_default_color</color>
     <color name="remote_input_hint">#99ffffff</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index dc9ffa9..a4d7a18 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1595,4 +1595,7 @@
     <!-- accessibility label for button to expand quick settings [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_expand">Expand quick settings.</string>
 
+    <!-- accessibility label for paging indicator in quick settings [CHAR LIMITi=NONE] -->
+    <string name="accessibility_quick_settings_page">Page <xliff:g name="current_page" example="1">%1$d</xliff:g> of <xliff:g name="num_pages" example="2">%2$d</xliff:g></string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f560a13..0730083 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -308,6 +308,11 @@
         <item name="android:layout_gravity">center_vertical</item>
     </style>
 
+    <style name="DockedDividerMinimizedShadow">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">8dp</item>
+    </style>
+
     <style name="DockedDividerHandle">
         <item name="android:layout_gravity">center_horizontal</item>
         <item name="android:layout_width">96dp</item>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index a130cf9..1af9075 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -100,10 +100,12 @@
 
     </PreferenceScreen>
 
+    <!--
     <Preference
         android:key="color_transform"
         android:title="@string/color_and_appearance"
         android:fragment="com.android.systemui.tuner.ColorAndAppearanceFragment" />
+    -->
 
     <PreferenceScreen
         android:key="volume_and_do_not_disturb"
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 087f61e..076b5bc 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -37,7 +37,7 @@
 
 import com.android.systemui.statusbar.policy.BatteryController;
 
-public class BatteryMeterDrawable extends Drawable implements DemoMode,
+public class BatteryMeterDrawable extends Drawable implements
         BatteryController.BatteryStateChangeCallback {
 
     private static final float ASPECT_RATIO = 9.5f / 14.5f;
@@ -184,14 +184,12 @@
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
         updateShowPercent();
-        if (mDemoMode) return;
         mBatteryController.addStateChangedCallback(this);
     }
 
     public void stopListening() {
         mListening = false;
         mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
-        if (mDemoMode) return;
         mBatteryController.removeStateChangedCallback(this);
     }
 
@@ -507,35 +505,6 @@
         return 0;
     }
 
-    private boolean mDemoMode;
-
-    @Override
-    public void dispatchDemoCommand(String command, Bundle args) {
-        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
-            mBatteryController.removeStateChangedCallback(this);
-            mDemoMode = true;
-            if (mListening) {
-                mBatteryController.removeStateChangedCallback(this);
-            }
-        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
-            mDemoMode = false;
-            postInvalidate();
-            if (mListening) {
-                mBatteryController.addStateChangedCallback(this);
-            }
-        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
-           String level = args.getString("level");
-           String plugged = args.getString("plugged");
-           if (level != null) {
-               mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
-           }
-           if (plugged != null) {
-               mPluggedIn = Boolean.parseBoolean(plugged);
-           }
-           postInvalidate();
-        }
-    }
-
     private final class SettingObserver extends ContentObserver {
         public SettingObserver() {
             super(new Handler());
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 001d1f2..73b9d02 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -32,7 +32,8 @@
     /**
      * Docks the top-most task and opens recents.
      */
-    boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds);
+    boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
+            int metricsDockAction);
 
     /**
      * Called during a drag-from-navbar-in gesture.
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 455a69f..39a3412 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -63,7 +63,8 @@
      * above.
      */
     private final Class<?>[] SERVICES_PER_USER = new Class[] {
-            com.android.systemui.recents.Recents.class
+            com.android.systemui.recents.Recents.class,
+            com.android.systemui.tv.pip.PipUI.class
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 5cb46ac..e050b0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -60,6 +60,8 @@
 
     public void setLocation(float location) {
         int index = (int) location;
+        setContentDescription(getContext().getString(R.string.accessibility_quick_settings_page,
+                (index + 1), getChildCount()));
         int position = index << 1 | ((location != index) ? 1 : 0);
         if (DEBUG) Log.d(TAG, "setLocation " + location + " " + index + " " + position);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 4d959d8..af81c19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -87,6 +87,7 @@
 
     public void setOnKeyguard(boolean onKeyguard) {
         mOnKeyguard = onKeyguard;
+        mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
         if (mOnKeyguard) {
             clearAnimationState();
         }
@@ -290,7 +291,7 @@
 
     @Override
     public void onAnimationStarted() {
-        mQuickQsPanel.setVisibility(View.VISIBLE);
+        mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
         if (mOnFirstPage) {
             final int N = mTopFiveQs.size();
             for (int i = 0; i < N; i++) {
@@ -302,12 +303,11 @@
     private void clearAnimationState() {
         final int N = mAllViews.size();
         mQuickQsPanel.setAlpha(0);
-        mQuickQsPanel.setVisibility(View.VISIBLE);
         for (int i = 0; i < N; i++) {
             View v = mAllViews.get(i);
             v.setAlpha(1);
-            v.setTranslationX(1);
-            v.setTranslationY(1);
+            v.setTranslationX(0);
+            v.setTranslationY(0);
         }
         final int N2 = mTopFiveQs.size();
         for (int i = 0; i < N2; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index e3a4909..ef75562 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -70,8 +70,8 @@
         super.onFinishInflate();
         mQSPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
         mQSDetail = (QSDetail) findViewById(R.id.qs_detail);
-        mQSDetail.setQsPanel(mQSPanel);
         mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
+        mQSDetail.setQsPanel(mQSPanel, mHeader);
         mQSAnimator = new QSAnimator(this, (QuickQSPanel) mHeader.findViewById(R.id.quick_qs_panel),
                 mQSPanel);
         mQSCustomizer = (QSCustomizer) findViewById(R.id.qs_customize);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 50c0cca..0cf7e479 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -35,6 +35,7 @@
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile.DetailAdapter;
+import com.android.systemui.statusbar.phone.BaseStatusBarHeader;
 import com.android.systemui.statusbar.phone.QSTileHost;
 
 public class QSDetail extends LinearLayout {
@@ -62,6 +63,7 @@
     private boolean mClosingDetail;
     private boolean mFullyExpanded;
     private View mQsDetailHeaderBack;
+    private BaseStatusBarHeader mHeader;
 
     public QSDetail(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -107,8 +109,9 @@
         mDetailDoneButton.setOnClickListener(doneListener);
     }
 
-    public void setQsPanel(QSPanel panel) {
+    public void setQsPanel(QSPanel panel, BaseStatusBarHeader header) {
         mQsPanel = panel;
+        mHeader = header;
         mQsPanel.setCallback(mQsPanelCallback);
     }
 
@@ -195,6 +198,7 @@
             mClosingDetail = true;
             mDetailAdapter = null;
             listener = mTeardownDetailWhenDone;
+            mHeader.setVisibility(View.VISIBLE);
             mQsPanel.setGridContentVisibility(true);
             mQsPanelCallback.onScanStateChanged(false);
         }
@@ -273,6 +277,7 @@
             // Only hide content if still in detail state.
             if (mDetailAdapter != null) {
                 mQsPanel.setGridContentVisibility(false);
+                mHeader.setVisibility(View.INVISIBLE);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 82daaa6..b2d7b48 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -36,10 +37,13 @@
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.MutableBoolean;
 import android.view.Display;
 import android.view.View;
 import android.widget.Toast;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
@@ -78,6 +82,10 @@
     private final static String ACTION_HIDE_RECENTS = "com.android.systemui.recents.ACTION_HIDE";
     private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
 
+    private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
+    private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
+    private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
+
     private static SystemServicesProxy sSystemServicesProxy;
     private static RecentsDebugFlags sDebugFlags;
     private static RecentsTaskLoader sTaskLoader;
@@ -393,7 +401,8 @@
     }
 
     @Override
-    public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds) {
+    public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
+            int metricsDockAction) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
         if (!isUserSetup()) {
@@ -413,7 +422,12 @@
         boolean screenPinningActive = ssp.isScreenPinningActive();
         boolean isTopTaskHome = topTask != null && SystemServicesProxy.isHomeStack(topTask.stackId);
         if (topTask != null && !isTopTaskHome && !screenPinningActive) {
+            logDockAttempt(mContext, topTask.topActivity, topTask.resizeMode);
             if (topTask.isDockable) {
+                if (metricsDockAction != -1) {
+                    MetricsLogger.action(mContext, metricsDockAction,
+                            topTask.topActivity.flattenToShortString());
+                }
                 if (sSystemServicesProxy.isSystemUser(currentUser)) {
                     mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
                 } else {
@@ -444,6 +458,26 @@
         }
     }
 
+    public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
+        if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
+            MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
+                    activity.flattenToShortString());
+        }
+        MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
+    }
+
+    private static String getMetricsCounterForResizeMode(int resizeMode) {
+        switch (resizeMode) {
+            case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
+                return COUNTER_WINDOW_UNSUPPORTED;
+            case ActivityInfo.RESIZE_MODE_RESIZEABLE:
+            case ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
+                return COUNTER_WINDOW_SUPPORTED;
+            default:
+                return COUNTER_WINDOW_INCOMPATIBLE;
+        }
+    }
+
     @Override
     public void onDraggingInRecents(float distanceFromTop) {
         if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 44f220b..4ecda54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -25,6 +25,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.os.Trace;
 import android.util.ArraySet;
 import android.util.IntProperty;
 import android.util.Property;
@@ -261,6 +262,14 @@
     }
 
     /**
+     * Adds a trace event for debugging.
+     */
+    public static void addTraceEvent(String event) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+    }
+
+    /**
      * Returns a lightweight dump of a rect.
      */
     public static String dumpRect(Rect r) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 7aeff1f..af1628b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -206,7 +206,7 @@
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                     thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
                     activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
-                    t.isDockable, t.bounds, t.taskDescription);
+                    t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity);
 
             allTasks.add(task);
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 24eeaf2..9f9c48f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -174,6 +175,15 @@
     @ViewDebug.ExportedProperty(category="recents")
     public boolean isDockable;
 
+    /**
+     * Resize mode. See {@link ActivityInfo#resizeMode}.
+     */
+    @ViewDebug.ExportedProperty(category="recents")
+    public int resizeMode;
+
+    @ViewDebug.ExportedProperty(category="recents")
+    public ComponentName topActivity;
+
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
     public Task() {
@@ -184,7 +194,8 @@
                 Bitmap thumbnail, String title, String titleDescription, String dismissDescription,
                 String appInfoDescription, int colorPrimary, int colorBackground,
                 boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp,
-                boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription) {
+                boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription,
+                int resizeMode, ComponentName topActivity) {
         boolean isInAffiliationGroup = (affiliationTaskId != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
         this.key = key;
@@ -206,6 +217,8 @@
         this.isStackTask = isStackTask;
         this.isSystemApp = isSystemApp;
         this.isDockable = isDockable;
+        this.resizeMode = resizeMode;
+        this.topActivity = topActivity;
     }
 
     /**
@@ -231,6 +244,8 @@
         this.isStackTask = o.isStackTask;
         this.isSystemApp = o.isSystemApp;
         this.isDockable = o.isDockable;
+        this.resizeMode = o.resizeMode;
+        this.topActivity = o.topActivity;
     }
 
     /**
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 13e1a14..60a85df 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -55,6 +55,7 @@
 import com.android.systemui.recents.tv.views.RecentsTvView;
 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;
@@ -246,7 +247,7 @@
         dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
         dismissEvent.addPostAnimationCallback(closeSystemWindows);
 
-        if(mTaskStackHorizontalGridView.getChildCount() > 0) {
+        if(mTaskStackHorizontalGridView.getChildCount() > 0 && animateTaskViews) {
             mHomeRecentsEnterExitAnimationHolder.startExitAnimation(dismissEvent);
         } else {
             closeSystemWindows.run();
@@ -343,6 +344,13 @@
         } else {
             mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
         }
+        if(mTaskStackHorizontalGridView.getStack().getTaskCount() > 1 && !mLaunchedFromHome) {
+            // If there are 2 or more tasks, and we are not launching from home
+            // set the selected position to the 2nd task to allow for faster app switching
+            mTaskStackHorizontalGridView.setSelectedPosition(1);
+        } else {
+            mTaskStackHorizontalGridView.setSelectedPosition(0);
+        }
 
         // If this is a new instance from a configuration change, then we have to manually trigger
         // the enter animation state, or if recents was relaunched by AM, without going through
@@ -494,12 +502,10 @@
     }
 
     public final void onBusEvent(AllTaskViewsDismissedEvent event) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        if (ssp.hasDockedTask()) {
+        if (mPipManager.isPipShown()) {
             mRecentsView.showEmptyView();
         } else {
-            // Just go straight home (no animation necessary because there are no more task views)
-            dismissRecentsToHome(false /* animateTaskViews */);
+            dismissRecentsToHome(false);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
index fd31872..dc0d1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -75,7 +75,8 @@
 
         if (useThumbnailTransition) {
             // Try starting with a thumbnail transition
-            ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask);
+            ActivityOptions opts = getThumbnailTransitionActivityOptionsForTV(topTask,
+                    stack.getTaskCount());
             if (opts != null) {
                 startRecentsActivity(topTask, opts, false /* fromHome */, true /* fromThumbnail */);
             } else {
@@ -118,8 +119,8 @@
      * Creates the activity options for an app->recents transition on TV.
      */
     private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
-            ActivityManager.RunningTaskInfo topTask) {
-        Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
+            ActivityManager.RunningTaskInfo topTask, int numTasks) {
+        Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext, numTasks);
         SystemServicesProxy ssp = Recents.getSystemServices();
         ThumbnailData thumbnailData = ssp.getTaskThumbnail(topTask.id);
         if (thumbnailData.thumbnail != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
index fbcfa97..3e668af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
@@ -26,13 +26,13 @@
 
 public class DismissAnimationsHolder {
     private LinearLayout mDismissArea;
-    private LinearLayout mTaskCardView;
+    private LinearLayout mRecentsTvCard;
     private int mCardYDelta;
     private long mShortDuration;
     private long mLongDuration;
 
     public DismissAnimationsHolder(TaskCardView taskCardView) {
-        mTaskCardView = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
+        mRecentsTvCard = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
         mDismissArea = (LinearLayout) taskCardView.findViewById(R.id.card_dismiss);
 
         Resources res = taskCardView.getResources();
@@ -47,7 +47,7 @@
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(1.0f);
 
-        mTaskCardView.animate()
+        mRecentsTvCard.animate()
                 .setDuration(mShortDuration)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .translationYBy(mCardYDelta)
@@ -60,7 +60,7 @@
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(0.0f);
 
-        mTaskCardView.animate()
+        mRecentsTvCard.animate()
                 .setDuration(mShortDuration)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .translationYBy(-mCardYDelta)
@@ -73,11 +73,17 @@
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(0.0f);
 
-        mTaskCardView.animate()
+        mRecentsTvCard.animate()
                 .setDuration(mLongDuration)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .translationYBy(mCardYDelta)
                 .alpha(0.0f)
                 .setListener(listener);
     }
+
+    public void reset() {
+        mRecentsTvCard.setAlpha(1.0f);
+        mRecentsTvCard.setTranslationY(0);
+        mRecentsTvCard.animate().setListener(null);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index 53fdf62..b876fc70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -250,8 +250,9 @@
 
     public TaskStackHorizontalGridView setTaskStackViewAdapter(
             TaskStackHorizontalViewAdapter taskStackViewAdapter) {
-        if(mTaskStackHorizontalView != null) {
+        if (mTaskStackHorizontalView != null) {
             mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
+            taskStackViewAdapter.setTaskStackHorizontalGridView(mTaskStackHorizontalView);
         }
         return mTaskStackHorizontalView;
     }
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 99d478b..d3bc4b6 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
@@ -95,7 +95,38 @@
         return r;
     }
 
-    public static Rect getStartingCardThumbnailRect(Context context) {
+    public static Rect getStartingCardThumbnailRect(Context context, int numberOfTasks) {
+        if(numberOfTasks > 1) {
+            return getStartingCardThumbnailRectForStartPosition(context);
+        } else {
+            return getStartingCardThumbnailRectForFocusedPosition(context);
+        }
+    }
+
+    private static Rect getStartingCardThumbnailRectForStartPosition(Context context) {
+        Resources res = context.getResources();
+
+        int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
+        int totalSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing) * 2
+                + res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
+        int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
+        int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
+        int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
+                res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
+
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+        Point size = new Point();
+        display.getSize(size);
+        int screenWidth = size.x;
+
+        return new Rect(screenWidth / 2 + width / 2 + totalSpacing,
+                topMargin + headerHeight,
+                screenWidth / 2 + width / 2 + totalSpacing + width,
+                topMargin + headerHeight + height);
+    }
+
+    private static Rect getStartingCardThumbnailRectForFocusedPosition(Context context) {
         Resources res = context.getResources();
 
         TypedValue out = new TypedValue();
@@ -127,7 +158,6 @@
         Point size = new Point();
         display.getSize(size);
         int screenWidth = size.x;
-        int screenHeight = size.y;
 
         return new Rect(screenWidth / 2 - width / 2 - widthDelta / 2,
                 topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale),
@@ -189,6 +219,7 @@
     }
 
     public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
+        mDismissState = false;
         mDismissAnimationsHolder.startDismissAnimation(listener);
     }
 
@@ -201,4 +232,10 @@
         super.onDetachedFromWindow();
         setDismissState(false);
     }
+
+    public void reset() {
+        mDismissState = false;
+        mRecentsRowFocusAnimationHolder.reset();
+        mDismissAnimationsHolder.reset();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 603721a..77ab8c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -179,13 +179,14 @@
 
     @Override
     public void onStackTaskAdded(TaskStack stack, Task newTask) {
-        getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
+        ((TaskStackHorizontalViewAdapter) getAdapter()).addTaskAt(newTask,
+                stack.indexOfStackTask(newTask));
     }
 
     @Override
     public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
             Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture) {
-        getAdapter().notifyItemRemoved(stack.getStackTasks().indexOf(removedTask));
+        ((TaskStackHorizontalViewAdapter) getAdapter()).removeTask(removedTask);
         if (mFocusedTask == removedTask) {
             resetFocusedTask(removedTask);
         }
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 97712ea..eff1845 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
@@ -27,7 +27,9 @@
 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.events.ui.TaskViewDismissedEvent;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.AnimationProps;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -40,6 +42,7 @@
     //Full class name is 30 characters
     private static final String TAG = "TaskStackViewAdapter";
     private List<Task> mTaskList;
+    private TaskStackHorizontalGridView mGridView;
 
     public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
         private TaskCardView mTaskCardView;
@@ -62,7 +65,7 @@
             try {
                 if (mTaskCardView.isInDismissState()) {
                     mTaskCardView.startDismissTaskAnimation(
-                            getRemoveAtListener(getAdapterPosition(), mTaskCardView));
+                            getRemoveAtListener(getAdapterPosition(), mTaskCardView.getTask()));
                 } else {
                     EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
                             null, INVALID_STACK_ID));
@@ -74,6 +77,28 @@
             }
 
         }
+
+        private Animator.AnimatorListener getRemoveAtListener(final int position,
+                                                              final Task task) {
+            return new Animator.AnimatorListener() {
+
+                @Override
+                public void onAnimationStart(Animator animation) { }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    removeAt(position);
+                    EventBus.getDefault().send(new DeleteTaskDataEvent(task));
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) { }
+
+                @Override
+                public void onAnimationRepeat(Animator animation) { }
+            };
+
+        }
     }
 
     public TaskStackHorizontalViewAdapter(List tasks) {
@@ -101,39 +126,43 @@
     }
 
     @Override
+    public void onViewDetachedFromWindow(ViewHolder holder) {
+        holder.mTaskCardView.reset();
+    }
+
+    @Override
     public int getItemCount() {
         return mTaskList.size();
     }
 
-    private Animator.AnimatorListener getRemoveAtListener(final int position,
-                                                          final TaskCardView taskCardView) {
-        return new Animator.AnimatorListener() {
-
-            @Override
-            public void onAnimationStart(Animator animation) { }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                removeAt(position);
-                EventBus.getDefault().send(new DeleteTaskDataEvent(taskCardView.getTask()));
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) { }
-
-            @Override
-            public void onAnimationRepeat(Animator animation) { }
-        };
-
+    private void removeAt(int position) {
+        Task removedTask = mTaskList.remove(position);
+        if (mGridView != null) {
+            mGridView.getStack().removeTask(removedTask, AnimationProps.IMMEDIATE,
+                    false);
+        }
+        notifyItemRemoved(position);
     }
 
-    private void removeAt(int position) {
-        mTaskList.remove(position);
-        notifyItemRemoved(position);
+    public void removeTask(Task task) {
+        int position = mTaskList.indexOf(task);
+        if (position >= 0) {
+            mTaskList.remove(position);
+            notifyItemRemoved(position);
+        }
     }
 
     public int getPositionOfTask(Task task) {
         int position = mTaskList.indexOf(task);
         return (position >= 0) ? position : 0;
     }
+
+    public void setTaskStackHorizontalGridView(TaskStackHorizontalGridView gridView) {
+        mGridView = gridView;
+    }
+
+    public void addTaskAt(Task task, int position) {
+        mTaskList.add(position, task);
+        notifyItemInserted(position);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 21a43d5..59b7560 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -540,7 +540,8 @@
                     mTransitionHelper.wrapStartedListener(startedListener),
                     true /* scaleUp */);
 
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP);
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
+                    event.task.topActivity.flattenToShortString());
         } else {
             // Animate the overlay alpha back to 0
             updateVisibleDockRegions(null, true /* isDefaultDockState */, -1,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 22acb88..5fbc037 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -41,7 +41,6 @@
 
 import java.util.ArrayList;
 
-
 /**
  * Represents the dock regions for each orientation.
  */
@@ -163,6 +162,7 @@
         mVisibleDockStates.clear();
         if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
                 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
+            Recents.logDockAttempt(mRv.getContext(), event.task.topActivity, event.task.resizeMode);
             if (!event.task.isDockable) {
                 EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index fe91f42..76aab59d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.util.Log;
 import android.view.View;
@@ -77,7 +78,7 @@
 
     public static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
     public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 333;
-    public static final int ENTER_WHILE_DOCKING_DURATION = 150;
+    public static final int ENTER_WHILE_DOCKING_DURATION = 250;
 
     private static final PathInterpolator ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR =
             new PathInterpolator(0, 0, 0, 1f);
@@ -135,6 +136,8 @@
                 R.dimen.recents_task_stack_animation_affiliate_enter_offset);
         int launchedWhileDockingOffset = res.getDimensionPixelSize(
                 R.dimen.recents_task_stack_animation_launched_while_docking_offset);
+        boolean isLandscape = mStackView.getContext().getApplicationContext().getResources()
+                .getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
 
         // Prepare each of the task views for their enter animation from front to back
         List<TaskView> taskViews = mStackView.getTaskViews();
@@ -169,7 +172,10 @@
                 mTmpTransform.alpha = 0f;
                 mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
             } else if (launchState.launchedViaDockGesture) {
-                mTmpTransform.rect.offset(0, launchedWhileDockingOffset);
+                int offset = isLandscape
+                        ? launchedWhileDockingOffset
+                        : (int) (offscreenYOffset * 0.9f);
+                mTmpTransform.rect.offset(0, offset);
                 mTmpTransform.alpha = 0f;
                 mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index b75a91e..e4da8b3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -23,13 +23,13 @@
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.util.ArraySet;
+import android.util.MutableFloat;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.ViewDebug;
 
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.RecentsDebugFlags;
@@ -628,22 +628,24 @@
 
     /**
      * Updates this stack when a scroll happens.
+     *
      */
-    public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
-        if (deltaScroll == 0f) {
-            return;
+    public float updateFocusStateOnScroll(float lastTargetStackScroll, float targetStackScroll,
+            float lastStackScroll) {
+        if (targetStackScroll == lastStackScroll) {
+            return targetStackScroll;
         }
 
+        float deltaScroll = targetStackScroll - lastStackScroll;
+        float deltaTargetScroll = targetStackScroll - lastTargetStackScroll;
+        float newScroll = targetStackScroll;
+        mUnfocusedRange.offset(targetStackScroll);
         for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
             int taskId = mTaskIndexOverrideMap.keyAt(i);
             float x = mTaskIndexMap.get(taskId);
             float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
             float newOverrideX = overrideX + deltaScroll;
-            mUnfocusedRange.offset(stackScroll);
-            boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
-                    mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
-            if (outOfBounds || (overrideX >= x && x >= newOverrideX) ||
-                    (overrideX <= x && x <= newOverrideX)) {
+            if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
                 // Remove the override once we reach the original task index
                 mTaskIndexOverrideMap.removeAt(i);
             } else if ((overrideX >= x && deltaScroll <= 0f) ||
@@ -652,11 +654,23 @@
                 mTaskIndexOverrideMap.put(taskId, newOverrideX);
             } else {
                 // Scrolling override x away from x, we should still move the scroll towards x
-                float deltaX = overrideX - x;
-                newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll));
-                mTaskIndexOverrideMap.put(taskId, x + newOverrideX);
+                newScroll = lastStackScroll;
+                newOverrideX = overrideX - deltaTargetScroll;
+                if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
+                    mTaskIndexOverrideMap.removeAt(i);
+                } else{
+                    mTaskIndexOverrideMap.put(taskId, newOverrideX);
+                }
             }
         }
+        return newScroll;
+    }
+
+    private boolean isInvalidOverrideX(float x, float overrideX, float newOverrideX) {
+        boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
+                mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
+        return outOfBounds || (overrideX >= x && x >= newOverrideX) ||
+                (overrideX <= x && x <= newOverrideX);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 13c8403..0fc45ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1618,7 +1618,6 @@
         if (animation != null) {
             relayoutTaskViewsOnNextFrame(animation);
         }
-        mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
 
         if (mEnterAnimationComplete) {
             if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 19b3c94..1fa73c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.util.MutableFloat;
 import android.util.Property;
 import android.view.ViewDebug;
 import android.widget.OverScroller;
@@ -66,6 +67,8 @@
 
     @ViewDebug.ExportedProperty(category="recents")
     float mStackScrollP;
+    @ViewDebug.ExportedProperty(category="recents")
+    float mLastDeltaP = 0f;
     float mFlingDownScrollP;
     int mFlingDownY;
 
@@ -84,6 +87,11 @@
     /** Resets the task scroller. */
     void reset() {
         mStackScrollP = 0f;
+        mLastDeltaP = 0f;
+    }
+
+    void resetDeltaScroll() {
+        mLastDeltaP = 0f;
     }
 
     /** Gets the current stack scroll */
@@ -99,14 +107,27 @@
     }
 
     /**
+     * Sets the current stack scroll immediately, and returns the difference between the target
+     * scroll and the actual scroll after accounting for the effect on the focus state.
+     */
+    public float setDeltaStackScroll(float downP, float deltaP) {
+        float targetScroll = downP + deltaP;
+        float newScroll = mLayoutAlgorithm.updateFocusStateOnScroll(downP + mLastDeltaP, targetScroll,
+                mStackScrollP);
+        setStackScroll(newScroll, AnimationProps.IMMEDIATE);
+        mLastDeltaP = deltaP;
+        return newScroll - targetScroll;
+    }
+
+    /**
      * Sets the current stack scroll, but indicates to the callback the preferred animation to
      * update to this new scroll.
      */
-    public void setStackScroll(float s, AnimationProps animation) {
-        float prevStackScroll = mStackScrollP;
-        mStackScrollP = s;
+    public void setStackScroll(float newScroll, AnimationProps animation) {
+        float prevScroll = mStackScrollP;
+        mStackScrollP = newScroll;
         if (mCb != null) {
-            mCb.onStackScrollChanged(prevStackScroll, mStackScrollP, animation);
+            mCb.onStackScrollChanged(prevScroll, mStackScrollP, animation);
         }
     }
 
@@ -115,9 +136,9 @@
      * @return whether the stack progress changed.
      */
     public boolean setStackScrollToInitialState() {
-        float prevStackScrollP = mStackScrollP;
+        float prevScroll = mStackScrollP;
         setStackScroll(mLayoutAlgorithm.mInitialScrollP);
-        return Float.compare(prevStackScrollP, mStackScrollP) != 0;
+        return Float.compare(prevScroll, mStackScrollP) != 0;
     }
 
     /**
@@ -227,10 +248,9 @@
     boolean computeScroll() {
         if (mScroller.computeScrollOffset()) {
             float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
-            float scroll = mFlingDownScrollP + deltaP;
-            setStackScroll(scroll);
+            mFlingDownScrollP += setDeltaStackScroll(mFlingDownScrollP, deltaP);
             if (DEBUG) {
-                Log.d(TAG, "computeScroll: " + scroll);
+                Log.d(TAG, "computeScroll: " + (mFlingDownScrollP + deltaP));
             }
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index ee0de1a..3cdb1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -200,6 +200,7 @@
                 // Stop the current scroll if it is still flinging
                 mScroller.stopScroller();
                 mScroller.stopBoundScrollAnimation();
+                mScroller.resetDeltaScroll();
                 Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
 
                 // Finish any existing task animations from the delete
@@ -223,6 +224,7 @@
                 mDownY = (int) ev.getY(index);
                 mLastY = mDownY;
                 mDownScrollP = mScroller.getStackScroll();
+                mScroller.resetDeltaScroll();
                 mVelocityTracker.addMovement(ev);
                 break;
             }
@@ -256,20 +258,21 @@
                     // If we just move linearly on the screen, then that would map to 1/arclength
                     // of the curve, so just move the scroll proportional to that
                     float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
-                    float curScrollP = mDownScrollP + deltaP;
 
                     // Modulate the overscroll to prevent users from pulling the stack too far
                     float minScrollP = layoutAlgorithm.mMinScrollP;
                     float maxScrollP = layoutAlgorithm.mMaxScrollP;
+                    float curScrollP = mDownScrollP + deltaP;
                     if (curScrollP < minScrollP || curScrollP > maxScrollP) {
                         float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
                         float overscrollP = (curScrollP - clampedScrollP);
                         float overscrollX = Math.abs(overscrollP) / MAX_OVERSCROLL;
-                        curScrollP = clampedScrollP + (Math.signum(overscrollP) *
-                                (OVERSCROLL_INTERP.getInterpolation(overscrollX) * MAX_OVERSCROLL));
+                        float interpX = OVERSCROLL_INTERP.getInterpolation(overscrollX);
+                        curScrollP = clampedScrollP + Math.signum(overscrollP) *
+                                (interpX * MAX_OVERSCROLL);
                     }
-
-                    mScroller.setStackScroll(curScrollP);
+                    mDownScrollP += mScroller.setDeltaStackScroll(mDownScrollP,
+                            curScrollP - mDownScrollP);
                     mStackViewScrolledEvent.updateY(y - mLastY);
                     EventBus.getDefault().send(mStackViewScrolledEvent);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index e5ac0d3..62fd585 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -143,43 +143,28 @@
 
         int viewWidth = mTaskViewRect.width();
         int viewHeight = mTaskViewRect.height();
-        if (mBitmapShader != null) {
-
-            // We are drawing the thumbnail in the same orientation, so just fit the width
-            int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
-            int thumbnailHeight = (int) (mThumbnailRect.height() * mThumbnailScale);
-
-            if (thumbnailWidth >= viewWidth && thumbnailHeight >= viewHeight) {
-                // Thumbnail fills the full task view bounds, so just draw it
-                canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
-                        mDrawPaint);
-            } else {
-                // Thumbnail does not fill the full task view bounds, so just draw it and fill the
-                // empty areas with the background color
-                int count = canvas.save();
-
-                // Since we only want the top corners to be rounded, draw slightly beyond the
-                // thumbnail height, but clip to the thumbnail height
-                canvas.clipRect(0, 0, thumbnailWidth, thumbnailHeight, Region.Op.REPLACE);
-                canvas.drawRoundRect(0, 0,
-                        thumbnailWidth + (thumbnailWidth < viewWidth ? mCornerRadius : 0),
-                        thumbnailHeight + (thumbnailHeight < viewHeight ? mCornerRadius : 0),
-                        mCornerRadius, mCornerRadius, mDrawPaint);
-
-                // In the remaining space, draw the background color
-                if (thumbnailWidth < viewWidth) {
-                    canvas.clipRect(thumbnailWidth, 0, viewWidth, viewHeight, Region.Op.REPLACE);
-                    canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
-                            viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
-                }
-                if (thumbnailWidth > 0 && thumbnailHeight < viewHeight) {
-                    canvas.clipRect(0, thumbnailHeight, viewWidth, viewHeight, Region.Op.REPLACE);
-                    canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
-                            viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
-                }
-
-                canvas.restoreToCount(count);
+        int thumbnailWidth = Math.min(viewWidth,
+                (int) (mThumbnailRect.width() * mThumbnailScale));
+        int thumbnailHeight = Math.min(viewHeight,
+                (int) (mThumbnailRect.height() * mThumbnailScale));
+        if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+            // Draw the background, there will be some small overdraw with the thumbnail
+            if (thumbnailWidth < viewWidth) {
+                // Portrait thumbnail on a landscape task view
+                canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
+                        viewWidth, viewHeight,
+                        mCornerRadius, mCornerRadius, mBgFillPaint);
             }
+            if (thumbnailHeight < viewHeight) {
+                // Landscape thumbnail on a portrait task view
+                canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+                        viewWidth, viewHeight,
+                        mCornerRadius, mCornerRadius, mBgFillPaint);
+            }
+
+            // Draw the thumbnail
+            canvas.drawRoundRect(0, 0, thumbnailWidth, thumbnailHeight,
+                    mCornerRadius, mCornerRadius, mDrawPaint);
         } else {
             canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
                     mBgFillPaint);
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 69dcabe..0aeb7b4 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -102,8 +102,8 @@
                 int dockMode = (shortcutCode == SC_DOCK_LEFT)
                         ? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
                         : ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-                recents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, dockMode, null);
-                MetricsLogger.action(mContext, MetricsEvent.WINDOW_DOCK_SHORTCUTS);
+                recents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, dockMode, null,
+                        MetricsEvent.WINDOW_DOCK_SHORTCUTS);
             } else {
                 // If there is already a docked window, we respond by resizing the docking pane.
                 DividerView dividerView = getComponent(Divider.class).getView();
@@ -115,7 +115,8 @@
                 DividerSnapAlgorithm.SnapTarget target = snapAlgorithm.cycleNonDismissTarget(
                         currentTarget, increment);
                 dividerView.startDragging(true /* animate */, false /* touching */);
-                dividerView.stopDragging(target.position, 0f, true /* avoidDismissStart */);
+                dividerView.stopDragging(target.position, 0f, true /* avoidDismissStart */,
+                        true /* logMetrics */);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "handleDockKey() failed.");
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index d294c80..2bf0b40 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -146,5 +146,10 @@
                 throws RemoteException {
             updateMinimizedDockedStack(minimized, animDuration);
         }
+
+        @Override
+        public void onDockSideChanged(final int newDockSide) throws RemoteException {
+            mView.post(() -> mView.notifyDockSideChanged(newDockSide));
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 98e0dd9..66a413c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -54,11 +54,14 @@
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.recents.Constants.Metrics;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
@@ -80,7 +83,13 @@
     static final long TOUCH_ANIMATION_DURATION = 150;
     static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
 
-    private static final String TAG = "DividerView";
+    private static final int LOG_VALUE_RESIZE_50_50 = 0;
+    private static final int LOG_VALUE_RESIZE_DOCKED_SMALLER = 1;
+    private static final int LOG_VALUE_RESIZE_DOCKED_LARGER = 2;
+
+    private static final int LOG_VALUE_UNDOCK_MAX_DOCKED = 0;
+    private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1;
+
 
     private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
     private static final boolean SWAPPING_ENABLED = false;
@@ -88,7 +97,7 @@
     /**
      * How much the background gets scaled when we are in the minimized dock state.
      */
-    private static final float MINIMIZE_DOCK_SCALE = 0.375f;
+    private static final float MINIMIZE_DOCK_SCALE = 0f;
 
     private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
             new PathInterpolator(0.5f, 1f, 0.5f, 1f);
@@ -97,6 +106,7 @@
 
     private DividerHandleView mHandle;
     private View mBackground;
+    private MinimizedDockShadow mMinimizedShadow;
     private int mStartX;
     private int mStartY;
     private int mStartPosition;
@@ -133,6 +143,8 @@
     private boolean mGrowRecents;
     private ValueAnimator mCurrentAnimator;
     private boolean mEntranceAnimationRunning;
+    private boolean mExitAnimationRunning;
+    private int mExitStartPosition;
     private GestureDetector mGestureDetector;
     private boolean mDockedStackMinimized;
 
@@ -201,6 +213,7 @@
         super.onFinishInflate();
         mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle);
         mBackground = findViewById(R.id.docked_divider_background);
+        mMinimizedShadow = (MinimizedDockShadow) findViewById(R.id.minimized_dock_shadow);
         mHandle.setOnTouchListener(this);
         mDividerWindowWidth = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
@@ -267,6 +280,18 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
+        int minimizeLeft = 0;
+        int minimizeTop = 0;
+        if (mDockSide == WindowManager.DOCKED_TOP) {
+            minimizeTop = mBackground.getTop();
+        } else if (mDockSide == WindowManager.DOCKED_LEFT) {
+            minimizeLeft = mBackground.getLeft();
+        } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
+            minimizeLeft = mBackground.getRight() - mMinimizedShadow.getWidth();
+        }
+        mMinimizedShadow.layout(minimizeLeft, minimizeTop,
+                minimizeLeft + mMinimizedShadow.getMeasuredWidth(),
+                minimizeTop + mMinimizedShadow.getMeasuredHeight());
         if (changed) {
             mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
                     mHandle.getRight(), mHandle.getBottom()));
@@ -297,22 +322,28 @@
         return mDockSide != WindowManager.DOCKED_INVALID;
     }
 
-    public void stopDragging(int position, float velocity, boolean avoidDismissStart) {
+    public void stopDragging(int position, float velocity, boolean avoidDismissStart,
+            boolean logMetrics) {
         mHandle.setTouching(false, true /* animate */);
-        fling(position, velocity, avoidDismissStart);
+        fling(position, velocity, avoidDismissStart, logMetrics);
         mWindowManager.setSlippery(true);
         releaseBackground();
     }
 
     public void stopDragging(int position, SnapTarget target, long duration,
             Interpolator interpolator) {
-        stopDragging(position, target, duration, 0 /* startDelay*/, interpolator);
+        stopDragging(position, target, duration, 0 /* startDelay*/, 0 /* endDelay */, interpolator);
+    }
+
+    public void stopDragging(int position, SnapTarget target, long duration,
+            Interpolator interpolator, long endDelay) {
+        stopDragging(position, target, duration, 0 /* startDelay*/, endDelay, interpolator);
     }
 
     public void stopDragging(int position, SnapTarget target, long duration, long startDelay,
-            Interpolator interpolator) {
+            long endDelay, Interpolator interpolator) {
         mHandle.setTouching(false, true /* animate */);
-        flingTo(position, target, duration, startDelay, interpolator);
+        flingTo(position, target, duration, startDelay, endDelay, interpolator);
         mWindowManager.setSlippery(true);
         releaseBackground();
     }
@@ -325,6 +356,7 @@
 
     private void updateDockSide() {
         mDockSide = mWindowManagerProxy.getDockSide();
+        mMinimizedShadow.setDockSide(mDockSide);
     }
 
     private void initializeSnapAlgorithm() {
@@ -396,37 +428,70 @@
                 mVelocityTracker.computeCurrentVelocity(1000);
                 int position = calculatePosition(x, y);
                 stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
-                        : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */);
+                        : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */,
+                        true /* log */);
                 mMoving = false;
                 break;
         }
         return true;
     }
 
+    private void logResizeEvent(SnapTarget snapTarget) {
+        if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
+            MetricsLogger.action(
+                    mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
+                            ? LOG_VALUE_UNDOCK_MAX_OTHER
+                            : LOG_VALUE_UNDOCK_MAX_DOCKED);
+        } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) {
+            MetricsLogger.action(
+                    mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
+                            ? LOG_VALUE_UNDOCK_MAX_OTHER
+                            : LOG_VALUE_UNDOCK_MAX_DOCKED);
+        } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
+                    LOG_VALUE_RESIZE_50_50);
+        } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
+                    dockSideTopLeft(mDockSide)
+                            ? LOG_VALUE_RESIZE_DOCKED_SMALLER
+                            : LOG_VALUE_RESIZE_DOCKED_LARGER);
+        } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) {
+            MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
+                    dockSideTopLeft(mDockSide)
+                            ? LOG_VALUE_RESIZE_DOCKED_LARGER
+                            : LOG_VALUE_RESIZE_DOCKED_SMALLER);
+        }
+    }
+
     private void convertToScreenCoordinates(MotionEvent event) {
         event.setLocation(event.getRawX(), event.getRawY());
     }
 
-    private void fling(int position, float velocity, boolean avoidDismissStart) {
+    private void fling(int position, float velocity, boolean avoidDismissStart,
+            boolean logMetrics) {
         SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity);
         if (avoidDismissStart && snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
             snapTarget = mSnapAlgorithm.getFirstSplitTarget();
         }
-        ValueAnimator anim = getFlingAnimator(position, snapTarget);
+        if (logMetrics) {
+            logResizeEvent(snapTarget);
+        }
+        ValueAnimator anim = getFlingAnimator(position, snapTarget, 0 /* endDelay */);
         mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity);
         anim.start();
     }
 
     private void flingTo(int position, SnapTarget target, long duration, long startDelay,
-            Interpolator interpolator) {
-        ValueAnimator anim = getFlingAnimator(position, target);
+            long endDelay, Interpolator interpolator) {
+        ValueAnimator anim = getFlingAnimator(position, target, endDelay);
         anim.setDuration(duration);
         anim.setStartDelay(startDelay);
         anim.setInterpolator(interpolator);
         anim.start();
     }
 
-    private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget) {
+    private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget,
+            final long endDelay) {
         ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
@@ -437,15 +502,31 @@
                                 : snapTarget.position, snapTarget);
             }
         });
+        Runnable endAction = () -> {
+            commitSnapFlags(snapTarget);
+            mWindowManagerProxy.setResizing(false);
+            mDockSide = WindowManager.DOCKED_INVALID;
+            mCurrentAnimator = null;
+            mEntranceAnimationRunning = false;
+            mExitAnimationRunning = false;
+            EventBus.getDefault().send(new StoppedDragingEvent());
+        };
         anim.addListener(new AnimatorListenerAdapter() {
+
+            private boolean mCancelled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+
             @Override
             public void onAnimationEnd(Animator animation) {
-                commitSnapFlags(snapTarget);
-                mWindowManagerProxy.setResizing(false);
-                mDockSide = WindowManager.DOCKED_INVALID;
-                mCurrentAnimator = null;
-                mEntranceAnimationRunning = false;
-                EventBus.getDefault().send(new StoppedDragingEvent());
+                if (endDelay == 0 || mCancelled) {
+                    endAction.run();
+                } else {
+                    postDelayed(endAction, endDelay);
+                }
             }
         });
         mCurrentAnimator = anim;
@@ -538,6 +619,7 @@
                     : mBackground.getWidth());
             mBackground.setScaleX(MINIMIZE_DOCK_SCALE);
         }
+        mMinimizedShadow.setAlpha(minimized ? 1f : 0f);
         mDockedStackMinimized = minimized;
     }
 
@@ -563,6 +645,11 @@
         if (!minimized) {
             mBackground.animate().withEndAction(mResetBackgroundRunnable);
         }
+        mMinimizedShadow.animate()
+                .alpha(minimized ? 1f : 0f)
+                .setInterpolator(Interpolators.ALPHA_IN)
+                .setDuration(animDuration)
+                .start();
         mBackground.animate()
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(animDuration)
@@ -575,6 +662,7 @@
         mBackground.setPivotY(mBackground.getHeight() / 2);
         mBackground.setScaleX(1f);
         mBackground.setScaleY(1f);
+        mMinimizedShadow.setAlpha(0f);
     }
 
     @Override
@@ -583,6 +671,13 @@
         updateDisplayInfo();
     }
 
+
+    public void notifyDockSideChanged(int newDockSide) {
+        mDockSide = newDockSide;
+        mMinimizedShadow.setDockSide(mDockSide);
+        requestLayout();
+    }
+
     private void updateDisplayInfo() {
         final DisplayManager displayManager =
                 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
@@ -654,6 +749,15 @@
                     mOtherTaskRect);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
                     mOtherTaskRect, null);
+        } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
+            calculateBoundsForPosition(taskPosition,
+                    mDockSide, mDockedTaskRect);
+            calculateBoundsForPosition(mExitStartPosition,
+                    DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
+            mOtherInsetRect.set(mOtherTaskRect);
+            applyExitAnimationParallax(mOtherTaskRect, position);
+            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
+                    mOtherTaskRect, mOtherInsetRect);
         } else if (taskPosition != TASK_POSITION_SAME) {
             calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
                     mOtherRect);
@@ -692,6 +796,16 @@
                 dimFraction);
     }
 
+    private void applyExitAnimationParallax(Rect taskRect, int position) {
+        if (mDockSide == WindowManager.DOCKED_TOP) {
+            taskRect.offset(0, (int) ((position - mExitStartPosition) * 0.25f));
+        } else if (mDockSide == WindowManager.DOCKED_LEFT) {
+            taskRect.offset((int) ((position - mExitStartPosition) * 0.25f), 0);
+        } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
+            taskRect.offset((int) ((mExitStartPosition - position) * 0.25f), 0);
+        }
+    }
+
     private float getDimFraction(int position, SnapTarget dismissTarget) {
         if (mEntranceAnimationRunning) {
             return 0f;
@@ -875,14 +989,17 @@
         if (mAnimateAfterRecentsDrawn) {
             mAnimateAfterRecentsDrawn = false;
             updateDockSide();
-            stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
-                    Interpolators.TOUCH_RESPONSE);
+
+            // Delay switching resizing mode because this might cause jank in recents animation
+            // that's long than this animation.
+            stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(),
+                    250 /* startDelay */, Interpolators.FAST_OUT_SLOW_IN, 200 /* endDelay */);
         }
         if (mGrowAfterRecentsDrawn) {
             mGrowAfterRecentsDrawn = false;
             updateDockSide();
             stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 250,
-                    Interpolators.TOUCH_RESPONSE);
+                    Interpolators.FAST_OUT_SLOW_IN);
         }
     }
 
@@ -895,8 +1012,10 @@
                     : mSnapAlgorithm.getDismissStartTarget();
 
             // Don't start immediately - give a little bit time to settle the drag resize change.
-            stopDragging(getCurrentPosition(), target, 336 /* duration */, 100 /* startDelay */,
-                    Interpolators.TOUCH_RESPONSE);
+            mExitAnimationRunning = true;
+            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/stackdivider/MinimizedDockShadow.java b/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java
new file mode 100644
index 0000000..ecff54f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+/**
+ * Shadow for the minimized dock state on homescreen.
+ */
+public class MinimizedDockShadow extends View {
+
+    private final Paint mShadowPaint = new Paint();
+
+    private int mDockSide = WindowManager.DOCKED_INVALID;
+
+    public MinimizedDockShadow(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setDockSide(int dockSide) {
+        if (dockSide != mDockSide) {
+            mDockSide = dockSide;
+            updatePaint(getLeft(), getTop(), getRight(), getBottom());
+            invalidate();
+        }
+    }
+
+    private void updatePaint(int left, int top, int right, int bottom) {
+        int startColor = mContext.getResources().getColor(
+                R.color.minimize_dock_shadow_start, null);
+        int endColor = mContext.getResources().getColor(
+                R.color.minimize_dock_shadow_end, null);
+        final int middleColor = Color.argb(
+                (Color.alpha(startColor) + Color.alpha(endColor)) / 2, 0, 0, 0);
+        final int quarter = Color.argb(
+                (int) (Color.alpha(startColor) * 0.25f + Color.alpha(endColor) * 0.75f),
+                0, 0, 0);
+        if (mDockSide == WindowManager.DOCKED_TOP) {
+            mShadowPaint.setShader(new LinearGradient(
+                    0, 0, 0, bottom - top,
+                    new int[] { startColor, middleColor, quarter, endColor },
+                    new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
+        } else if (mDockSide == WindowManager.DOCKED_LEFT) {
+            mShadowPaint.setShader(new LinearGradient(
+                    0, 0, right - left, 0,
+                    new int[] { startColor, middleColor, quarter, endColor },
+                    new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
+        } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
+            mShadowPaint.setShader(new LinearGradient(
+                    right - left, 0, 0, 0,
+                    new int[] { startColor, middleColor, quarter, endColor },
+                    new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (changed) {
+            updatePaint(left, top, right, bottom);
+            invalidate();
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawRect(0, 0, getWidth(), getHeight(), mShadowPaint);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1b2393a..11a7048 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -63,6 +63,8 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -262,11 +264,24 @@
 
     protected AssistManager mAssistManager;
 
+    protected boolean mVrMode;
+
     @Override  // NotificationData.Environment
     public boolean isDeviceProvisioned() {
         return mDeviceProvisioned;
     }
 
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            mVrMode = enabled;
+        }
+    };
+
+    public boolean isDeviceInVrMode() {
+        return mVrMode;
+    }
+
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
@@ -776,6 +791,14 @@
         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
                 null, null);
         updateCurrentProfilesCache();
+
+        IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
+        try {
+            vrManager.registerListener(mVrStateCallbacks);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+        }
+
     }
 
     protected void notifyUserAboutHiddenNotifications() {
@@ -1207,7 +1230,7 @@
 
     @Override
     public void toggleSplitScreen() {
-        toggleSplitScreenMode();
+        toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
     }
 
     @Override
@@ -1283,9 +1306,12 @@
     /**
      * Toggle docking the app window
      *
-     * @return {@code true} if the app window is docked after the toggle, {@code false} otherwise.
+     * @param metricsDockAction the action to log when docking is successful, or -1 to not log
+     *                          anything on successful docking
+     * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when
+     *                            undocking
      */
-    protected abstract boolean toggleSplitScreenMode();
+    protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
 
     /** Proxy for RecentsComponent */
 
@@ -2353,6 +2379,10 @@
     }
 
     protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
+        if (isDeviceInVrMode()) {
+            return false;
+        }
+
         if (mNotificationData.shouldFilterOut(sbn)) {
             if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5b00523..491ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -348,6 +348,12 @@
         setVisible(isShown());
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
+    }
+
     private void setVisible(final boolean isVisible) {
         if (isVisible) {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index a5ebbba..9ed5022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -232,8 +232,9 @@
             return;
         }
         final boolean isRtl = mParent.isLayoutRtl();
-        final float left = isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0;
-        final float right = isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear);
+        // TODO No need to cast to float here once b/28050538 is fixed.
+        final float left = (float) (isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0);
+        final float right = (float) (isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
         setTranslationX(onLeft ? left : right);
         mOnLeft = onLeft;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 03b51c6..244536d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -240,6 +240,11 @@
     }
 
     @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+        // TODO: Car demo mode.
+    }
+
+    @Override
     public boolean isPowerSave() {
         // Power save is not valid for the car, so always return false.
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 7fbb176..74bd096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -30,6 +30,7 @@
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -150,6 +151,10 @@
         });
     }
 
+    public void setQSPanel(QSPanel qsp) {
+        mMultiUserSwitch.setQsPanel(qsp);
+    }
+
     @Override
     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
         String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 54af684..6698d13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -127,10 +127,10 @@
                         mTmpInt2);
             }
         } else {
-            Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
-                    getContext(), v, ContactsContract.Profile.CONTENT_URI,
-                    ContactsContract.QuickContact.MODE_LARGE, null);
             if (mQsPanel != null) {
+                Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
+                        getContext(), v, ContactsContract.Profile.CONTENT_URI,
+                        ContactsContract.QuickContact.MODE_LARGE, null);
                 mQsPanel.getHost().startActivityDismissingKeyguard(intent);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index bb03454..583a63e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -214,15 +214,14 @@
                         < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
                     createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
                 }
-                boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds);
+                boolean docked = mRecentsComponent.dockTopTask(dragMode, createMode, initialBounds,
+                        MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
                 if (docked) {
                     mDragMode = dragMode;
                     if (mDragMode == DRAG_MODE_DIVIDER) {
                         mDivider.getView().startDragging(false /* animate */, true /* touching*/);
                     }
                     mDockWindowTouchSlopExceeded = true;
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE);
-
                     return true;
                 }
             }
@@ -250,7 +249,7 @@
                         mIsVertical
                                 ? mVelocityTracker.getXVelocity()
                                 : mVelocityTracker.getYVelocity(),
-                        true /* avoidDismissStart */);
+                        true /* avoidDismissStart */, false /* logMetrics */);
             } else if (mDragMode == DRAG_MODE_RECENTS) {
                 mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 6859348..581b611 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -526,6 +526,10 @@
                 public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
                         throws RemoteException {
                 }
+
+                @Override
+                public void onDockSideChanged(int newDockSide) throws RemoteException {
+                }
             });
         } catch (RemoteException e) {
             Log.e(TAG, "Failed registering docked stack exists listener", e);
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 75430ff..4f3a6a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -893,6 +893,7 @@
                     qsContainer.setHost(qsh);
                     mQSPanel = qsContainer.getQsPanel();
                     mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
+                    mKeyguardStatusBar.setQSPanel(mQSPanel);
                     mHeader = qsContainer.getHeader();
                     initSignalCluster(mHeader);
                     mHeader.setActivityStarter(PhoneStatusBar.this);
@@ -1177,31 +1178,26 @@
                 return false;
             }
 
-            boolean initiallyDocked = WindowManagerProxy.getInstance().getDockSide()
-                    == WindowManager.DOCKED_INVALID;
-            boolean dockedAtEnd = toggleSplitScreenMode();
-            if (dockedAtEnd != initiallyDocked) {
-                int logAction = dockedAtEnd ? MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS
-                        : MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS;
-                MetricsLogger.action(mContext, logAction);
-                return true;
-            }
-            return false;
+            toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
+                    MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
+            return true;
         }
     };
 
     @Override
-    protected boolean toggleSplitScreenMode() {
+    protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
         if (mRecents == null) {
-            return false;
+            return;
         }
         int dockSide = WindowManagerProxy.getInstance().getDockSide();
         if (dockSide == WindowManager.DOCKED_INVALID) {
-            return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
-                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null);
+            mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
+                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
         } else {
             EventBus.getDefault().send(new UndockingTaskEvent());
-            return false;
+            if (metricsUndockAction != -1) {
+                MetricsLogger.action(mContext, metricsUndockAction);
+            }
         }
     }
 
@@ -1374,6 +1370,10 @@
     }
 
     private boolean shouldSuppressFullScreenIntent(String key) {
+        if (isDeviceInVrMode()) {
+            return true;
+        }
+
         if (mPowerManager.isInteractive()) {
             return mNotificationData.shouldSuppressScreenOn(key);
         } else {
@@ -3591,11 +3591,10 @@
             dispatchDemoCommandToView(command, args, R.id.clock);
         }
         if (modeChange || command.equals(COMMAND_BATTERY)) {
-            dispatchDemoCommandToView(command, args, R.id.battery);
+            mBatteryController.dispatchDemoCommand(command, args);
         }
         if (modeChange || command.equals(COMMAND_STATUS)) {
             mIconController.dispatchDemoCommand(command, args);
-
         }
         if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
             mNetworkController.dispatchDemoCommand(command, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0ac2e7c..823af36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -139,6 +139,7 @@
         filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
 
         // listen for user / profile change.
@@ -509,7 +510,8 @@
             } else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
                 updateTTY(intent);
             } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) ||
-                    action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
+                    action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) ||
+                    action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)) {
                 updateQuietState();
                 updateManagedProfile();
             } else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index ea64fd8..559436b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,10 +16,12 @@
 
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.DemoMode;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public interface BatteryController {
+public interface BatteryController extends DemoMode {
     /**
      * Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 24207f3..5d734c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -21,9 +21,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.BatteryManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.util.Log;
+import com.android.systemui.DemoMode;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -43,6 +45,7 @@
     private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
     private final PowerManager mPowerManager;
     private final Handler mHandler;
+    private final Context mContext;
 
     protected int mLevel;
     protected boolean mPluggedIn;
@@ -52,17 +55,21 @@
     private boolean mTestmode = false;
 
     public BatteryControllerImpl(Context context) {
+        mContext = context;
         mHandler = new Handler();
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
 
+        registerReceiver();
+        updatePowerSave();
+    }
+
+    private void registerReceiver() {
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
         filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
         filter.addAction(ACTION_LEVEL_TEST);
-        context.registerReceiver(this, filter);
-
-        updatePowerSave();
+        mContext.registerReceiver(this, filter);
     }
 
     @Override
@@ -176,4 +183,28 @@
             mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
         }
     }
+
+    private boolean mDemoMode;
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+            mDemoMode = true;
+            mContext.unregisterReceiver(this);
+        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+            mDemoMode = false;
+            registerReceiver();
+            updatePowerSave();
+        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
+           String level = args.getString("level");
+           String plugged = args.getString("plugged");
+           if (level != null) {
+               mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
+           }
+           if (plugged != null) {
+               mPluggedIn = Boolean.parseBoolean(plugged);
+           }
+            fireBatteryLevelChanged();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index 6dd196b..c4c64e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -33,24 +33,29 @@
     }
 
     private void handleRestrictBackgroundChanged(boolean isDataSaving) {
-        final int N = mListeners.size();
-        for (int i = 0; i < N; i++) {
-            mListeners.get(i).onDataSaverChanged(isDataSaving);
+        synchronized (mListeners) {
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onDataSaverChanged(isDataSaving);
+            }
         }
     }
 
     public void addListener(Listener listener) {
-        mListeners.add(listener);
-        if (mListeners.size() == 1) {
-            mPolicyManager.registerListener(mPolicyListener);
+        synchronized (mListeners) {
+            mListeners.add(listener);
+            if (mListeners.size() == 1) {
+                mPolicyManager.registerListener(mPolicyListener);
+            }
         }
         listener.onDataSaverChanged(isDataSaverEnabled());
     }
 
     public void remListener(Listener listener) {
-        mListeners.remove(listener);
-        if (mListeners.size() == 0) {
-            mPolicyManager.unregisterListener(mPolicyListener);
+        synchronized (mListeners) {
+            mListeners.remove(listener);
+            if (mListeners.size() == 0) {
+                mPolicyManager.unregisterListener(mPolicyListener);
+            }
         }
     }
 
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 7c5cdfb..8b52bf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3799,6 +3799,7 @@
             } else {
                 getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
             }
+            mContinuousShadowUpdate = continuousShadowUpdate;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 450001f..acef81b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -127,8 +127,7 @@
     }
 
     @Override
-    protected boolean toggleSplitScreenMode() {
-        return false;
+    protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 748ee97..5e5da74 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -36,11 +36,16 @@
         super.onCreate(savedInstanceState);
 
         if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
+            final String action = getIntent().getAction();
+            boolean showDemoMode = action != null && action.equals(
+                    "com.android.settings.action.DEMO_MODE");
             boolean showNightMode = getIntent().getBooleanExtra(
                     NightModeFragment.EXTRA_SHOW_NIGHT_MODE, false);
+            final PreferenceFragment fragment = showNightMode ? new NightModeFragment()
+                    : showDemoMode ? new DemoModeFragment()
+                    : new TunerFragment();
             getFragmentManager().beginTransaction().replace(R.id.content_frame,
-                    showNightMode ? new NightModeFragment() : new TunerFragment(),
-                    TAG_TUNER).commit();
+                    fragment, TAG_TUNER).commit();
         }
     }
 
diff --git a/preloaded-classes b/preloaded-classes
index be645d2..d854769 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -12,6 +12,7 @@
 [Landroid.animation.Keyframe;
 [Landroid.animation.PropertyValuesHolder;
 [Landroid.app.LoaderManagerImpl;
+[Landroid.app.Notification$Action;
 [Landroid.content.ContentProviderResult;
 [Landroid.content.ContentValues;
 [Landroid.content.Intent;
@@ -29,7 +30,6 @@
 [Landroid.content.res.Configuration;
 [Landroid.content.res.StringBlock;
 [Landroid.content.res.XmlBlock;
-[Landroid.database.Cursor;
 [Landroid.database.CursorWindow;
 [Landroid.database.sqlite.SQLiteConnection$Operation;
 [Landroid.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus;
@@ -54,7 +54,6 @@
 [Landroid.graphics.drawable.GradientDrawable$Orientation;
 [Landroid.graphics.drawable.LayerDrawable$ChildDrawable;
 [Landroid.graphics.drawable.RippleForeground;
-[Landroid.hardware.display.WifiDisplay;
 [Landroid.hardware.soundtrigger.SoundTrigger$ConfidenceLevel;
 [Landroid.hardware.soundtrigger.SoundTrigger$Keyphrase;
 [Landroid.hardware.soundtrigger.SoundTrigger$KeyphraseRecognitionExtra;
@@ -83,6 +82,10 @@
 [Landroid.icu.util.ULocale$Category;
 [Landroid.icu.util.ULocale;
 [Landroid.media.AudioGain;
+[Landroid.media.MediaCodecInfo$CodecCapabilities;
+[Landroid.media.MediaCodecInfo$CodecProfileLevel;
+[Landroid.media.MediaCodecInfo$Feature;
+[Landroid.media.MediaCodecInfo;
 [Landroid.net.Network;
 [Landroid.net.NetworkInfo$DetailedState;
 [Landroid.net.NetworkInfo$State;
@@ -109,7 +112,6 @@
 [Landroid.text.method.TextKeyListener;
 [Landroid.text.style.AlignmentSpan;
 [Landroid.text.style.CharacterStyle;
-[Landroid.text.style.ClickableSpan;
 [Landroid.text.style.LeadingMarginSpan;
 [Landroid.text.style.LineBackgroundSpan;
 [Landroid.text.style.LineHeightSpan;
@@ -122,12 +124,11 @@
 [Landroid.text.style.URLSpan;
 [Landroid.text.style.WrapTogetherSpan;
 [Landroid.util.LongSparseArray;
-[Landroid.util.Pair;
+[Landroid.util.Range;
 [Landroid.util.Rational;
 [Landroid.view.Choreographer$CallbackQueue;
 [Landroid.view.Display$ColorTransform;
 [Landroid.view.Display$Mode;
-[Landroid.view.Display;
 [Landroid.view.HandlerActionQueue$HandlerAction;
 [Landroid.view.MenuItem;
 [Landroid.view.View;
@@ -198,11 +199,13 @@
 [Ljava.text.DateFormat$Field;
 [Ljava.text.Normalizer$Form;
 [Ljava.util.ArrayList;
+[Ljava.util.Comparators$NaturalOrderComparator;
 [Ljava.util.Enumeration;
 [Ljava.util.Formatter$Flags;
 [Ljava.util.Formatter$FormatString;
 [Ljava.util.HashMap$HashMapEntry;
 [Ljava.util.Hashtable$HashtableEntry;
+[Ljava.util.List;
 [Ljava.util.Locale$Category;
 [Ljava.util.Locale;
 [Ljava.util.Map$Entry;
@@ -216,11 +219,8 @@
 [Ljava.util.regex.Pattern;
 [Ljavax.crypto.Cipher$InitType;
 [Ljavax.crypto.Cipher$NeedToSet;
-[Ljavax.microedition.khronos.egl.EGLConfig;
 [Ljavax.net.ssl.KeyManager;
-[Ljavax.net.ssl.SSLSession;
 [Ljavax.net.ssl.TrustManager;
-[Ljavax.security.auth.x500.X500Principal;
 [Ljavax.security.cert.X509Certificate;
 [Llibcore.io.ClassPathURLStreamHandler;
 [Llibcore.reflect.AnnotationMember$DefaultValues;
@@ -307,12 +307,10 @@
 android.animation.PathKeyframes$1
 android.animation.PathKeyframes$2
 android.animation.PathKeyframes$FloatKeyframesBase
-android.animation.PathKeyframes$IntKeyframesBase
 android.animation.PathKeyframes$SimpleKeyframes
 android.animation.PropertyValuesHolder
 android.animation.PropertyValuesHolder$FloatPropertyValuesHolder
 android.animation.PropertyValuesHolder$IntPropertyValuesHolder
-android.animation.PropertyValuesHolder$PropertyValues
 android.animation.RectEvaluator
 android.animation.StateListAnimator
 android.animation.StateListAnimator$1
@@ -328,7 +326,6 @@
 android.app.Activity$HostCallbacks
 android.app.ActivityManager
 android.app.ActivityManager$MemoryInfo
-android.app.ActivityManager$RecentTaskInfo
 android.app.ActivityManager$RunningAppProcessInfo
 android.app.ActivityManager$RunningAppProcessInfo$1
 android.app.ActivityManager$StackId
@@ -348,7 +345,6 @@
 android.app.ActivityThread$ApplicationThread
 android.app.ActivityThread$BindServiceData
 android.app.ActivityThread$ContextCleanupInfo
-android.app.ActivityThread$CreateBackupAgentData
 android.app.ActivityThread$CreateServiceData
 android.app.ActivityThread$DropBoxReporter
 android.app.ActivityThread$EventLoggingReporter
@@ -381,7 +377,7 @@
 android.app.ContextImpl
 android.app.ContextImpl$ApplicationContentResolver
 android.app.Dialog
-android.app.Dialog$1
+android.app.Dialog$-void__init__android_content_Context_context_int_themeResId_boolean_createContextThemeWrapper_LambdaImpl0
 android.app.Dialog$ListenersHandler
 android.app.DialogFragment
 android.app.DownloadManager
@@ -402,8 +398,6 @@
 android.app.IAlarmManager$Stub
 android.app.IAlarmManager$Stub$Proxy
 android.app.IApplicationThread
-android.app.IBackupAgent
-android.app.IBackupAgent$Stub
 android.app.IInstrumentationWatcher
 android.app.IInstrumentationWatcher$Stub
 android.app.INotificationManager
@@ -415,6 +409,9 @@
 android.app.ITransientNotification$Stub
 android.app.IUiAutomationConnection
 android.app.IUiAutomationConnection$Stub
+android.app.IUiModeManager
+android.app.IUiModeManager$Stub
+android.app.IUiModeManager$Stub$Proxy
 android.app.Instrumentation
 android.app.IntentReceiverLeaked
 android.app.IntentService
@@ -437,16 +434,21 @@
 android.app.Notification
 android.app.Notification$1
 android.app.Notification$Action
+android.app.Notification$Action$1
 android.app.Notification$BigTextStyle
 android.app.Notification$Builder
+android.app.Notification$BuilderRemoteViews
 android.app.Notification$Style
 android.app.NotificationManager
+android.app.OnActivityPausedListener
 android.app.PendingIntent
 android.app.PendingIntent$1
 android.app.PendingIntent$CanceledException
 android.app.QueuedWork
 android.app.ReceiverRestrictedContext
 android.app.ResourcesManager
+android.app.ResourcesManager$1
+android.app.ResourcesManager$ActivityResources
 android.app.ResultInfo
 android.app.ResultInfo$1
 android.app.Service
@@ -530,11 +532,15 @@
 android.app.SystemServiceRegistry$69
 android.app.SystemServiceRegistry$7
 android.app.SystemServiceRegistry$70
+android.app.SystemServiceRegistry$71
+android.app.SystemServiceRegistry$72
+android.app.SystemServiceRegistry$73
+android.app.SystemServiceRegistry$74
 android.app.SystemServiceRegistry$8
 android.app.SystemServiceRegistry$9
 android.app.SystemServiceRegistry$CachedServiceFetcher
 android.app.SystemServiceRegistry$ServiceFetcher
-android.app.SystemServiceRegistry$StaticOuterContextServiceFetcher
+android.app.SystemServiceRegistry$StaticApplicationContextServiceFetcher
 android.app.SystemServiceRegistry$StaticServiceFetcher
 android.app.UiModeManager
 android.app.WallpaperManager
@@ -542,10 +548,9 @@
 android.app.admin.IDevicePolicyManager
 android.app.admin.IDevicePolicyManager$Stub
 android.app.admin.IDevicePolicyManager$Stub$Proxy
-android.app.backup.BackupAgent
-android.app.backup.BackupAgent$BackupServiceBinder
-android.app.backup.BackupAgent$SharedPrefsSynchronizer
-android.app.backup.BackupAgentHelper
+android.app.admin.SecurityLog
+android.app.admin.SecurityLog$SecurityEvent
+android.app.admin.SecurityLog$SecurityEvent$1
 android.app.backup.BackupDataInput
 android.app.backup.BackupDataInput$EntityHeader
 android.app.backup.BackupDataOutput
@@ -554,9 +559,6 @@
 android.app.backup.FileBackupHelperBase
 android.app.backup.FullBackup
 android.app.backup.FullBackupDataOutput
-android.app.backup.IBackupManager
-android.app.backup.IBackupManager$Stub
-android.app.backup.IBackupManager$Stub$Proxy
 android.app.job.JobScheduler
 android.app.trust.ITrustManager
 android.app.trust.ITrustManager$Stub
@@ -566,9 +568,6 @@
 android.app.usage.UsageStatsManager
 android.appwidget.AppWidgetManager
 android.appwidget.AppWidgetProvider
-android.auditing.SecurityLog
-android.auditing.SecurityLog$SecurityEvent
-android.auditing.SecurityLog$SecurityEvent$1
 android.bluetooth.BluetoothAdapter
 android.bluetooth.BluetoothAdapter$1
 android.bluetooth.BluetoothManager
@@ -585,6 +584,7 @@
 android.content.ActivityNotFoundException
 android.content.BroadcastReceiver
 android.content.BroadcastReceiver$PendingResult
+android.content.BroadcastReceiver$PendingResult$1
 android.content.ClipData
 android.content.ClipData$1
 android.content.ClipData$Item
@@ -600,7 +600,6 @@
 android.content.ContentProviderClient
 android.content.ContentProviderNative
 android.content.ContentProviderOperation
-android.content.ContentProviderOperation$Builder
 android.content.ContentProviderProxy
 android.content.ContentProviderResult
 android.content.ContentResolver
@@ -637,7 +636,6 @@
 android.content.IntentSender
 android.content.IntentSender$SendIntentException
 android.content.OperationApplicationException
-android.content.PeriodicSync
 android.content.RestrictionsManager
 android.content.ServiceConnection
 android.content.SharedPreferences
@@ -646,7 +644,6 @@
 android.content.SyncContext
 android.content.SyncRequest
 android.content.SyncRequest$1
-android.content.SyncRequest$Builder
 android.content.SyncResult
 android.content.SyncResult$1
 android.content.SyncStats
@@ -693,6 +690,7 @@
 android.content.pm.ResolveInfo$1
 android.content.pm.ServiceInfo
 android.content.pm.ServiceInfo$1
+android.content.pm.ShortcutManager
 android.content.pm.Signature
 android.content.pm.Signature$1
 android.content.pm.UserInfo
@@ -721,6 +719,8 @@
 android.content.res.Resources$NotFoundException
 android.content.res.Resources$Theme
 android.content.res.Resources$ThemeKey
+android.content.res.ResourcesImpl
+android.content.res.ResourcesImpl$ThemeImpl
 android.content.res.ResourcesKey
 android.content.res.StringBlock
 android.content.res.StringBlock$StyleIDs
@@ -762,7 +762,6 @@
 android.database.IContentObserver$Stub$Proxy
 android.database.MatrixCursor
 android.database.MatrixCursor$RowBuilder
-android.database.MergeCursor
 android.database.Observable
 android.database.SQLException
 android.database.sqlite.DatabaseObjectNotClosedException
@@ -775,7 +774,6 @@
 android.database.sqlite.SQLiteConnectionPool
 android.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus
 android.database.sqlite.SQLiteConnectionPool$ConnectionWaiter
-android.database.sqlite.SQLiteConstraintException
 android.database.sqlite.SQLiteCursor
 android.database.sqlite.SQLiteCursorDriver
 android.database.sqlite.SQLiteCustomFunction
@@ -807,7 +805,6 @@
 android.ddm.DdmHandleThread
 android.ddm.DdmHandleViewDebug
 android.ddm.DdmRegister
-android.graphics.AvoidXfermode
 android.graphics.Bitmap
 android.graphics.Bitmap$1
 android.graphics.Bitmap$CompressFormat
@@ -820,10 +817,10 @@
 android.graphics.Camera
 android.graphics.Canvas
 android.graphics.Canvas$EdgeType
+android.graphics.Canvas$NoImagePreloadHolder
 android.graphics.CanvasProperty
 android.graphics.Color
 android.graphics.ColorFilter
-android.graphics.ColorMatrix
 android.graphics.ColorMatrixColorFilter
 android.graphics.ComposePathEffect
 android.graphics.ComposeShader
@@ -859,6 +856,7 @@
 android.graphics.Paint$FontMetrics
 android.graphics.Paint$FontMetricsInt
 android.graphics.Paint$Join
+android.graphics.Paint$NoImagePreloadHolder
 android.graphics.Paint$Style
 android.graphics.PaintFlagsDrawFilter
 android.graphics.Path
@@ -869,7 +867,6 @@
 android.graphics.PathMeasure
 android.graphics.Picture
 android.graphics.PixelFormat
-android.graphics.PixelXorXfermode
 android.graphics.Point
 android.graphics.Point$1
 android.graphics.PointF
@@ -903,11 +900,14 @@
 android.graphics.drawable.Animatable2
 android.graphics.drawable.AnimatedStateListDrawable
 android.graphics.drawable.AnimatedStateListDrawable$AnimatedStateListState
+android.graphics.drawable.AnimatedStateListDrawable$Transition
 android.graphics.drawable.AnimatedVectorDrawable
 android.graphics.drawable.AnimatedVectorDrawable$1
 android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState
 android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState$PendingAnimator
+android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator
 android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT
+android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI
 android.graphics.drawable.AnimationDrawable
 android.graphics.drawable.AnimationDrawable$AnimationState
 android.graphics.drawable.BitmapDrawable
@@ -976,12 +976,14 @@
 android.hardware.Camera$Face
 android.hardware.ConsumerIrManager
 android.hardware.Sensor
+android.hardware.SensorEvent
 android.hardware.SensorEventListener
 android.hardware.SensorManager
 android.hardware.SerialManager
 android.hardware.SerialPort
 android.hardware.SystemSensorManager
 android.hardware.SystemSensorManager$BaseEventQueue
+android.hardware.SystemSensorManager$SensorEventQueue
 android.hardware.camera2.CameraCharacteristics$Key
 android.hardware.camera2.CameraManager
 android.hardware.camera2.CaptureRequest$Key
@@ -1000,12 +1002,6 @@
 android.hardware.display.IDisplayManager$Stub$Proxy
 android.hardware.display.IDisplayManagerCallback
 android.hardware.display.IDisplayManagerCallback$Stub
-android.hardware.display.WifiDisplay
-android.hardware.display.WifiDisplay$1
-android.hardware.display.WifiDisplaySessionInfo
-android.hardware.display.WifiDisplaySessionInfo$1
-android.hardware.display.WifiDisplayStatus
-android.hardware.display.WifiDisplayStatus$1
 android.hardware.fingerprint.FingerprintManager
 android.hardware.hdmi.HdmiControlManager
 android.hardware.input.IInputDevicesChangedListener
@@ -1021,6 +1017,10 @@
 # android.hardware.location.ActivityRecognitionHardware
 # android.hardware.location.IActivityRecognitionHardware
 # android.hardware.location.IActivityRecognitionHardware$Stub
+android.hardware.location.ContextHubManager
+android.hardware.location.ContextHubService
+android.hardware.location.IContextHubService
+android.hardware.location.IContextHubService$Stub
 android.hardware.radio.RadioManager
 android.hardware.radio.RadioManager$AmBandConfig
 android.hardware.radio.RadioManager$AmBandConfig$1
@@ -1046,6 +1046,7 @@
 android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel
 android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel$1
 android.hardware.soundtrigger.SoundTrigger$GenericRecognitionEvent
+android.hardware.soundtrigger.SoundTrigger$GenericRecognitionEvent$1
 android.hardware.soundtrigger.SoundTrigger$GenericSoundModel
 android.hardware.soundtrigger.SoundTrigger$Keyphrase
 android.hardware.soundtrigger.SoundTrigger$Keyphrase$1
@@ -1070,7 +1071,6 @@
 android.hardware.usb.IUsbManager$Stub$Proxy
 android.hardware.usb.UsbDevice
 android.hardware.usb.UsbDeviceConnection
-android.hardware.usb.UsbInterface
 android.hardware.usb.UsbManager
 android.hardware.usb.UsbRequest
 android.icu.impl.BMPSet
@@ -1396,6 +1396,7 @@
 android.media.AudioDevicePort
 android.media.AudioDevicePortConfig
 android.media.AudioFormat
+android.media.AudioFormat$1
 android.media.AudioGain
 android.media.AudioGainConfig
 android.media.AudioHandle
@@ -1412,8 +1413,6 @@
 android.media.AudioPortConfig
 android.media.AudioPortEventHandler
 android.media.AudioRecord
-android.media.AudioRoutesInfo
-android.media.AudioRoutesInfo$1
 android.media.AudioRouting
 android.media.AudioSystem
 android.media.AudioTimestamp
@@ -1425,21 +1424,13 @@
 android.media.ExifInterface
 android.media.IAudioFocusDispatcher
 android.media.IAudioFocusDispatcher$Stub
-android.media.IAudioRoutesObserver
-android.media.IAudioRoutesObserver$Stub
 android.media.IAudioService
 android.media.IAudioService$Stub
 android.media.IAudioService$Stub$Proxy
 android.media.IMediaHTTPConnection
 android.media.IMediaHTTPConnection$Stub
-android.media.IMediaRouterClient
-android.media.IMediaRouterClient$Stub
-android.media.IMediaRouterService
-android.media.IMediaRouterService$Stub
 android.media.IRecordingConfigDispatcher
 android.media.IRecordingConfigDispatcher$Stub
-android.media.IRemoteVolumeObserver
-android.media.IRemoteVolumeObserver$Stub
 android.media.Image
 android.media.ImageReader
 android.media.ImageReader$SurfaceImage
@@ -1447,10 +1438,18 @@
 android.media.ImageWriter$WriterSurfaceImage
 android.media.JetPlayer
 android.media.MediaCodec
+android.media.MediaCodecInfo
+android.media.MediaCodecInfo$AudioCapabilities
+android.media.MediaCodecInfo$CodecCapabilities
+android.media.MediaCodecInfo$CodecProfileLevel
+android.media.MediaCodecInfo$EncoderCapabilities
+android.media.MediaCodecInfo$Feature
+android.media.MediaCodecInfo$VideoCapabilities
 android.media.MediaCodecList
 android.media.MediaCrypto
 android.media.MediaDrm
 android.media.MediaExtractor
+android.media.MediaFormat
 android.media.MediaHTTPConnection
 android.media.MediaMetadataRetriever
 android.media.MediaMuxer
@@ -1461,17 +1460,6 @@
 android.media.MediaPlayer$OnSeekCompleteListener
 android.media.MediaRecorder
 android.media.MediaRouter
-android.media.MediaRouter$Callback
-android.media.MediaRouter$RouteCategory
-android.media.MediaRouter$RouteInfo
-android.media.MediaRouter$RouteInfo$1
-android.media.MediaRouter$Static
-android.media.MediaRouter$Static$1
-android.media.MediaRouter$Static$Client
-android.media.MediaRouter$VolumeChangeReceiver
-android.media.MediaRouter$WifiDisplayStatusChangedReceiver
-android.media.MediaRouterClientState
-android.media.MediaRouterClientState$1
 android.media.MediaScanner
 android.media.MediaSync
 android.media.PlaybackParams
@@ -1479,9 +1467,10 @@
 android.media.RemoteDisplay
 android.media.ResampleInputStream
 android.media.SubtitleController$Listener
-android.media.ThumbnailUtils
 android.media.ToneGenerator
-android.media.audiofx.AcousticEchoCanceler
+android.media.Utils
+android.media.Utils$1
+android.media.Utils$2
 android.media.audiofx.AudioEffect
 android.media.audiopolicy.AudioMix
 android.media.audiopolicy.AudioMixingRule
@@ -1504,13 +1493,13 @@
 android.net.ConnectivityManager
 android.net.ConnectivityManager$CallbackHandler
 android.net.ConnectivityManager$NetworkCallback
+android.net.ConnectivityThread
 android.net.Credentials
-android.net.DhcpResults
-android.net.DhcpResults$1
 android.net.EthernetManager
 android.net.IConnectivityManager
 android.net.IConnectivityManager$Stub
 android.net.IConnectivityManager$Stub$Proxy
+android.net.INetworkPolicyManager
 android.net.IpPrefix
 android.net.IpPrefix$1
 android.net.LinkAddress
@@ -1545,8 +1534,6 @@
 android.net.SSLCertificateSocketFactory
 android.net.SSLCertificateSocketFactory$1
 android.net.SSLSessionCache
-android.net.StaticIpConfiguration
-android.net.StaticIpConfiguration$1
 android.net.TrafficStats
 android.net.Uri
 android.net.Uri$1
@@ -1584,10 +1571,16 @@
 android.nfc.INfcAdapter$Stub$Proxy
 android.nfc.INfcCardEmulation
 android.nfc.INfcCardEmulation$Stub
+android.nfc.INfcCardEmulation$Stub$Proxy
 android.nfc.INfcFCardEmulation
 android.nfc.INfcFCardEmulation$Stub
+android.nfc.INfcFCardEmulation$Stub$Proxy
 android.nfc.INfcTag
 android.nfc.INfcTag$Stub
+android.nfc.INfcTag$Stub$Proxy
+android.nfc.NfcActivityManager
+android.nfc.NfcAdapter
+android.nfc.NfcAdapter$1
 android.nfc.NfcManager
 android.opengl.EGL14
 android.opengl.EGLConfig
@@ -1631,9 +1624,8 @@
 android.os.CancellationSignal$OnCancelListener
 android.os.CancellationSignal$Transport
 android.os.ConditionVariable
-android.os.CpuUsageInfo
-android.os.CpuUsageInfo$1
 android.os.DeadObjectException
+android.os.DeadSystemException
 android.os.Debug
 android.os.Debug$MemoryInfo
 android.os.Debug$MemoryInfo$1
@@ -1665,6 +1657,8 @@
 android.os.IUserManager
 android.os.IUserManager$Stub
 android.os.IUserManager$Stub$Proxy
+android.os.IVibratorService
+android.os.IVibratorService$Stub
 android.os.Looper
 android.os.MemoryFile
 android.os.Message
@@ -1679,10 +1673,13 @@
 android.os.ParcelFileDescriptor
 android.os.ParcelFileDescriptor$1
 android.os.ParcelFileDescriptor$AutoCloseInputStream
+android.os.ParcelFileDescriptor$AutoCloseOutputStream
 android.os.ParcelUuid
 android.os.Parcelable
 android.os.Parcelable$ClassLoaderCreator
 android.os.Parcelable$Creator
+android.os.ParcelableParcel
+android.os.ParcelableParcel$1
 android.os.PatternMatcher
 android.os.PatternMatcher$1
 android.os.PersistableBundle
@@ -1691,6 +1688,7 @@
 android.os.PowerManager$WakeLock
 android.os.PowerManager$WakeLock$1
 android.os.Process
+android.os.RecoverySystem
 android.os.RemoteException
 android.os.ResultReceiver
 android.os.SELinux
@@ -1726,24 +1724,26 @@
 android.os.StrictMode$VmPolicy$Builder
 android.os.SystemClock
 android.os.SystemProperties
+android.os.SystemVibrator
 android.os.Trace
 android.os.Trace$1
-android.os.TransactionTooLargeException
 android.os.UEventObserver
 android.os.UserHandle
 android.os.UserHandle$1
 android.os.UserManager
 android.os.Vibrator
 android.os.ZygoteStartFailedEx
+android.os.health.SystemHealthManager
 android.os.storage.IMountService
 android.os.storage.IMountService$Stub
 android.os.storage.IMountService$Stub$Proxy
+android.os.storage.IObbActionListener
+android.os.storage.IObbActionListener$Stub
 android.os.storage.StorageManager
+android.os.storage.StorageManager$ObbActionListener
 android.os.storage.StorageVolume
 android.os.storage.StorageVolume$1
-android.preference.Preference$OnPreferenceChangeListener
 android.preference.PreferenceActivity
-android.preference.PreferenceFragment
 android.preference.PreferenceFragment$OnPreferenceStartFragmentCallback
 android.preference.PreferenceManager
 android.preference.PreferenceManager$OnPreferenceTreeClickListener
@@ -1836,7 +1836,6 @@
 android.system.UnixSocketAddress
 android.telecom.TelecomManager
 android.telephony.CarrierConfigManager
-android.telephony.PhoneNumberUtils
 android.telephony.Rlog
 android.telephony.SubscriptionManager
 android.telephony.TelephonyManager
@@ -1852,12 +1851,15 @@
 android.text.GetChars
 android.text.GraphicsOperations
 android.text.Html
+android.text.Html$HtmlParser
+android.text.HtmlToSpannedConverter
 android.text.Hyphenator
 android.text.InputFilter
 android.text.InputType
 android.text.Layout
 android.text.Layout$Alignment
 android.text.Layout$Directions
+android.text.Layout$Ellipsizer
 android.text.MeasuredText
 android.text.NoCopySpan
 android.text.NoCopySpan$Concrete
@@ -1916,7 +1918,6 @@
 android.text.method.SingleLineTransformationMethod
 android.text.method.TextKeyListener
 android.text.method.TextKeyListener$Capitalize
-android.text.method.Touch
 android.text.method.TransformationMethod
 android.text.method.TransformationMethod2
 android.text.style.AlignmentSpan
@@ -1937,6 +1938,7 @@
 android.text.style.StyleSpan
 android.text.style.SuggestionSpan
 android.text.style.TabStopSpan
+android.text.style.TextAppearanceSpan
 android.text.style.URLSpan
 android.text.style.UnderlineSpan
 android.text.style.UpdateAppearance
@@ -1985,7 +1987,6 @@
 android.util.EventLog$Event
 android.util.FloatProperty
 android.util.IntProperty
-android.util.JsonReader
 android.util.LocaleList
 android.util.LocaleList$1
 android.util.Log
@@ -1993,7 +1994,6 @@
 android.util.Log$ImmediateLogWriter
 android.util.Log$TerribleFailureHandler
 android.util.LogPrinter
-android.util.LongArray
 android.util.LongSparseArray
 android.util.LongSparseLongArray
 android.util.LruCache
@@ -2006,15 +2006,16 @@
 android.util.Pair
 android.util.PathParser
 android.util.PathParser$PathData
-android.util.Patterns
 android.util.Pools$Pool
 android.util.Pools$SimplePool
 android.util.Pools$SynchronizedPool
 android.util.Printer
 android.util.Property
+android.util.Range
 android.util.Rational
 android.util.Singleton
 android.util.Size
+android.util.SizeF
 android.util.Slog
 android.util.SparseArray
 android.util.SparseBooleanArray
@@ -2031,6 +2032,7 @@
 android.view.AbsSavedState$2
 android.view.ActionMode
 android.view.ActionMode$Callback
+android.view.ActionMode$Callback2
 android.view.ActionProvider
 android.view.ActionProvider$SubUiVisibilityListener
 android.view.Choreographer
@@ -2135,7 +2137,6 @@
 android.view.RenderNode
 android.view.RenderNodeAnimator
 android.view.RenderNodeAnimator$1
-android.view.RenderNodeAnimatorSetHelper
 android.view.SearchEvent
 android.view.SoundEffectConstants
 android.view.SubMenu
@@ -2151,6 +2152,7 @@
 android.view.SurfaceSession
 android.view.SurfaceView
 android.view.TextureView
+android.view.TextureView$SurfaceTextureListener
 android.view.ThreadedRenderer
 android.view.ThreadedRenderer$HardwareDrawCallbacks
 android.view.ThreadedRenderer$ProcessInitializer
@@ -2174,6 +2176,7 @@
 android.view.View$AttachInfo$Callbacks
 android.view.View$BaseSavedState
 android.view.View$BaseSavedState$1
+android.view.View$CheckForLongPress
 android.view.View$CheckForTap
 android.view.View$ForegroundInfo
 android.view.View$ListenerInfo
@@ -2258,10 +2261,10 @@
 android.view.WindowCallbacks
 android.view.WindowContentFrameStats
 android.view.WindowContentFrameStats$1
-android.view.WindowId
 android.view.WindowInsets
 android.view.WindowLeaked
 android.view.WindowManager
+android.view.WindowManager$BadTokenException
 android.view.WindowManager$LayoutParams
 android.view.WindowManager$LayoutParams$1
 android.view.WindowManagerGlobal
@@ -2280,6 +2283,7 @@
 android.view.accessibility.AccessibilityRecord
 android.view.accessibility.CaptioningManager
 android.view.accessibility.CaptioningManager$1
+android.view.accessibility.CaptioningManager$CaptionStyle
 android.view.accessibility.CaptioningManager$CaptioningChangeListener
 android.view.accessibility.CaptioningManager$MyContentObserver
 android.view.accessibility.IAccessibilityManager
@@ -2295,6 +2299,7 @@
 android.view.animation.Animation$2
 android.view.animation.Animation$3
 android.view.animation.Animation$AnimationListener
+android.view.animation.Animation$Description
 android.view.animation.AnimationSet
 android.view.animation.AnimationUtils
 android.view.animation.BaseInterpolator
@@ -2316,6 +2321,7 @@
 android.view.inputmethod.ExtractedText
 android.view.inputmethod.ExtractedText$1
 android.view.inputmethod.InputConnection
+android.view.inputmethod.InputConnectionInspector
 android.view.inputmethod.InputMethodManager
 android.view.inputmethod.InputMethodManager$1
 android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper
@@ -2323,15 +2329,17 @@
 android.view.inputmethod.InputMethodManager$H
 android.view.inputmethod.InputMethodManager$ImeInputEventSender
 android.view.inputmethod.InputMethodManager$PendingEvent
-android.view.inputmethod.InputMethodSubtype
 android.view.textservice.SpellCheckerSession$SpellCheckerSessionListener
 android.view.textservice.SpellCheckerSubtype
 android.view.textservice.SpellCheckerSubtype$1
 android.view.textservice.TextServicesManager
+android.webkit.IWebViewUpdateService
+android.webkit.IWebViewUpdateService$Stub
 android.webkit.MimeTypeMap
 android.webkit.URLUtil
 android.webkit.WebSettings
 android.webkit.WebView
+android.webkit.WebViewClient
 android.webkit.WebViewFactory
 android.webkit.WebViewFactory$MissingWebViewPackageException
 android.widget.AbsListView
@@ -2348,6 +2356,7 @@
 android.widget.AbsListView$WindowRunnnable
 android.widget.AbsSeekBar
 android.widget.AbsSpinner
+android.widget.AbsSpinner$RecycleBin
 android.widget.AbsoluteLayout
 android.widget.ActionMenuPresenter
 android.widget.ActionMenuPresenter$1
@@ -2362,13 +2371,9 @@
 android.widget.AdapterView
 android.widget.AdapterView$AdapterDataSetObserver
 android.widget.AdapterView$OnItemClickListener
-android.widget.AdapterView$OnItemLongClickListener
 android.widget.AdapterView$OnItemSelectedListener
 android.widget.ArrayAdapter
 android.widget.AutoCompleteTextView
-android.widget.AutoCompleteTextView$DropDownItemClickListener
-android.widget.AutoCompleteTextView$MyWatcher
-android.widget.AutoCompleteTextView$PassThroughClickListener
 android.widget.BaseAdapter
 android.widget.Button
 android.widget.CheckBox
@@ -2381,15 +2386,17 @@
 android.widget.Editor
 android.widget.Editor$1
 android.widget.Editor$2
-android.widget.Editor$3
 android.widget.Editor$Blink
 android.widget.Editor$CursorAnchorInfoNotifier
+android.widget.Editor$CursorController
 android.widget.Editor$EditOperation
 android.widget.Editor$EditOperation$1
 android.widget.Editor$InputContentType
 android.widget.Editor$InputMethodState
+android.widget.Editor$InsertionPointCursorController
 android.widget.Editor$PositionListener
 android.widget.Editor$ProcessTextIntentActionsHandler
+android.widget.Editor$SelectionModifierCursorController
 android.widget.Editor$SpanController
 android.widget.Editor$SuggestionHelper
 android.widget.Editor$SuggestionHelper$SuggestionSpanComparator
@@ -2439,6 +2446,19 @@
 android.widget.RelativeLayout$DependencyGraph$Node
 android.widget.RelativeLayout$LayoutParams
 android.widget.RemoteViews
+android.widget.RemoteViews$1
+android.widget.RemoteViews$2
+android.widget.RemoteViews$3
+android.widget.RemoteViews$Action
+android.widget.RemoteViews$ActionException
+android.widget.RemoteViews$BitmapCache
+android.widget.RemoteViews$LayoutParamAction
+android.widget.RemoteViews$MemoryUsageCounter
+android.widget.RemoteViews$MutablePair
+android.widget.RemoteViews$OnClickHandler
+android.widget.RemoteViews$ReflectionAction
+android.widget.RemoteViews$RuntimeAction
+android.widget.RemoteViews$SetDrawableParameters
 android.widget.RemoteViewsAdapter$RemoteAdapterConnectionCallback
 android.widget.RtlSpacingHelper
 android.widget.ScrollBarDrawable
@@ -2451,7 +2471,10 @@
 android.widget.SpellChecker
 android.widget.SpellChecker$SpellParser
 android.widget.Spinner
+android.widget.Spinner$SpinnerPopup
 android.widget.SpinnerAdapter
+android.widget.Switch
+android.widget.Switch$1
 android.widget.TextView
 android.widget.TextView$3
 android.widget.TextView$BufferType
@@ -2460,6 +2483,7 @@
 android.widget.TextView$Drawables
 android.widget.TextView$OnEditorActionListener
 android.widget.TextView$SavedState
+android.widget.TextView$SavedState$1
 android.widget.ThemedSpinnerAdapter
 android.widget.Toast
 android.widget.Toast$TN
@@ -2470,7 +2494,6 @@
 android.widget.Toolbar$2
 android.widget.Toolbar$ExpandedActionViewMenuPresenter
 android.widget.Toolbar$LayoutParams
-android.widget.ViewAnimator
 android.widget.WrapperListAdapter
 com.android.dex.Annotation
 com.android.dex.ClassData
@@ -2511,9 +2534,10 @@
 com.android.internal.app.IAppOpsService
 com.android.internal.app.IAppOpsService$Stub
 com.android.internal.app.IAppOpsService$Stub$Proxy
+com.android.internal.app.IBatteryStats
+com.android.internal.app.IBatteryStats$Stub
 com.android.internal.app.IVoiceInteractor
 com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.WindowDecorActionBar
 com.android.internal.appwidget.IAppWidgetService
 com.android.internal.appwidget.IAppWidgetService$Stub
 com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
@@ -2521,6 +2545,8 @@
 com.android.internal.content.ReferrerIntent
 com.android.internal.content.ReferrerIntent$1
 com.android.internal.inputmethod.InputMethodUtils
+com.android.internal.inputmethod.InputMethodUtils$1
+com.android.internal.inputmethod.LocaleUtils$LocaleExtractor
 com.android.internal.logging.AndroidConfig
 com.android.internal.logging.AndroidHandler
 com.android.internal.logging.AndroidHandler$1
@@ -2575,6 +2601,9 @@
 com.android.internal.util.FastXmlSerializer
 com.android.internal.util.GrowingArrayUtils
 com.android.internal.util.LineBreakBufferedWriter
+com.android.internal.util.MessageUtils
+com.android.internal.util.NotificationColorUtil
+com.android.internal.util.NotificationColorUtil$ColorUtilsFromCompat
 com.android.internal.util.Preconditions
 com.android.internal.util.VirtualRefBasePtr
 com.android.internal.util.XmlUtils
@@ -2613,13 +2642,9 @@
 com.android.internal.view.menu.MenuPresenter$Callback
 com.android.internal.view.menu.MenuView
 com.android.internal.view.menu.ShowableListMenu
-com.android.internal.widget.ActionBarOverlayLayout$ActionBarVisibilityCallback
-com.android.internal.widget.AlertDialogLayout
 com.android.internal.widget.BackgroundFallback
-com.android.internal.widget.ButtonBarLayout
 com.android.internal.widget.DecorContentParent
 com.android.internal.widget.DecorToolbar
-com.android.internal.widget.DialogTitle
 com.android.internal.widget.EditableInputConnection
 com.android.internal.widget.ScrollBarUtils
 com.android.internal.widget.ToolbarWidgetWrapper
@@ -2884,7 +2909,6 @@
 dalvik.system.CloseGuard$DefaultReporter
 dalvik.system.CloseGuard$Reporter
 dalvik.system.DalvikLogHandler
-dalvik.system.DalvikLogging
 dalvik.system.DexClassLoader
 dalvik.system.DexFile
 dalvik.system.DexFile$DFEnum
@@ -2911,6 +2935,7 @@
 java.io.ByteArrayOutputStream
 java.io.CharArrayWriter
 java.io.Closeable
+java.io.Console
 java.io.DataInput
 java.io.DataInputStream
 java.io.DataOutput
@@ -2922,15 +2947,14 @@
 java.io.Externalizable
 java.io.File
 java.io.File$PathStatus
-java.io.File$TempDirectory
 java.io.FileDescriptor
+java.io.FileFilter
 java.io.FileInputStream
 java.io.FileInputStream$UseManualSkipException
 java.io.FileNotFoundException
 java.io.FileOutputStream
 java.io.FileReader
 java.io.FileSystem
-java.io.FileWriter
 java.io.FilenameFilter
 java.io.FilterInputStream
 java.io.FilterOutputStream
@@ -2941,7 +2965,6 @@
 java.io.InputStreamReader
 java.io.InterruptedIOException
 java.io.InvalidObjectException
-java.io.NotSerializableException
 java.io.ObjectInput
 java.io.ObjectInputStream
 java.io.ObjectInputStream$BlockDataInputStream
@@ -3013,6 +3036,7 @@
 java.lang.EnumConstantNotPresentException
 java.lang.Error
 java.lang.Exception
+java.lang.ExceptionInInitializerError
 java.lang.Float
 java.lang.FloatingDecimal
 java.lang.FloatingDecimal$1
@@ -3056,7 +3080,6 @@
 java.lang.RuntimeException
 java.lang.RuntimePermission
 java.lang.SecurityException
-java.lang.SecurityManager
 java.lang.Short
 java.lang.Short$ShortCache
 java.lang.Shutdown
@@ -3133,7 +3156,6 @@
 java.math.BigInt
 java.math.BigInteger
 java.math.BitLevel
-java.math.Multiplication
 java.math.NativeBN
 java.math.RoundingMode
 java.net.AbstractPlainDatagramSocketImpl
@@ -3260,22 +3282,18 @@
 java.security.AlgorithmParameters
 java.security.AlgorithmParametersSpi
 java.security.BasicPermission
-java.security.BasicPermissionCollection
 java.security.CryptoPrimitive
 java.security.GeneralSecurityException
 java.security.Guard
 java.security.InvalidAlgorithmParameterException
 java.security.InvalidKeyException
-java.security.InvalidParameterException
 java.security.Key
 java.security.KeyException
 java.security.KeyFactory
 java.security.KeyFactorySpi
 java.security.KeyManagementException
-java.security.KeyPair
 java.security.KeyStore
 java.security.KeyStore$1
-java.security.KeyStore$LoadStoreParameter
 java.security.KeyStoreException
 java.security.KeyStoreSpi
 java.security.MessageDigest
@@ -3285,6 +3303,7 @@
 java.security.NoSuchProviderException
 java.security.Permission
 java.security.PermissionCollection
+java.security.Permissions
 java.security.Principal
 java.security.PrivateKey
 java.security.PrivilegedAction
@@ -3332,7 +3351,6 @@
 java.security.cert.PKIXRevocationChecker
 java.security.cert.PolicyNode
 java.security.cert.TrustAnchor
-java.security.cert.X509CRL
 java.security.cert.X509CertSelector
 java.security.cert.X509Certificate
 java.security.cert.X509Extension
@@ -3342,7 +3360,6 @@
 java.security.interfaces.ECPrivateKey
 java.security.interfaces.ECPublicKey
 java.security.interfaces.RSAKey
-java.security.interfaces.RSAPrivateCrtKey
 java.security.interfaces.RSAPrivateKey
 java.security.interfaces.RSAPublicKey
 java.security.spec.AlgorithmParameterSpec
@@ -3351,19 +3368,14 @@
 java.security.spec.ECFieldFp
 java.security.spec.ECParameterSpec
 java.security.spec.ECPoint
-java.security.spec.ECPrivateKeySpec
 java.security.spec.ECPublicKeySpec
 java.security.spec.EllipticCurve
 java.security.spec.EncodedKeySpec
 java.security.spec.InvalidKeySpecException
 java.security.spec.InvalidParameterSpecException
 java.security.spec.KeySpec
-java.security.spec.PKCS8EncodedKeySpec
-java.security.spec.RSAPrivateCrtKeySpec
-java.security.spec.RSAPrivateKeySpec
 java.security.spec.RSAPublicKeySpec
 java.security.spec.X509EncodedKeySpec
-java.sql.Timestamp
 java.text.AttributedCharacterIterator$Attribute
 java.text.CalendarBuilder
 java.text.Collator
@@ -3401,7 +3413,9 @@
 java.util.AbstractSequentialList
 java.util.AbstractSet
 java.util.ArrayDeque
+java.util.ArrayDeque$DeqIterator
 java.util.ArrayList
+java.util.ArrayList$ArrayListSpliterator
 java.util.ArrayList$Itr
 java.util.ArrayList$ListItr
 java.util.ArrayList$SubList
@@ -3414,6 +3428,7 @@
 java.util.Collections
 java.util.Collections$1
 java.util.Collections$2
+java.util.Collections$3
 java.util.Collections$AsLIFOQueue
 java.util.Collections$CheckedCollection
 java.util.Collections$CheckedList
@@ -3456,6 +3471,14 @@
 java.util.Collections$UnmodifiableSortedSet
 java.util.ComparableTimSort
 java.util.Comparator
+java.util.Comparator$-java_util_Comparator_comparingDouble_java_util_function_ToDoubleFunction_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparingInt_java_util_function_ToIntFunction_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparingLong_java_util_function_ToLongFunction_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparing_java_util_function_Function_keyExtractor_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_comparing_java_util_function_Function_keyExtractor_java_util_Comparator_keyComparator_LambdaImpl0
+java.util.Comparator$-java_util_Comparator_thenComparing_java_util_Comparator_other_LambdaImpl0
+java.util.Comparators$NaturalOrderComparator
+java.util.Comparators$NullComparator
 java.util.ConcurrentModificationException
 java.util.Currency
 java.util.Date
@@ -3464,7 +3487,6 @@
 java.util.DualPivotQuicksort
 java.util.EnumMap
 java.util.EnumMap$1
-java.util.EnumMap$EnumMapIterator
 java.util.EnumSet
 java.util.Enumeration
 java.util.EventListener
@@ -3497,8 +3519,6 @@
 java.util.Hashtable$KeySet
 java.util.Hashtable$ValueCollection
 java.util.IdentityHashMap
-java.util.IdentityHashMap$EntryIterator
-java.util.IdentityHashMap$EntrySet
 java.util.IdentityHashMap$IdentityHashMapIterator
 java.util.IdentityHashMap$KeySet
 java.util.IllegalFormatException
@@ -3538,7 +3558,6 @@
 java.util.RandomAccess
 java.util.RandomAccessSubList
 java.util.RegularEnumSet
-java.util.RegularEnumSet$EnumSetIterator
 java.util.ResourceBundle
 java.util.ResourceBundle$1
 java.util.ResourceBundle$BundleReference
@@ -3550,7 +3569,6 @@
 java.util.ResourceBundle$LoaderReference
 java.util.ResourceBundle$RBClassLoader
 java.util.ResourceBundle$RBClassLoader$1
-java.util.Scanner
 java.util.ServiceLoader
 java.util.ServiceLoader$1
 java.util.ServiceLoader$LazyIterator
@@ -3558,6 +3576,17 @@
 java.util.SimpleTimeZone
 java.util.SortedMap
 java.util.SortedSet
+java.util.Spliterator
+java.util.Spliterator$OfDouble
+java.util.Spliterator$OfInt
+java.util.Spliterator$OfLong
+java.util.Spliterator$OfPrimitive
+java.util.Spliterators
+java.util.Spliterators$EmptySpliterator
+java.util.Spliterators$EmptySpliterator$OfDouble
+java.util.Spliterators$EmptySpliterator$OfInt
+java.util.Spliterators$EmptySpliterator$OfLong
+java.util.Spliterators$EmptySpliterator$OfRef
 java.util.Stack
 java.util.StringTokenizer
 java.util.SubList
@@ -3569,12 +3598,10 @@
 java.util.TimerTask
 java.util.TimerThread
 java.util.TreeMap
-java.util.TreeMap$AscendingSubMap
 java.util.TreeMap$EntryIterator
 java.util.TreeMap$EntrySet
 java.util.TreeMap$KeyIterator
 java.util.TreeMap$KeySet
-java.util.TreeMap$NavigableSubMap
 java.util.TreeMap$PrivateEntryIterator
 java.util.TreeMap$TreeMapEntry
 java.util.TreeMap$ValueIterator
@@ -3595,14 +3622,12 @@
 java.util.WeakHashMap$Values
 java.util.XMLUtils
 java.util.concurrent.AbstractExecutorService
-java.util.concurrent.ArrayBlockingQueue
 java.util.concurrent.BlockingQueue
 java.util.concurrent.Callable
 java.util.concurrent.CancellationException
 java.util.concurrent.ConcurrentHashMap
 java.util.concurrent.ConcurrentHashMap$BaseIterator
 java.util.concurrent.ConcurrentHashMap$CollectionView
-java.util.concurrent.ConcurrentHashMap$CounterCell
 java.util.concurrent.ConcurrentHashMap$ForwardingNode
 java.util.concurrent.ConcurrentHashMap$KeyIterator
 java.util.concurrent.ConcurrentHashMap$KeySetView
@@ -3611,8 +3636,6 @@
 java.util.concurrent.ConcurrentHashMap$Traverser
 java.util.concurrent.ConcurrentHashMap$TreeBin
 java.util.concurrent.ConcurrentHashMap$TreeNode
-java.util.concurrent.ConcurrentHashMap$ValueIterator
-java.util.concurrent.ConcurrentHashMap$ValuesView
 java.util.concurrent.ConcurrentLinkedQueue
 java.util.concurrent.ConcurrentLinkedQueue$Node
 java.util.concurrent.ConcurrentMap
@@ -3689,6 +3712,16 @@
 java.util.concurrent.locks.ReentrantReadWriteLock$Sync
 java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter
 java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock
+java.util.function.BiConsumer
+java.util.function.BiFunction
+java.util.function.Consumer
+java.util.function.Function
+java.util.function.Predicate
+java.util.function.Supplier
+java.util.function.ToDoubleFunction
+java.util.function.ToIntFunction
+java.util.function.ToLongFunction
+java.util.function.UnaryOperator
 java.util.jar.JarEntry
 java.util.jar.JarFile
 java.util.jar.JarFile$1
@@ -3709,15 +3742,19 @@
 java.util.logging.LogManager$LoggerWeakRef
 java.util.logging.LogManager$RootLogger
 java.util.logging.LogManager$SystemLoggerContext
-java.util.logging.LogRecord
 java.util.logging.Logger
 java.util.logging.LoggingPermission
 java.util.logging.LoggingProxyImpl
+java.util.prefs.AbstractPreferences
+java.util.prefs.Preferences
 java.util.regex.MatchResult
 java.util.regex.Matcher
 java.util.regex.Pattern
 java.util.regex.PatternSyntaxException
 java.util.spi.LocaleServiceProvider
+java.util.stream.BaseStream
+java.util.stream.Stream
+java.util.stream.StreamSupport
 java.util.zip.Adler32
 java.util.zip.CRC32
 java.util.zip.CheckedInputStream
@@ -3748,7 +3785,6 @@
 javax.crypto.CipherSpi
 javax.crypto.IllegalBlockSizeException
 javax.crypto.JceSecurity
-javax.crypto.Mac
 javax.crypto.NoSuchPaddingException
 javax.crypto.NullCipher
 javax.crypto.SecretKey
@@ -3757,10 +3793,6 @@
 javax.crypto.spec.SecretKeySpec
 javax.microedition.khronos.egl.EGL
 javax.microedition.khronos.egl.EGL10
-javax.microedition.khronos.egl.EGLConfig
-javax.microedition.khronos.egl.EGLContext
-javax.microedition.khronos.egl.EGLDisplay
-javax.microedition.khronos.egl.EGLSurface
 javax.microedition.khronos.opengles.GL
 javax.microedition.khronos.opengles.GL10
 javax.microedition.khronos.opengles.GL10Ext
@@ -3771,7 +3803,6 @@
 javax.net.ServerSocketFactory
 javax.net.SocketFactory
 javax.net.ssl.ExtendedSSLSession
-javax.net.ssl.HandshakeCompletedEvent
 javax.net.ssl.HandshakeCompletedListener
 javax.net.ssl.HostnameVerifier
 javax.net.ssl.HttpsURLConnection
@@ -3779,23 +3810,20 @@
 javax.net.ssl.KeyManagerFactory
 javax.net.ssl.KeyManagerFactory$1
 javax.net.ssl.KeyManagerFactorySpi
+javax.net.ssl.SNIHostName
+javax.net.ssl.SNIServerName
 javax.net.ssl.SSLContext
 javax.net.ssl.SSLContextSpi
 javax.net.ssl.SSLEngine
 javax.net.ssl.SSLException
-javax.net.ssl.SSLHandshakeException
 javax.net.ssl.SSLParameters
 javax.net.ssl.SSLPeerUnverifiedException
 javax.net.ssl.SSLProtocolException
-javax.net.ssl.SSLServerSocket
 javax.net.ssl.SSLServerSocketFactory
 javax.net.ssl.SSLSession
-javax.net.ssl.SSLSessionBindingEvent
-javax.net.ssl.SSLSessionBindingListener
 javax.net.ssl.SSLSessionContext
 javax.net.ssl.SSLSocket
 javax.net.ssl.SSLSocketFactory
-javax.net.ssl.SSLSocketFactory$1
 javax.net.ssl.TrustManager
 javax.net.ssl.TrustManagerFactory
 javax.net.ssl.TrustManagerFactory$1
@@ -3841,7 +3869,6 @@
 libcore.io.NioBufferIterator
 libcore.io.Os
 libcore.io.Posix
-libcore.math.MathUtils
 libcore.net.NetworkSecurityPolicy
 libcore.net.NetworkSecurityPolicy$DefaultNetworkSecurityPolicy
 libcore.net.UriCodec
@@ -3857,7 +3884,6 @@
 libcore.reflect.ListOfTypes
 libcore.reflect.ListOfVariables
 libcore.reflect.ParameterizedTypeImpl
-libcore.reflect.TypeVariableImpl
 libcore.reflect.Types
 libcore.util.BasicLruCache
 libcore.util.CharsetUtils
@@ -3866,7 +3892,6 @@
 libcore.util.NativeAllocationRegistry
 libcore.util.NativeAllocationRegistry$CleanerRunner
 libcore.util.NativeAllocationRegistry$CleanerThunk
-libcore.util.Objects
 libcore.util.ZoneInfo
 libcore.util.ZoneInfo$CheckedArithmeticException
 libcore.util.ZoneInfo$WallTime
@@ -3892,11 +3917,8 @@
 org.apache.http.HttpResponse
 org.apache.http.HttpVersion
 org.apache.http.NameValuePair
-org.apache.http.ProtocolException
 org.apache.http.ProtocolVersion
-org.apache.http.ReasonPhraseCatalog
 org.apache.http.StatusLine
-org.apache.http.client.ClientProtocolException
 org.apache.http.client.HttpClient
 org.apache.http.client.ResponseHandler
 org.apache.http.client.methods.AbortableHttpRequest
@@ -3905,26 +3927,15 @@
 org.apache.http.client.methods.HttpPost
 org.apache.http.client.methods.HttpRequestBase
 org.apache.http.client.methods.HttpUriRequest
-org.apache.http.client.utils.URLEncodedUtils
 org.apache.http.conn.ClientConnectionManager
 org.apache.http.conn.ConnectTimeoutException
-org.apache.http.conn.params.ConnManagerPNames
-org.apache.http.conn.params.ConnManagerParams
-org.apache.http.conn.params.ConnManagerParams$1
-org.apache.http.conn.params.ConnPerRoute
-org.apache.http.conn.scheme.LayeredSocketFactory
-org.apache.http.conn.scheme.SocketFactory
 org.apache.http.entity.AbstractHttpEntity
-org.apache.http.entity.BasicHttpEntity
 org.apache.http.entity.ByteArrayEntity
-org.apache.http.impl.client.EntityEnclosingRequestWrapper
-org.apache.http.impl.client.RequestWrapper
 org.apache.http.impl.cookie.DateParseException
 org.apache.http.impl.cookie.DateUtils
 org.apache.http.message.AbstractHttpMessage
 org.apache.http.message.BasicHeader
 org.apache.http.message.BasicHttpResponse
-org.apache.http.message.BasicNameValuePair
 org.apache.http.message.BasicStatusLine
 org.apache.http.message.HeaderGroup
 org.apache.http.params.AbstractHttpParams
@@ -3933,6 +3944,18 @@
 org.apache.http.params.HttpConnectionParams
 org.apache.http.params.HttpParams
 org.apache.http.protocol.HttpContext
+org.ccil.cowan.tagsoup.AttributesImpl
+org.ccil.cowan.tagsoup.AutoDetector
+org.ccil.cowan.tagsoup.Element
+org.ccil.cowan.tagsoup.ElementType
+org.ccil.cowan.tagsoup.HTMLModels
+org.ccil.cowan.tagsoup.HTMLScanner
+org.ccil.cowan.tagsoup.HTMLSchema
+org.ccil.cowan.tagsoup.Parser
+org.ccil.cowan.tagsoup.Parser$1
+org.ccil.cowan.tagsoup.ScanHandler
+org.ccil.cowan.tagsoup.Scanner
+org.ccil.cowan.tagsoup.Schema
 org.json.JSON
 org.json.JSONArray
 org.json.JSONException
@@ -3954,6 +3977,7 @@
 org.xml.sax.SAXNotRecognizedException
 org.xml.sax.SAXNotSupportedException
 org.xml.sax.XMLReader
+org.xml.sax.ext.LexicalHandler
 org.xml.sax.helpers.DefaultHandler
 org.xmlpull.v1.XmlPullParser
 org.xmlpull.v1.XmlPullParserException
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index ea3cffe..afea7f3 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2138,6 +2138,20 @@
     // Settings > Apps > Gear > Special Access > Premium SMS access
     PREMIUM_SMS_ACCESS = 388;
 
+    // Logged when the user resizes the docked stack. Arguments:
+    // 0: Split 50:50
+    // 1: Docked smaller
+    // 2: Docked larger
+    ACTION_WINDOW_DOCK_RESIZE = 389;
+
+    // User exits split-screen by dragging the divider to the side of the screen. Arguments
+    // 0: Docked gets maximized
+    // 1: Fullscreen gets maximized
+    ACTION_WINDOW_UNDOCK_MAX = 390;
+
+    // User tried to dock an unresizable app.
+    ACTION_WINDOW_DOCK_UNRESIZABLE = 391;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 9ec6e8d..04ea8e5 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -58,6 +58,7 @@
     private static final int MAX_NUMBER_IO_INPUT_ALLOC = 16;
 
     Type mType;
+    boolean mOwningType = false;
     Bitmap mBitmap;
     int mUsage;
     Allocation mAdaptedAllocation;
@@ -383,13 +384,16 @@
         guard.open("destroy");
     }
 
-    Allocation(long id, RenderScript rs, Type t, int usage, MipmapControl mips) {
+    Allocation(long id, RenderScript rs, Type t, boolean owningType, int usage, MipmapControl mips) {
         this(id, rs, t, usage);
+        mOwningType = owningType;
         mMipmapControl = mips;
     }
 
     protected void finalize() throws Throwable {
         RenderScript.registerNativeFree.invoke(RenderScript.sRuntime, mSize);
+        // Set mType null to avoid double-destroying it in case its finalizer races ahead
+        mType = null;
         super.finalize();
     }
 
@@ -1578,6 +1582,9 @@
         mRS.finish();  // Necessary because resize is fifoed and update is async.
 
         long typeID = mRS.nAllocationGetType(getID(mRS));
+        // Sets zero the mID so that the finalizer of the old mType value won't
+        // destroy the native object that is being reused.
+        mType.setID(0);
         mType = new Type(typeID, mRS);
         mType.updateFromNative();
         updateCacheInfo(mType);
@@ -1921,7 +1928,7 @@
             if (id == 0) {
                 throw new RSRuntimeException("Allocation creation failed.");
             }
-            return new Allocation(id, rs, type, usage, mips);
+            return new Allocation(id, rs, type, false, usage, mips);
         } finally {
             Trace.traceEnd(RenderScript.TRACE_TAG);
         }
@@ -1979,7 +1986,7 @@
             if (id == 0) {
                 throw new RSRuntimeException("Allocation creation failed.");
             }
-            return new Allocation(id, rs, t, usage, MipmapControl.MIPMAP_NONE);
+            return new Allocation(id, rs, t, true, usage, MipmapControl.MIPMAP_NONE);
         } finally {
             Trace.traceEnd(RenderScript.TRACE_TAG);
         }
@@ -2068,7 +2075,7 @@
                 }
 
                 // keep a reference to the Bitmap around to prevent GC
-                Allocation alloc = new Allocation(id, rs, t, usage, mips);
+                Allocation alloc = new Allocation(id, rs, t, true, usage, mips);
                 alloc.setBitmap(b);
                 return alloc;
             }
@@ -2078,7 +2085,7 @@
             if (id == 0) {
                 throw new RSRuntimeException("Load failed.");
             }
-            return new Allocation(id, rs, t, usage, mips);
+            return new Allocation(id, rs, t, true, usage, mips);
         } finally {
             Trace.traceEnd(RenderScript.TRACE_TAG);
         }
@@ -2150,7 +2157,7 @@
             }
 
             for (int i=1; i<numAlloc; i++) {
-                mAllocationArray[i] = createFromAllcation(rs, mAllocationArray[0]);
+                mAllocationArray[i] = createFromAllocation(rs, mAllocationArray[0]);
             }
             return mAllocationArray;
         } finally {
@@ -2169,7 +2176,7 @@
      * @param alloc RenderScript Allocation describing data layout.
      * @return Allocation sharing the same data structure.
      */
-    static Allocation createFromAllcation(RenderScript rs, Allocation alloc) {
+    static Allocation createFromAllocation(RenderScript rs, Allocation alloc) {
         try {
             Trace.traceBegin(RenderScript.TRACE_TAG, "createFromAllcation");
             rs.validate();
@@ -2184,7 +2191,7 @@
             if (id == 0) {
                 throw new RSRuntimeException("Allocation creation failed.");
             }
-            Allocation outAlloc = new Allocation(id, rs, type, usage, mips);
+            Allocation outAlloc = new Allocation(id, rs, type, false, usage, mips);
             if ((usage & USAGE_IO_INPUT) != 0) {
                 outAlloc.shareBufferQueue(alloc);
             }
@@ -2360,7 +2367,7 @@
         if(id == 0) {
             throw new RSRuntimeException("Load failed for bitmap " + b + " element " + e);
         }
-        return new Allocation(id, rs, t, usage, mips);
+        return new Allocation(id, rs, t, true, usage, mips);
     }
 
     /**
@@ -2605,6 +2612,13 @@
         if((mUsage & USAGE_IO_OUTPUT) != 0) {
             setSurface(null);
         }
+
+        if (mType != null && mOwningType) {
+            mType.destroy();
+            mType = null;
+        }
+
         super.destroy();
     }
+
 }
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 219f16b..35ae8b4 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -187,6 +187,23 @@
             guard.open("destroy");
         }
 
+        /**
+         * Destroys this Closure and the Allocation for its return value
+         */
+        public void destroy() {
+            super.destroy();
+            if (mReturnValue != null) {
+                mReturnValue.destroy();
+            }
+        }
+
+        protected void finalize() throws Throwable {
+            // Set null mReturnValue to avoid double-destroying it, in case its
+            // finalizer races ahead.
+            mReturnValue = null;
+            super.finalize();
+        }
+
         private void retrieveValueAndDependenceInfo(RenderScript rs,
                                                     int index, Script.FieldID fid, Object obj,
                                                     long[] values, int[] sizes,
@@ -1015,6 +1032,8 @@
                 throw new RSIllegalArgumentException("invalid script group name");
             }
             ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
+            mClosures = new ArrayList<Closure>();
+            mInputs = new ArrayList<Input>();
             return ret;
         }
 
@@ -1042,4 +1061,20 @@
 
     }
 
+    /**
+     * Destroy this ScriptGroup and all Closures in it
+     */
+    public void destroy() {
+        super.destroy();
+        for(Closure c : mClosures) {
+            c.destroy();
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        // Clear out the list mClosures to avoid double-destroying the closures,
+        // in case their finalizers race ahead.
+        mClosures.clear();
+        super.finalize();
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 613f890..4d7f82d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -70,12 +70,22 @@
     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
 
     /**
-     * Flag for enabling motion event injectsion
+     * Flag for enabling motion event injection.
      *
      * @see #setUserAndEnabledFeatures(int, int)
      */
     static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
 
+    /**
+     * Flag for enabling the feature to control the screen magnifier. If
+     * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
+     * as the screen magnifier feature performs a super set of the work
+     * performed by this feature.
+     *
+     * @see #setUserAndEnabledFeatures(int, int)
+     */
+    static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020;
+
     private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
         @Override
         public void run() {
@@ -373,8 +383,12 @@
             addFirstEventHandler(mTouchExplorer);
         }
 
-        if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
-            mMagnificationGestureHandler = new MagnificationGestureHandler(mContext, mAms);
+        if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+                || (mEnabledFeatures  & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
+            final boolean detectControlGestures = (mEnabledFeatures
+                    & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
+            mMagnificationGestureHandler = new MagnificationGestureHandler(
+                    mContext, mAms, detectControlGestures);
             addFirstEventHandler(mMagnificationGestureHandler);
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ca17c43..d900b37 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1311,6 +1311,9 @@
             if (userState.mIsDisplayMagnificationEnabled) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
             }
+            if (userHasMagnificationServicesLocked(userState)) {
+                flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
+            }
             // Touch exploration without accessibility makes no sense.
             if (userState.isHandlingAccessibilityEvents()
                     && userState.mIsTouchExplorationEnabled) {
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index fb1ef37..b2196bf 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -156,10 +156,10 @@
             final float offsetY = sentSpec.offsetY;
 
             // Compute the new center and update spec as needed.
-            final float centerX = (mMagnifiedBounds.width() / 2.0f - offsetX) / scale
-                    + mMagnifiedBounds.left;
-            final float centerY = (mMagnifiedBounds.height() / 2.0f - offsetY) / scale
-                    + mMagnifiedBounds.top;
+            final float centerX = (mMagnifiedBounds.width() / 2.0f
+                    + mMagnifiedBounds.left - offsetX) / scale;
+            final float centerY = (mMagnifiedBounds.height() / 2.0f
+                    + mMagnifiedBounds.top - offsetY) / scale;
             if (updateSpec) {
                 setScaleAndCenter(scale, centerX, centerY, false);
             } else {
@@ -256,7 +256,7 @@
     public float getCenterX() {
         synchronized (mLock) {
             return  (mMagnifiedBounds.width() / 2.0f
-                   - getOffsetX()) / getScale() + mMagnifiedBounds.left;
+                    + mMagnifiedBounds.left - getOffsetX()) / getScale();
         }
     }
 
@@ -279,7 +279,7 @@
     public float getCenterY() {
         synchronized (mLock) {
             return (mMagnifiedBounds.height() / 2.0f
-                    - getOffsetY()) / getScale() + mMagnifiedBounds.top;
+                    + mMagnifiedBounds.top - getOffsetY()) / getScale();
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 51c8ab5..818ac81 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -80,7 +80,6 @@
     private static final boolean DEBUG_STATE_TRANSITIONS = false;
     private static final boolean DEBUG_DETECTING = false;
     private static final boolean DEBUG_PANNING = false;
-    private static final boolean DEBUG_SCALING = false;
 
     private static final int STATE_DELEGATING = 1;
     private static final int STATE_DETECTING = 2;
@@ -95,6 +94,9 @@
     private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler;
     private final StateViewportDraggingHandler mStateViewportDraggingHandler;
 
+
+    private final boolean mDetectControlGestures;
+
     private EventStreamTransformation mNext;
 
     private int mCurrentState;
@@ -107,12 +109,14 @@
 
     private long mDelegatingStateDownTime;
 
-    public MagnificationGestureHandler(Context context, AccessibilityManagerService ams) {
+    public MagnificationGestureHandler(Context context, AccessibilityManagerService ams,
+            boolean detectControlGestures) {
         mMagnificationController = ams.getMagnificationController();
         mDetectingStateHandler = new DetectingStateHandler(context);
         mStateViewportDraggingHandler = new StateViewportDraggingHandler();
         mMagnifiedContentInteractionStateHandler =
                 new MagnifiedContentInteractionStateHandler(context);
+        mDetectControlGestures = detectControlGestures;
 
         transitionToState(STATE_DETECTING);
     }
@@ -125,6 +129,12 @@
             }
             return;
         }
+        if (!mDetectControlGestures) {
+            if (mNext != null) {
+                dispatchTransformedEvent(event, rawEvent, policyFlags);
+            }
+            return;
+        }
         mMagnifiedContentInteractionStateHandler.onMotionEvent(event, rawEvent, policyFlags);
         switch (mCurrentState) {
             case STATE_DELEGATING: {
@@ -140,7 +150,7 @@
             }
             break;
             case STATE_MAGNIFIED_INTERACTION: {
-                // mMagnifiedContentInteractonStateHandler handles events only
+                // mMagnifiedContentInteractionStateHandler handles events only
                 // if this is the current state since it uses ScaleGestureDetecotr
                 // and a GestureDetector which need well formed event stream.
             }
@@ -208,31 +218,6 @@
             break;
         }
         if (mNext != null) {
-            // If the event is within the magnified portion of the screen we have
-            // to change its location to be where the user thinks he is poking the
-            // UI which may have been magnified and panned.
-            final float eventX = event.getX();
-            final float eventY = event.getY();
-            if (mMagnificationController.isMagnifying()
-                    && mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
-                final float scale = mMagnificationController.getScale();
-                final float scaledOffsetX = mMagnificationController.getOffsetX();
-                final float scaledOffsetY = mMagnificationController.getOffsetY();
-                final int pointerCount = event.getPointerCount();
-                PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
-                PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
-                        pointerCount);
-                for (int i = 0; i < pointerCount; i++) {
-                    event.getPointerCoords(i, coords[i]);
-                    coords[i].x = (coords[i].x - scaledOffsetX) / scale;
-                    coords[i].y = (coords[i].y - scaledOffsetY) / scale;
-                    event.getPointerProperties(i, properties[i]);
-                }
-                event = MotionEvent.obtain(event.getDownTime(),
-                        event.getEventTime(), event.getAction(), pointerCount, properties,
-                        coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
-                        event.getFlags());
-            }
             // We cache some events to see if the user wants to trigger magnification.
             // If no magnification is triggered we inject these events with adjusted
             // time and down time to prevent subsequent transformations being confused
@@ -240,10 +225,40 @@
             // injected we need to also update the down time of all subsequent non cached
             // events. All delegated events cached and non-cached are delivered here.
             event.setDownTime(mDelegatingStateDownTime);
-            mNext.onMotionEvent(event, rawEvent, policyFlags);
+            dispatchTransformedEvent(event, rawEvent, policyFlags);
         }
     }
 
+    private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
+            int policyFlags) {
+        // If the event is within the magnified portion of the screen we have
+        // to change its location to be where the user thinks he is poking the
+        // UI which may have been magnified and panned.
+        final float eventX = event.getX();
+        final float eventY = event.getY();
+        if (mMagnificationController.isMagnifying()
+                && mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
+            final float scale = mMagnificationController.getScale();
+            final float scaledOffsetX = mMagnificationController.getOffsetX();
+            final float scaledOffsetY = mMagnificationController.getOffsetY();
+            final int pointerCount = event.getPointerCount();
+            PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
+            PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
+                    pointerCount);
+            for (int i = 0; i < pointerCount; i++) {
+                event.getPointerCoords(i, coords[i]);
+                coords[i].x = (coords[i].x - scaledOffsetX) / scale;
+                coords[i].y = (coords[i].y - scaledOffsetY) / scale;
+                event.getPointerProperties(i, properties[i]);
+            }
+            event = MotionEvent.obtain(event.getDownTime(),
+                    event.getEventTime(), event.getAction(), pointerCount, properties,
+                    coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
+                    event.getFlags());
+        }
+        mNext.onMotionEvent(event, rawEvent, policyFlags);
+    }
+
     private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
         final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0;
         if (oldSize < size) {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6ca3af8..0428ecf 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -613,23 +613,22 @@
                             | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 }
             }
+            for (int j = 0; j < widgetCount; j++) {
+                Widget widget = provider.widgets.get(j);
+                if (targetWidget != null && targetWidget != widget) continue;
+                PendingIntent intent = null;
+                if (onClickIntent != null) {
+                    intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
+                            onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+                }
+                RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
+                if (widget.replaceWithMaskedViewsLocked(views)) {
+                    scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
+                }
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
-
-        for (int j = 0; j < widgetCount; j++) {
-            Widget widget = provider.widgets.get(j);
-            if (targetWidget != null && targetWidget != widget) continue;
-            PendingIntent intent = null;
-            if (onClickIntent != null) {
-                intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
-                        onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            }
-            RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
-            if (widget.replaceWithMaskedViewsLocked(views)) {
-                scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
-            }
-        }
     }
 
     private void unmaskWidgetsViewsLocked(Provider provider) {
@@ -1062,8 +1061,6 @@
             widget.provider = provider;
             widget.options = (options != null) ? cloneIfLocalBinder(options) : new Bundle();
 
-            onWidgetProviderAddedOrChangedLocked(widget);
-
             // We need to provide a default value for the widget category if it is not specified
             if (!widget.options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
                 widget.options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
@@ -1072,6 +1069,8 @@
 
             provider.widgets.add(widget);
 
+            onWidgetProviderAddedOrChangedLocked(widget);
+
             final int widgetCount = provider.widgets.size();
             if (widgetCount == 1) {
                 // Tell the provider that it's ready.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 428e192..2ccc3fe 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2025,11 +2025,15 @@
                 default:
                     return false;
                 case NetworkMonitor.EVENT_NETWORK_TESTED: {
-                    NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    if (isLiveNetworkAgent(nai, msg.what)) {
+                    final NetworkAgentInfo nai;
+                    synchronized (mNetworkForNetId) {
+                        nai = mNetworkForNetId.get(msg.arg2);
+                    }
+                    if (nai != null) {
                         final boolean valid =
                                 (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
-                        if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed"));
+                        if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
+                                (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
                         if (valid != nai.lastValidated) {
                             final int oldScore = nai.getCurrentScore();
                             nai.lastValidated = valid;
@@ -2040,10 +2044,12 @@
                         }
                         updateInetCondition(nai);
                         // Let the NetworkAgent know the state of its network
+                        Bundle redirectUrlBundle = new Bundle();
+                        redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj);
                         nai.asyncChannel.sendMessage(
-                                android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+                                NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                                 (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
-                                0, null);
+                                0, redirectUrlBundle);
                     }
                     break;
                 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 22cc066..544e645 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2966,16 +2966,6 @@
                 return;
             }
             setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
-            if (mSubtypeSwitchedByShortCutToast != null) {
-                mSubtypeSwitchedByShortCutToast.cancel();
-                mSubtypeSwitchedByShortCutToast = null;
-            }
-            if ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
-                // IME window is shown.  The user should be able to visually understand that the
-                // subtype is changed in most of cases.  To avoid UI overlap, we do not show a toast
-                // in this case.
-                return;
-            }
             final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId);
             if (newInputMethodInfo == null) {
                 return;
@@ -2983,8 +2973,12 @@
             final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext,
                     newInputMethodInfo, mCurrentSubtype);
             if (!TextUtils.isEmpty(toastText)) {
-                mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText.toString(),
-                        Toast.LENGTH_SHORT);
+                if (mSubtypeSwitchedByShortCutToast == null) {
+                    mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText,
+                            Toast.LENGTH_SHORT);
+                } else {
+                    mSubtypeSwitchedByShortCutToast.setText(toastText);
+                }
                 mSubtypeSwitchedByShortCutToast.show();
             }
         }
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index d136f1a..9ab6300 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -401,7 +401,8 @@
         return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE);
     }
 
-    private String getChildProfileLockFile(int userId) {
+    @VisibleForTesting
+    String getChildProfileLockFile(int userId) {
         return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
     }
 
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 7db9be2..6009984 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -243,7 +243,7 @@
                             log("Problem parsing message " + e);
                         } finally {
                             if (releaseWl) {
-                                mWakeLock.acquire();
+                                mWakeLock.release();
                             }
                         }
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index bf4df94..fffd850 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -174,6 +174,16 @@
         public static final int StrictCleartext           = 617;
     }
 
+    /**
+     * String indicating a softap command.
+     */
+    static final String SOFT_AP_COMMAND = "softap";
+
+    /**
+     * String passed back to netd connector indicating softap command success.
+     */
+    static final String SOFT_AP_COMMAND_SUCCESS = "Ok";
+
     static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
 
     /**
@@ -1426,20 +1436,48 @@
         }
     }
 
+    /**
+     * Private method used to call execute for a command given the provided arguments.
+     *
+     * This function checks the returned NativeDaemonEvent for the provided expected response code
+     * and message.  If either of these is not correct, an error is logged.
+     *
+     * @param String command The command to execute.
+     * @param Object[] args If needed, arguments for the command to execute.
+     * @param int expectedResponseCode The code expected to be returned in the corresponding event.
+     * @param String expectedResponseMessage The message expected in the returned event.
+     * @param String logMsg The message to log as an error (TAG will be applied).
+     */
+    private void executeOrLogWithMessage(String command, Object[] args,
+            int expectedResponseCode, String expectedResponseMessage, String logMsg)
+            throws NativeDaemonConnectorException {
+        NativeDaemonEvent event = mConnector.execute(command, args);
+        if (event.getCode() != expectedResponseCode
+                || !event.getMessage().equals(expectedResponseMessage)) {
+            Log.e(TAG, logMsg + ": event = " + event);
+        }
+    }
+
     @Override
-    public void startAccessPoint(
-            WifiConfiguration wifiConfig, String wlanIface) {
+    public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Object[] args;
+        String logMsg = "startAccessPoint Error setting up softap";
         try {
             if (wifiConfig == null) {
-                mConnector.execute("softap", "set", wlanIface);
+                args = new Object[] {"set", wlanIface};
             } else {
-                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
-                                   "broadcast", Integer.toString(wifiConfig.apChannel),
-                                   getSecurityType(wifiConfig),
-                                   new SensitiveArg(wifiConfig.preSharedKey));
+                args = new Object[] {"set", wlanIface, wifiConfig.SSID,
+                        "broadcast", Integer.toString(wifiConfig.apChannel),
+                        getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey)};
             }
-            mConnector.execute("softap", "startap");
+            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
+                    SOFT_AP_COMMAND_SUCCESS, logMsg);
+
+            logMsg = "startAccessPoint Error starting softap";
+            args = new Object[] {"startap"};
+            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
+                    SOFT_AP_COMMAND_SUCCESS, logMsg);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1460,8 +1498,12 @@
     @Override
     public void wifiFirmwareReload(String wlanIface, String mode) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Object[] args = {"fwreload", wlanIface, mode};
+        String logMsg = "wifiFirmwareReload Error reloading "
+                + wlanIface + " fw in " + mode + " mode";
         try {
-            mConnector.execute("softap", "fwreload", wlanIface, mode);
+            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
+                    SOFT_AP_COMMAND_SUCCESS, logMsg);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1470,8 +1512,12 @@
     @Override
     public void stopAccessPoint(String wlanIface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Object[] args = {"stopap"};
+        String logMsg = "stopAccessPoint Error stopping softap";
+
         try {
-            mConnector.execute("softap", "stopap");
+            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
+                    SOFT_AP_COMMAND_SUCCESS, logMsg);
             wifiFirmwareReload(wlanIface, "STA");
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
@@ -1481,14 +1527,21 @@
     @Override
     public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Object[] args;
+        String logMsg = "startAccessPoint Error setting up softap";
         try {
             if (wifiConfig == null) {
-                mConnector.execute("softap", "set", wlanIface);
+                args = new Object[] {"set", wlanIface};
             } else {
-                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
-                                   "broadcast", "6", getSecurityType(wifiConfig),
-                                   new SensitiveArg(wifiConfig.preSharedKey));
+                // TODO: understand why this is set to "6" instead of
+                // Integer.toString(wifiConfig.apChannel) as in startAccessPoint
+                // TODO: should startAccessPoint call this instead of repeating code?
+                args = new Object[] {"set", wlanIface, wifiConfig.SSID,
+                        "broadcast", "6",
+                        getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey)};
             }
+            executeOrLogWithMessage(SOFT_AP_COMMAND, args, NetdResponseCode.SoftapStatusResult,
+                    SOFT_AP_COMMAND_SUCCESS, logMsg);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 05e4245..4791818 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -91,6 +91,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
 
@@ -127,9 +129,34 @@
 public class AccountManagerService
         extends IAccountManager.Stub
         implements RegisteredServicesCacheListener<AuthenticatorDescription> {
-
     private static final String TAG = "AccountManagerService";
 
+    public static class Lifecycle extends SystemService {
+        private AccountManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new AccountManagerService(getContext());
+            publishBinderService(Context.ACCOUNT_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mService.systemReady();
+            }
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mService.onUnlockUser(userHandle);
+        }
+    }
+
     private static final String DATABASE_NAME = "accounts.db";
     private static final int PRE_N_DATABASE_VERSION = 9;
     private static final int CE_DATABASE_VERSION = 10;
@@ -340,15 +367,12 @@
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
                     onUserRemoved(intent);
-                } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                    onUserUnlocked(intent);
                 }
             }
         }, UserHandle.ALL, userFilter, null, null);
@@ -654,7 +678,10 @@
 
     @VisibleForTesting
     void onUserUnlocked(Intent intent) {
-        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+        onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+    }
+
+    void onUnlockUser(int userId) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "onUserUnlocked " + userId);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3ec51e3..4c75f50 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8792,6 +8792,7 @@
             rti.bounds = new Rect(tr.mBounds);
         }
         rti.isDockable = tr.canGoInDockedStack();
+        rti.resizeMode = tr.mResizeMode;
 
         ActivityRecord base = null;
         ActivityRecord top = null;
@@ -9424,7 +9425,8 @@
             if (prev != null && prev.isRecentsActivity()) {
                 task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
             }
-            mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront");
+            mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront",
+                    false /* forceNonResizable */);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2e9947a..837a1c1 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4776,6 +4776,7 @@
             ci.numActivities = numActivities;
             ci.numRunning = numRunning;
             ci.isDockable = task.canGoInDockedStack();
+            ci.resizeMode = task.mResizeMode;
             list.add(ci);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d34e8fc..598d9ff 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1759,8 +1759,8 @@
         }
     }
 
-    void findTaskToMoveToFrontLocked(
-            TaskRecord task, int flags, ActivityOptions options, String reason) {
+    void findTaskToMoveToFrontLocked(TaskRecord task, int flags, ActivityOptions options,
+            String reason, boolean forceNonResizeable) {
         if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
             mUserLeaving = true;
         }
@@ -1811,7 +1811,8 @@
         if (DEBUG_STACK) Slog.d(TAG_STACK,
                 "findTaskToMoveToFront: moved to front of stack=" + task.stack);
 
-        handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
+        handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId,
+                forceNonResizeable);
     }
 
     boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -3360,19 +3361,25 @@
         }
     }
 
+    void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, int actualStackId) {
+        handleNonResizableTaskIfNeeded(task, preferredStackId, actualStackId,
+                false /* forceNonResizable */);
+    }
+
     void handleNonResizableTaskIfNeeded(
-            TaskRecord task, int preferredStackId, int actualStackId) {
+            TaskRecord task, int preferredStackId, int actualStackId, boolean forceNonResizable) {
         if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
                 || task.isHomeTask()) {
             return;
         }
 
-        if (!task.canGoInDockedStack()) {
+        if (!task.canGoInDockedStack() || forceNonResizable) {
             // Display a warning toast that we tried to put a non-dockable task in the docked stack.
             mService.mHandler.sendEmptyMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
 
-            // Dismiss docked stack.
-            mService.moveTasksToFullscreenStack(DOCKED_STACK_ID, false);
+            // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
+            // we need to move it to top of fullscreen stack, otherwise it will be covered.
+            mService.moveTasksToFullscreenStack(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
         } else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
             String packageName = task.getTopActivity() != null
                     ? task.getTopActivity().appInfo.packageName : null;
@@ -3443,8 +3450,12 @@
         }
 
         if (andResume) {
-            findTaskToMoveToFrontLocked(task, 0, null, reason);
+            findTaskToMoveToFrontLocked(task, 0, null, reason,
+                    lockTaskModeState != LOCK_TASK_MODE_NONE);
             resumeFocusedStackTopActivityLocked();
+        } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
+            handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId,
+                    true /* forceNonResizable */);
         }
     }
 
@@ -4248,6 +4259,7 @@
         // Work Challenge is present) let startActivityInPackage handle the intercepting.
         if (!mService.mUserController.shouldConfirmCredentials(task.userId)
                 && task.getRootActivity() != null) {
+            mActivityMetricsLogger.notifyActivityLaunching();
             mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
 
             // If we are launching the task in the docked stack, put it into resizing mode so
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bbb162e..d330756 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -120,8 +120,9 @@
 
     /**
      * Inform ConnectivityService that the network has been tested.
-     * obj = NetworkAgentInfo
+     * obj = String representing URL that Internet probe was redirect to, if it was redirected.
      * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
+     * arg2 = NetID.
      */
     public static final int EVENT_NETWORK_TESTED = BASE + 2;
 
@@ -334,8 +335,8 @@
                             mDontDisplaySigninNotification = true;
                             mUserDoesNotWant = true;
                             mConnectivityServiceHandler.sendMessage(obtainMessage(
-                                    EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
-                                    mNetworkAgentInfo));
+                                    EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
+                                    mNetworkAgentInfo.network.netId, null));
                             // TODO: Should teardown network.
                             mUidResponsibleForReeval = 0;
                             transitionTo(mEvaluatingState);
@@ -358,7 +359,7 @@
             CaptivePortalStateChangeEvent.logEvent(
                    CaptivePortalStateChangeEvent.NETWORK_MONITOR_VALIDATED);
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
-                    NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));
+                    NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
         }
 
         @Override
@@ -412,6 +413,21 @@
         }
     }
 
+    /**
+     * Result of calling isCaptivePortal().
+     * @hide
+     */
+    @VisibleForTesting
+    public static final class CaptivePortalProbeResult {
+        final int mHttpResponseCode; // HTTP response code returned from Internet probe.
+        final String mRedirectUrl;   // Redirect destination returned from Internet probe.
+
+        public CaptivePortalProbeResult(int httpResponseCode, String redirectUrl) {
+            mHttpResponseCode = httpResponseCode;
+            mRedirectUrl = redirectUrl;
+        }
+    }
+
     // Being in the EvaluatingState State indicates the Network is being evaluated for internet
     // connectivity, or that the user has indicated that this network is unwanted.
     private class EvaluatingState extends State {
@@ -464,19 +480,23 @@
                     // IPv6) could each take SOCKET_TIMEOUT_MS.  During this time this StateMachine
                     // will be unresponsive. isCaptivePortal() could be executed on another Thread
                     // if this is found to cause problems.
-                    int httpResponseCode = isCaptivePortal();
+                    CaptivePortalProbeResult probeResult = isCaptivePortal();
                     CaptivePortalCheckResultEvent.logEvent(mNetworkAgentInfo.network.netId,
-                            httpResponseCode);
-                    if (httpResponseCode == 204) {
+                            probeResult.mHttpResponseCode);
+                    if (probeResult.mHttpResponseCode == 204) {
                         transitionTo(mValidatedState);
-                    } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
+                    } else if (probeResult.mHttpResponseCode >= 200 &&
+                            probeResult.mHttpResponseCode <= 399) {
+                        mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
+                                NETWORK_TEST_RESULT_INVALID, mNetworkAgentInfo.network.netId,
+                                probeResult.mRedirectUrl));
                         transitionTo(mCaptivePortalState);
                     } else {
                         final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
                         sendMessageDelayed(msg, mReevaluateDelayMs);
                         mConnectivityServiceHandler.sendMessage(obtainMessage(
-                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0,
-                                mNetworkAgentInfo));
+                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
+                                mNetworkAgentInfo.network.netId, probeResult.mRedirectUrl));
                         if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
                             // Don't continue to blame UID forever.
                             TrafficStats.clearThreadStatsUid();
@@ -533,8 +553,6 @@
 
         @Override
         public void enter() {
-            mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
-                    NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
             // Don't annoy user with sign-in notifications.
             if (mDontDisplaySigninNotification) return;
             // Create a CustomIntentReceiver that sends us a
@@ -639,11 +657,12 @@
      * Returns HTTP response code.
      */
     @VisibleForTesting
-    protected int isCaptivePortal() {
-        if (!mIsCaptivePortalCheckEnabled) return 204;
+    protected CaptivePortalProbeResult isCaptivePortal() {
+        if (!mIsCaptivePortalCheckEnabled) return new CaptivePortalProbeResult(204, null);
 
         HttpURLConnection urlConnection = null;
         int httpResponseCode = 599;
+        String redirectUrl = null;
         try {
             URL url = new URL(getCaptivePortalServerUrl(mContext));
             // On networks with a PAC instead of fetching a URL that should result in a 204
@@ -699,6 +718,7 @@
             long requestTimestamp = SystemClock.elapsedRealtime();
 
             httpResponseCode = urlConnection.getResponseCode();
+            redirectUrl = urlConnection.getHeaderField("location");
 
             // Time how long it takes to get a response to our request
             long responseTimestamp = SystemClock.elapsedRealtime();
@@ -739,7 +759,7 @@
                 urlConnection.disconnect();
             }
         }
-        return httpResponseCode;
+        return new CaptivePortalProbeResult(httpResponseCode, redirectUrl);
     }
 
     /**
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 6e7ea99..03eb019 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -77,7 +77,7 @@
     static final boolean DEBUG = false;
 
     public static class Lifecycle extends SystemService {
-        private ContentService mContentService;
+        private ContentService mService;
 
         public Lifecycle(Context context) {
             super(context);
@@ -87,14 +87,21 @@
         public void onStart() {
             final boolean factoryTest = (FactoryTest
                     .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
-            mContentService = new ContentService(getContext(), factoryTest);
-            publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mContentService);
+            mService = new ContentService(getContext(), factoryTest);
+            publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mService.systemReady();
+            }
         }
 
         @Override
         public void onCleanupUser(int userHandle) {
-            synchronized (mContentService.mCache) {
-                mContentService.mCache.remove(userHandle);
+            synchronized (mService.mCache) {
+                mService.mCache.remove(userHandle);
             }
         }
     }
@@ -265,7 +272,7 @@
                 localeFilter, null, null);
     }
 
-    public void systemReady() {
+    void systemReady() {
         getSyncManager();
     }
 
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index db41a54..8af0af0 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1402,24 +1402,12 @@
         }
     }
 
-    private void restoreLostPeriodicSyncsIfNeeded(int userId) {
-        List<SyncOperation> periodicSyncs = new ArrayList<SyncOperation>();
-        for (SyncOperation sync : getAllPendingSyncs()) {
-            if (sync.isPeriodic && sync.target.userId == userId) {
-                periodicSyncs.add(sync);
-            }
-        }
-        mSyncStorageEngine.restorePeriodicSyncsIfNeededForUser(userId, periodicSyncs);
-    }
-
     private void onUserUnlocked(int userId) {
         // Make sure that accounts we're about to use are valid.
         AccountManagerService.getSingleton().validateAccounts(userId);
 
         mSyncAdapters.invalidateCache(userId);
 
-        restoreLostPeriodicSyncsIfNeeded(userId);
-
         EndPoint target = new EndPoint(null, null, userId);
         updateRunningAccounts(target);
 
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index fb23265..bc3fc6a 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -826,35 +826,6 @@
         return true;
     }
 
-    /**
-     * STOPSHIP This is a temporary workaround and should be removed before shipping: b/28052438
-     */
-    void restorePeriodicSyncsIfNeededForUser(int userHandle, List<SyncOperation> periodicSyncs) {
-        if (mPeriodicSyncAddedListener == null) {
-            return;
-        }
-        synchronized (mAuthorities) {
-            for (int i = 0; i < mAuthorities.size(); i++) {
-                AuthorityInfo authority = mAuthorities.valueAt(i);
-                if (authority.target.userId == userHandle && authority.enabled) {
-                    boolean periodicSyncAlreadyExists = false;
-                    for (SyncOperation sync : periodicSyncs) {
-                        if (authority.target.matchesSpec(sync.target)) {
-                            periodicSyncAlreadyExists = true;
-                            break;
-                        }
-                    }
-                    // The periodic sync must have been lost due to previous bug.
-                    if (!periodicSyncAlreadyExists) {
-                        mPeriodicSyncAddedListener.onPeriodicSyncAdded(authority.target,
-                                new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS,
-                                calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS));
-                    }
-                }
-            }
-        }
-    }
-
     public void setMasterSyncAutomatically(boolean flag, int userId) {
         synchronized (mAuthorities) {
             Boolean auto = mMasterSyncAutomatically.get(userId);
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
new file mode 100644
index 0000000..e5a2095
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import android.app.job.JobInfo;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import com.android.server.job.controllers.JobStatus;
+
+import java.io.PrintWriter;
+
+public final class JobPackageTracker {
+    // We batch every 30 minutes.
+    static final long BATCHING_TIME = 30*60*1000;
+    // Number of historical data sets we keep.
+    static final int NUM_HISTORY = 5;
+
+    DataSet mCurDataSet = new DataSet();
+    DataSet[] mLastDataSets = new DataSet[NUM_HISTORY];
+
+    final static class PackageEntry {
+        long pastActiveTime;
+        long activeStartTime;
+        int activeCount;
+        boolean hadActive;
+        long pastActiveTopTime;
+        long activeTopStartTime;
+        int activeTopCount;
+        boolean hadActiveTop;
+        long pastPendingTime;
+        long pendingStartTime;
+        int pendingCount;
+        boolean hadPending;
+
+        public long getActiveTime(long now) {
+            long time = pastActiveTime;
+            if (activeCount > 0) {
+                time += now - activeStartTime;
+            }
+            return time;
+        }
+
+        public long getActiveTopTime(long now) {
+            long time = pastActiveTopTime;
+            if (activeTopCount > 0) {
+                time += now - activeTopStartTime;
+            }
+            return time;
+        }
+
+        public long getPendingTime(long now) {
+            long time = pastPendingTime;
+            if (pendingCount > 0) {
+                time += now - pendingStartTime;
+            }
+            return time;
+        }
+    }
+
+    final static class DataSet {
+        final SparseArray<ArrayMap<String, PackageEntry>> mEntries = new SparseArray<>();
+        final long mStartUptimeTime;
+        final long mStartElapsedTime;
+        final long mStartClockTime;
+        long mSummedTime;
+
+        public DataSet(DataSet otherTimes) {
+            mStartUptimeTime = otherTimes.mStartUptimeTime;
+            mStartElapsedTime = otherTimes.mStartElapsedTime;
+            mStartClockTime = otherTimes.mStartClockTime;
+        }
+
+        public DataSet() {
+            mStartUptimeTime = SystemClock.uptimeMillis();
+            mStartElapsedTime = SystemClock.elapsedRealtime();
+            mStartClockTime = System.currentTimeMillis();
+        }
+
+        private PackageEntry getOrCreateEntry(int uid, String pkg) {
+            ArrayMap<String, PackageEntry> uidMap = mEntries.get(uid);
+            if (uidMap == null) {
+                uidMap = new ArrayMap<>();
+                mEntries.put(uid, uidMap);
+            }
+            PackageEntry entry = uidMap.get(pkg);
+            if (entry == null) {
+                entry = new PackageEntry();
+                uidMap.put(pkg, entry);
+            }
+            return entry;
+        }
+
+        public PackageEntry getEntry(int uid, String pkg) {
+            ArrayMap<String, PackageEntry> uidMap = mEntries.get(uid);
+            if (uidMap == null) {
+                return null;
+            }
+            return uidMap.get(pkg);
+        }
+
+        long getTotalTime(long now) {
+            if (mSummedTime > 0) {
+                return mSummedTime;
+            }
+            return now - mStartUptimeTime;
+        }
+
+        void incPending(int uid, String pkg, long now) {
+            PackageEntry pe = getOrCreateEntry(uid, pkg);
+            if (pe.pendingCount == 0) {
+                pe.pendingStartTime = now;
+            }
+            pe.pendingCount++;
+        }
+
+        void decPending(int uid, String pkg, long now) {
+            PackageEntry pe = getOrCreateEntry(uid, pkg);
+            if (pe.pendingCount == 1) {
+                pe.pastPendingTime += now - pe.pendingStartTime;
+            }
+            pe.pendingCount--;
+        }
+
+        void incActive(int uid, String pkg, long now) {
+            PackageEntry pe = getOrCreateEntry(uid, pkg);
+            if (pe.activeCount == 0) {
+                pe.activeStartTime = now;
+            }
+            pe.activeCount++;
+        }
+
+        void decActive(int uid, String pkg, long now) {
+            PackageEntry pe = getOrCreateEntry(uid, pkg);
+            if (pe.activeCount == 1) {
+                pe.pastActiveTime += now - pe.activeStartTime;
+            }
+            pe.activeCount--;
+        }
+
+        void incActiveTop(int uid, String pkg, long now) {
+            PackageEntry pe = getOrCreateEntry(uid, pkg);
+            if (pe.activeTopCount == 0) {
+                pe.activeTopStartTime = now;
+            }
+            pe.activeTopCount++;
+        }
+
+        void decActiveTop(int uid, String pkg, long now) {
+            PackageEntry pe = getOrCreateEntry(uid, pkg);
+            if (pe.activeTopCount == 1) {
+                pe.pastActiveTopTime += now - pe.activeTopStartTime;
+            }
+            pe.activeTopCount--;
+        }
+
+        void finish(DataSet next, long now) {
+            for (int i = mEntries.size() - 1; i >= 0; i--) {
+                ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+                for (int j = uidMap.size() - 1; j >= 0; j--) {
+                    PackageEntry pe = uidMap.valueAt(j);
+                    if (pe.activeCount > 0 || pe.activeTopCount > 0 || pe.pendingCount > 0) {
+                        // Propagate existing activity in to next data set.
+                        PackageEntry nextPe = next.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j));
+                        nextPe.activeStartTime = now;
+                        nextPe.activeCount = pe.activeCount;
+                        nextPe.activeTopStartTime = now;
+                        nextPe.activeTopCount = pe.activeTopCount;
+                        nextPe.pendingStartTime = now;
+                        nextPe.pendingCount = pe.pendingCount;
+                        // Finish it off.
+                        if (pe.activeCount > 0) {
+                            pe.pastActiveTime += now - pe.activeStartTime;
+                            pe.activeCount = 0;
+                        }
+                        if (pe.activeTopCount > 0) {
+                            pe.pastActiveTopTime += now - pe.activeTopStartTime;
+                            pe.activeTopCount = 0;
+                        }
+                        if (pe.pendingCount > 0) {
+                            pe.pastPendingTime += now - pe.pendingStartTime;
+                            pe.pendingCount = 0;
+                        }
+                    }
+                }
+            }
+        }
+
+        void addTo(DataSet out, long now) {
+            out.mSummedTime += getTotalTime(now);
+            for (int i = mEntries.size() - 1; i >= 0; i--) {
+                ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+                for (int j = uidMap.size() - 1; j >= 0; j--) {
+                    PackageEntry pe = uidMap.valueAt(j);
+                    PackageEntry outPe = out.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j));
+                    outPe.pastActiveTime += pe.pastActiveTime;
+                    outPe.pastActiveTopTime += pe.pastActiveTopTime;
+                    outPe.pastPendingTime += pe.pastPendingTime;
+                    if (pe.activeCount > 0) {
+                        outPe.pastActiveTime += now - pe.activeStartTime;
+                        outPe.hadActive = true;
+                    }
+                    if (pe.activeTopCount > 0) {
+                        outPe.pastActiveTopTime += now - pe.activeTopStartTime;
+                        outPe.hadActiveTop = true;
+                    }
+                    if (pe.pendingCount > 0) {
+                        outPe.pastPendingTime += now - pe.pendingStartTime;
+                        outPe.hadPending = true;
+                    }
+                }
+            }
+        }
+
+        void printDuration(PrintWriter pw, long period, long duration, String suffix) {
+            float fraction = duration / (float) period;
+            int percent = (int) ((fraction * 100) + .5f);
+            if (percent > 0) {
+                pw.print(" ");
+                pw.print(percent);
+                pw.print("% ");
+                pw.print(suffix);
+            }
+        }
+
+        void dump(PrintWriter pw, String header, String prefix, long now, long nowEllapsed) {
+            final long period = getTotalTime(now);
+            pw.print(prefix); pw.print(header); pw.print(" at ");
+            pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartClockTime).toString());
+            pw.print(" (");
+            TimeUtils.formatDuration(mStartElapsedTime, nowEllapsed, pw);
+            pw.print(") over ");
+            TimeUtils.formatDuration(period, pw);
+            pw.println(":");
+            final int NE = mEntries.size();
+            for (int i = 0; i < NE; i++) {
+                ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+                final int NP = uidMap.size();
+                for (int j = 0; j < NP; j++) {
+                    PackageEntry pe = uidMap.valueAt(j);
+                    pw.print(prefix); pw.print("  ");
+                    UserHandle.formatUid(pw, mEntries.keyAt(i));
+                    pw.print(" / "); pw.print(uidMap.keyAt(j));
+                    pw.print(":");
+                    printDuration(pw, period, pe.getPendingTime(now), "pending");
+                    printDuration(pw, period, pe.getActiveTime(now), "active");
+                    printDuration(pw, period, pe.getActiveTopTime(now), "active-top");
+                    if (pe.pendingCount > 0 || pe.hadPending) {
+                        pw.print(" (pending)");
+                    }
+                    if (pe.activeCount > 0 || pe.hadActive) {
+                        pw.print(" (active)");
+                    }
+                    if (pe.activeTopCount > 0 || pe.hadActiveTop) {
+                        pw.print(" (active-top)");
+                    }
+                    pw.println();
+                }
+            }
+        }
+    }
+
+    void rebatchIfNeeded(long now) {
+        long totalTime = mCurDataSet.getTotalTime(now);
+        if (totalTime > BATCHING_TIME) {
+            DataSet last = mCurDataSet;
+            last.mSummedTime = totalTime;
+            mCurDataSet = new DataSet();
+            last.finish(mCurDataSet, now);
+            System.arraycopy(mLastDataSets, 0, mLastDataSets, 1, mLastDataSets.length-1);
+            mLastDataSets[0] = last;
+        }
+    }
+
+    public void notePending(JobStatus job) {
+        final long now = SystemClock.uptimeMillis();
+        rebatchIfNeeded(now);
+        mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now);
+    }
+
+    public void noteNonpending(JobStatus job) {
+        final long now = SystemClock.uptimeMillis();
+        mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now);
+        rebatchIfNeeded(now);
+    }
+
+    public void noteActive(JobStatus job) {
+        final long now = SystemClock.uptimeMillis();
+        rebatchIfNeeded(now);
+        if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+            mCurDataSet.incActiveTop(job.getSourceUid(), job.getSourcePackageName(), now);
+        } else {
+            mCurDataSet.incActive(job.getSourceUid(), job.getSourcePackageName(), now);
+        }
+    }
+
+    public void noteInactive(JobStatus job) {
+        final long now = SystemClock.uptimeMillis();
+        if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+            mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now);
+        } else {
+            mCurDataSet.decActive(job.getSourceUid(), job.getSourcePackageName(), now);
+        }
+        rebatchIfNeeded(now);
+    }
+
+    public float getLoadFactor(JobStatus job) {
+        final int uid = job.getSourceUid();
+        final String pkg = job.getSourcePackageName();
+        PackageEntry cur = mCurDataSet.getEntry(uid, pkg);
+        PackageEntry last = mLastDataSets[0] != null ? mLastDataSets[0].getEntry(uid, pkg) : null;
+        if (cur == null && last == null) {
+            return 0;
+        }
+        final long now = SystemClock.uptimeMillis();
+        long time = cur.getActiveTime(now) + cur.getPendingTime(now);
+        long period = mCurDataSet.getTotalTime(now);
+        if (last != null) {
+            time += last.getActiveTime(now) + last.getPendingTime(now);
+            period += mLastDataSets[0].getTotalTime(now);
+        }
+        return time / (float)period;
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        final long now = SystemClock.uptimeMillis();
+        final long nowEllapsed = SystemClock.elapsedRealtime();
+        final DataSet total;
+        if (mLastDataSets[0] != null) {
+            total = new DataSet(mLastDataSets[0]);
+            mLastDataSets[0].addTo(total, now);
+        } else {
+            total = new DataSet(mCurDataSet);
+        }
+        mCurDataSet.addTo(total, now);
+        for (int i = 1; i < mLastDataSets.length; i++) {
+            if (mLastDataSets[i] != null) {
+                mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowEllapsed);
+                pw.println();
+            }
+        }
+        total.dump(pw, "Current stats", prefix, now, nowEllapsed);
+    }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b235002..c4a2c731 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -93,16 +93,24 @@
     public static final boolean DEBUG = false;
 
     /** The maximum number of concurrent jobs we run at one time. */
-    private static final int MAX_JOB_CONTEXTS_COUNT = 8;
+    private static final int MAX_JOB_CONTEXTS_COUNT = 12;
+    /** The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. */
+    private static final int FG_JOB_CONTEXTS_COUNT = 4;
     /** Enforce a per-app limit on scheduled jobs? */
     private static final boolean ENFORCE_MAX_JOBS = true;
     /** The maximum number of jobs that we allow an unprivileged app to schedule */
     private static final int MAX_JOBS_PER_APP = 100;
+    /** This is the job execution factor that is considered to be heavy use of the system. */
+    private static final float HEAVY_USE_FACTOR = .9f;
+    /** This is the job execution factor that is considered to be moderate use of the system. */
+    private static final float MODERATE_USE_FACTOR = .5f;
 
     /** Global local for all job scheduler state. */
     final Object mLock = new Object();
     /** Master list of jobs. */
     final JobStore mJobs;
+    /** Tracking amount of time each package runs for. */
+    final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
 
     static final int MSG_JOB_EXPIRED = 0;
     static final int MSG_CHECK_JOB = 1;
@@ -173,7 +181,7 @@
      * Current limit on the number of concurrent JobServiceContext entries we want to
      * keep actively running a job.
      */
-    int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+    int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT;
 
     /**
      * Which uids are currently in the foreground.
@@ -386,7 +394,9 @@
         stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
         synchronized (mLock) {
             // Remove from pending queue.
-            mPendingJobs.remove(cancelled);
+            if (mPendingJobs.remove(cancelled)) {
+                mJobPackageTracker.noteNonpending(cancelled);
+            }
             // Cancel if running.
             stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
             reportActive();
@@ -518,7 +528,7 @@
                 // Create the "runners".
                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
                     mActiveServices.add(
-                            new JobServiceContext(this, mBatteryStats,
+                            new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
                                     getContext().getMainLooper()));
                 }
                 // Attach jobs to their controllers.
@@ -604,6 +614,20 @@
         return false;
     }
 
+    void noteJobsPending(List<JobStatus> jobs) {
+        for (int i = jobs.size() - 1; i >= 0; i--) {
+            JobStatus job = jobs.get(i);
+            mJobPackageTracker.notePending(job);
+        }
+    }
+
+    void noteJobsNonpending(List<JobStatus> jobs) {
+        for (int i = jobs.size() - 1; i >= 0; i--) {
+            JobStatus job = jobs.get(i);
+            mJobPackageTracker.noteNonpending(job);
+        }
+    }
+
     /**
      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
      * specify an override deadline on a failed job (the failed job will run even though it's not
@@ -759,6 +783,7 @@
                         // state is such that all ready jobs should be run immediately.
                         if (runNow != null && !mPendingJobs.contains(runNow)
                                 && mJobs.containsJob(runNow)) {
+                            mJobPackageTracker.notePending(runNow);
                             mPendingJobs.add(runNow);
                         }
                         queueReadyJobsForExecutionLockedH();
@@ -797,6 +822,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "queuing all ready jobs for execution:");
             }
+            noteJobsNonpending(mPendingJobs);
             mPendingJobs.clear();
             mJobs.forEachJob(mReadyQueueFunctor);
             mReadyQueueFunctor.postProcess();
@@ -832,6 +858,7 @@
 
             public void postProcess() {
                 if (newReadyJobs != null) {
+                    noteJobsPending(newReadyJobs);
                     mPendingJobs.addAll(newReadyJobs);
                 }
                 newReadyJobs = null;
@@ -910,6 +937,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
                     }
+                    noteJobsPending(runnableJobs);
                     mPendingJobs.addAll(runnableJobs);
                 } else {
                     if (DEBUG) {
@@ -935,6 +963,7 @@
         private void maybeQueueReadyJobsForExecutionLockedH() {
             if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
 
+            noteJobsNonpending(mPendingJobs);
             mPendingJobs.clear();
             mJobs.forEachJob(mMaybeQueueFunctor);
             mMaybeQueueFunctor.postProcess();
@@ -998,16 +1027,28 @@
         }
     }
 
+    private int adjustJobPriority(int curPriority, JobStatus job) {
+        if (curPriority < JobInfo.PRIORITY_TOP_APP) {
+            float factor = mJobPackageTracker.getLoadFactor(job);
+            if (factor >= HEAVY_USE_FACTOR) {
+                curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
+            } else if (factor >= MODERATE_USE_FACTOR) {
+                curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
+            }
+        }
+        return curPriority;
+    }
+
     private int evaluateJobPriorityLocked(JobStatus job) {
         int priority = job.getPriority();
         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
-            return priority;
+            return adjustJobPriority(priority, job);
         }
         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
         if (override != 0) {
-            return override;
+            return adjustJobPriority(override, job);
         }
-        return priority;
+        return adjustJobPriority(priority, job);
     }
 
     /**
@@ -1029,16 +1070,16 @@
         }
         switch (memLevel) {
             case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
-                mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - 2) * 2) / 3;
+                mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) * 2) / 3;
                 break;
             case ProcessStats.ADJ_MEM_FACTOR_LOW:
-                mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - 2) / 3;
+                mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) / 3;
                 break;
             case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
                 mMaxActiveJobs = 1;
                 break;
             default:
-                mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+                mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT;
                 break;
         }
 
@@ -1134,7 +1175,9 @@
                     if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
                         Slog.d(TAG, "Error executing " + pendingJob);
                     }
-                    mPendingJobs.remove(pendingJob);
+                    if (mPendingJobs.remove(pendingJob)) {
+                        mJobPackageTracker.noteNonpending(pendingJob);
+                    }
                 }
             }
             if (!preservePreferredUid) {
@@ -1444,6 +1487,8 @@
                 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
             }
             pw.println();
+            mJobPackageTracker.dump(pw, "");
+            pw.println();
             pw.println("Pending queue:");
             for (int i=0; i<mPendingJobs.size(); i++) {
                 JobStatus job = mPendingJobs.get(i);
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 4239248..4fd1350 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -105,6 +105,7 @@
     private final Context mContext;
     private final Object mLock;
     private final IBatteryStats mBatteryStats;
+    private final JobPackageTracker mJobPackageTracker;
     private PowerManager.WakeLock mWakeLock;
 
     // Execution state.
@@ -136,16 +137,18 @@
     /** Track when job will timeout. */
     private long mTimeoutElapsed;
 
-    JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, Looper looper) {
-        this(service.getContext(), service.getLock(), batteryStats, service, looper);
+    JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
+            JobPackageTracker tracker, Looper looper) {
+        this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
     }
 
     @VisibleForTesting
     JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
-                      JobCompletedListener completedListener, Looper looper) {
+            JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
         mContext = context;
         mLock = lock;
         mBatteryStats = batteryStats;
+        mJobPackageTracker = tracker;
         mCallbackHandler = new JobServiceHandler(looper);
         mCompletedListener = completedListener;
         mAvailable = true;
@@ -208,6 +211,7 @@
             } catch (RemoteException e) {
                 // Whatever.
             }
+            mJobPackageTracker.noteActive(job);
             mAvailable = false;
             return true;
         }
@@ -580,6 +584,7 @@
                     return;
                 }
                 completedJob = mRunningJob;
+                mJobPackageTracker.noteInactive(completedJob);
                 try {
                     mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
                             mRunningJob.getSourceUid());
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d80dc3b..6b916be 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -40,7 +40,6 @@
 import android.location.IGnssStatusProvider;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
-import android.location.GnssNavigationMessageEvent;
 import android.location.IGpsGeofenceHardware;
 import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
@@ -1668,16 +1667,6 @@
     }
 
     /**
-     * called from native code - GPS navigation message callback
-     */
-    private void reportNavigationMessage(GnssNavigationMessageEvent event) {
-        if (event != null) {
-            mGnssNavigationMessageProvider
-                    .onNavigationMessageAvailable(event.getNavigationMessage());
-        }
-    }
-
-    /**
      * called from native code to inform us what the GPS engine capabilities are
      */
     private void setEngineCapabilities(int capabilities) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 6d9fed4..c31d93e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -97,8 +97,6 @@
     // List of packages in restored setting across all mUserProfiles, for quick
     // filtering upon package updates.
     private ArraySet<String> mRestoredPackages = new ArraySet<>();
-    // State of current service categories
-    private ArrayMap<String, Boolean> mCategoryEnabled = new ArrayMap<>();
     // List of enabled packages that have nevertheless asked not to be run
     private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
 
@@ -342,47 +340,6 @@
         }
     }
 
-    public void setCategoryState(String category, boolean enabled) {
-        synchronized (mMutex) {
-            final Boolean previous = mCategoryEnabled.put(category, enabled);
-            if (!(previous == null || previous != enabled)) {
-                return;
-            }
-
-            // State changed
-            if (DEBUG) {
-                Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "category " + category);
-            }
-
-            final int[] userIds = mUserProfiles.getCurrentProfileIds();
-            for (int userId : userIds) {
-                final Set<ComponentName> componentNames = queryPackageForServices(null,
-                        userId, category);
-
-                // Disallow services not enabled in Settings
-                final ArraySet<ComponentName> userComponents =
-                        loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
-                if (userComponents == null) {
-                    componentNames.clear();
-                } else {
-                    componentNames.retainAll(userComponents);
-                }
-
-                if (DEBUG) {
-                    Slog.d(TAG, "Components for category " + category + ": " + componentNames);
-                }
-                for (ComponentName c : componentNames) {
-                    if (enabled) {
-                        registerServiceLocked(c, userId);
-                    } else {
-                        unregisterServiceLocked(c, userId);
-                    }
-                }
-            }
-
-        }
-    }
-
     private void rebuildRestoredPackages() {
         mRestoredPackages.clear();
         mSnoozingForCurrentProfiles.clear();
@@ -454,20 +411,12 @@
     }
 
     protected Set<ComponentName> queryPackageForServices(String packageName, int userId) {
-        return queryPackageForServices(packageName, userId, null);
-    }
-
-    public Set<ComponentName> queryPackageForServices(String packageName, int userId,
-            String category) {
         Set<ComponentName> installed = new ArraySet<>();
         final PackageManager pm = mContext.getPackageManager();
         Intent queryIntent = new Intent(mConfig.serviceInterface);
         if (!TextUtils.isEmpty(packageName)) {
             queryIntent.setPackage(packageName);
         }
-        if (category != null) {
-            queryIntent.addCategory(category);
-        }
         List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                 queryIntent,
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
@@ -578,15 +527,6 @@
                 }
 
                 final Set<ComponentName> add = new HashSet<>(userComponents);
-
-                // Remove components from disabled categories so that those services aren't run.
-                for (Entry<String, Boolean> e : mCategoryEnabled.entrySet()) {
-                    if (!e.getValue()) {
-                        Set<ComponentName> c = queryPackageForServices(null, userIds[i],
-                            e.getKey());
-                        add.removeAll(c);
-                    }
-                }
                 add.removeAll(mSnoozingForCurrentProfiles);
 
                 toAdd.put(userIds[i], add);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2765578..2d3ca1f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1496,7 +1496,7 @@
                     final StatusBarNotification sbn = mNotificationList.get(i).sbn;
                     if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
                             && (sbn.getNotification().flags
-                            & Notification.FLAG_AUTOGROUP_SUMMARY) != 0) {
+                            & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
                         // We could pass back a cloneLight() but clients might get confused and
                         // try to send this thing back to notify() again, which would not work
                         // very well.
@@ -2009,7 +2009,7 @@
         @Override
         public ComponentName getEffectsSuppressor() {
             enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
-            return mEffectsSuppressors.get(0);
+            return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
         }
 
         @Override
@@ -2543,18 +2543,13 @@
                 // Handle grouped notifications and bail out early if we
                 // can to avoid extracting signals.
                 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
-                boolean ignoreNotification =
-                        removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
-                if (DBG) Slog.d(TAG, "ignoreNotification is " + ignoreNotification);
 
                 // This conditional is a dirty hack to limit the logging done on
                 //     behalf of the download manager without affecting other apps.
                 if (!pkg.equals("com.android.providers.downloads")
                         || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
                     int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
-                    if (ignoreNotification) {
-                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
-                    } else if (old != null) {
+                    if (old != null) {
                         enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
                     }
                     EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
@@ -2562,10 +2557,6 @@
                             enqueueStatus);
                 }
 
-                if (ignoreNotification) {
-                    return;
-                }
-
                 mRankingHelper.extractSignals(r);
 
                 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
@@ -2681,58 +2672,6 @@
         }
     }
 
-    /**
-     * Performs group notification optimizations if SysUI is the only active
-     * notification listener and returns whether the given notification should
-     * be ignored.
-     *
-     * <p>Returns true if the given notification is a child of a group with a
-     * summary, which means that SysUI will never show it, and hence the new
-     * notification can be safely ignored. Also cancels any previous instance
-     * of the ignored notification.</p>
-     *
-     * <p>For summaries, cancels all children of that group, as SysUI will
-     * never show them anymore.</p>
-     *
-     * @return true if the given notification can be ignored as an optimization
-     */
-    private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
-            NotificationRecord old, int callingUid, int callingPid) {
-        if (!ENABLE_CHILD_NOTIFICATIONS) {
-            // No optimizations are possible if listeners want groups.
-            if (mListeners.notificationGroupsDesired()) {
-                return false;
-            }
-
-            StatusBarNotification sbn = r.sbn;
-            String group = sbn.getGroupKey();
-            boolean isSummary = sbn.getNotification().isGroupSummary();
-            boolean isChild = !isSummary && sbn.isGroup();
-
-            NotificationRecord summary = mSummaryByGroupKey.get(group);
-            if (isChild && summary != null) {
-                // Child with an active summary -> ignore
-                if (DBG) {
-                    Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
-                            + summary.getKey());
-                }
-                // Make sure we don't leave an old version of the notification around.
-                if (old != null) {
-                    if (DBG) {
-                        Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
-                    }
-                    cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
-                }
-                return true;
-            } else if (isSummary) {
-                // Summary -> cancel children
-                cancelGroupChildrenLocked(r, callingUid, callingPid, null,
-                        REASON_GROUP_OPTIMIZATION);
-            }
-        }
-        return false;
-    }
-
     @VisibleForTesting
     void buzzBeepBlinkLocked(NotificationRecord record) {
         boolean buzz = false;
@@ -3851,7 +3790,7 @@
                 return;
             }
             Set<ComponentName> rankerComponents = queryPackageForServices(
-                    mRankerServicePackageName, UserHandle.USER_SYSTEM, null);
+                    mRankerServicePackageName, UserHandle.USER_SYSTEM);
             Iterator<ComponentName> iterator = rankerComponents.iterator();
             if (iterator.hasNext()) {
                 ComponentName rankerComponent = iterator.next();
@@ -3869,7 +3808,6 @@
     public class NotificationListeners extends ManagedServices {
 
         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
-        private boolean mNotificationGroupsDesired;
 
         public NotificationListeners() {
             super(getContext(), mHandler, mNotificationList, mUserProfiles);
@@ -3902,7 +3840,6 @@
             final INotificationListener listener = (INotificationListener) info.service;
             final NotificationRankingUpdate update;
             synchronized (mNotificationList) {
-                updateNotificationGroupsDesiredLocked();
                 update = makeRankingUpdateLocked(info);
             }
             try {
@@ -3919,7 +3856,6 @@
                 updateEffectsSuppressorLocked();
             }
             mLightTrimListeners.remove(removed);
-            updateNotificationGroupsDesiredLocked();
         }
 
         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
@@ -4112,31 +4048,6 @@
             }
             return false;
         }
-
-        /**
-         * Returns whether any of the currently registered listeners wants to receive notification
-         * groups.
-         *
-         * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
-         */
-        public boolean notificationGroupsDesired() {
-            return mNotificationGroupsDesired;
-        }
-
-        private void updateNotificationGroupsDesiredLocked() {
-            mNotificationGroupsDesired = true;
-            // No listeners, no groups.
-            if (mServices.isEmpty()) {
-                mNotificationGroupsDesired = false;
-                return;
-            }
-            // One listener: Check whether it's SysUI.
-            if (mServices.size() == 1 &&
-                    mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
-                mNotificationGroupsDesired = false;
-                return;
-            }
-        }
     }
 
     public static final class DumpFilter {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5ee7cc9..43a0b91 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -294,7 +294,7 @@
         }
 
         @Override
-        public ResolveInfo resolveActivity(Intent intent, UserHandle user)
+        public ActivityInfo resolveActivity(ComponentName component, UserHandle user)
                 throws RemoteException {
             ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
             if (!isUserEnabled(user)) {
@@ -303,11 +303,11 @@
 
             long ident = Binder.clearCallingIdentity();
             try {
-                ResolveInfo app = mPm.resolveActivityAsUser(intent,
+                IPackageManager pm = AppGlobals.getPackageManager();
+                return pm.getActivityInfo(component,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                         user.getIdentifier());
-                return app;
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3b07fe1..1fb260d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4922,6 +4922,7 @@
                 }
                 ri = new ResolveInfo(mResolveInfo);
                 ri.activityInfo = new ActivityInfo(ri.activityInfo);
+                ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
                 ri.activityInfo.applicationInfo = new ApplicationInfo(
                         ri.activityInfo.applicationInfo);
                 if (userId != 0) {
@@ -7084,9 +7085,6 @@
             pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
         }
 
-        UsageStatsManager usageMgr =
-                (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
-
         int curr = 0;
         int total = pkgs.size();
         for (PackageParser.Package pkg : pkgs) {
@@ -7099,13 +7097,6 @@
                 continue;
             }
 
-            if (!causeFirstBoot && usageMgr.isAppInactive(pkg.packageName)) {
-                if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Skipping update of of idle app " + pkg.packageName);
-                }
-                continue;
-            }
-
             if (DEBUG_DEXOPT) {
                 Log.i(TAG, "Extracting app " + curr + " of " + total + ": " + pkg.packageName);
             }
@@ -16032,28 +16023,36 @@
     @Override
     public void deleteApplicationCacheFiles(final String packageName,
             final IPackageDataObserver observer) {
+        final int userId = UserHandle.getCallingUserId();
+        deleteApplicationCacheFilesAsUser(packageName, userId, observer);
+    }
+
+    @Override
+    public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
+            final IPackageDataObserver observer) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_CACHE_FILES, null);
-        // Queue up an async operation since the package deletion may take a little while.
-        final int userId = UserHandle.getCallingUserId();
+        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                /* requireFullPermission= */ true, /* checkShell= */ false,
+                "delete application cache files");
 
         final PackageParser.Package pkg;
         synchronized (mPackages) {
             pkg = mPackages.get(packageName);
         }
 
+        // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(new Runnable() {
             public void run() {
-                try (PackageFreezer freezer = freezePackage(packageName,
-                        "deleteApplicationCacheFiles")) {
-                    synchronized (mInstallLock) {
-                        final int flags = StorageManager.FLAG_STORAGE_DE
-                                | StorageManager.FLAG_STORAGE_CE;
-                        clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
-                        clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-                    }
-                    clearExternalStorageDataSync(packageName, userId, false);
+                synchronized (mInstallLock) {
+                    final int flags = StorageManager.FLAG_STORAGE_DE
+                            | StorageManager.FLAG_STORAGE_CE;
+                    // We're only clearing cache files, so we don't care if the
+                    // app is unfrozen and still able to run
+                    clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+                    clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                 }
+                clearExternalStorageDataSync(packageName, userId, false);
                 if (observer != null) {
                     try {
                         observer.onRemoveCompleted(packageName, true);
@@ -16098,11 +16097,17 @@
             mInstaller.getAppSize(ps.volumeUuid, packageName, userId,
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE,
                     ps.getCeDataInode(userId), ps.codePathString, stats);
-            return true;
         } catch (InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
             return false;
         }
+
+        // For now, ignore code size of packages on system partition
+        if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+            stats.codeSize = 0;
+        }
+
+        return true;
     }
 
     private int getUidTargetSdkVersionLockedLPr(int uid) {
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index aa10c08..a6350fe 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -92,7 +92,7 @@
      * MAC_PERMISSIONS class variable which is set at class load time which itself
      * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on
      * the proper structure of a mac_permissions.xml file consult the source code
-     * located at external/sepolicy/mac_permissions.xml.
+     * located at system/sepolicy/mac_permissions.xml.
      *
      * @return boolean indicating if policy was correctly loaded. A value of false
      *         typically indicates a structural problem with the xml or incorrectly
@@ -373,7 +373,7 @@
  * {@link Policy#getMatchedSeinfo} method. To create an instance of this use the
  * {@link PolicyBuilder} pattern class, where each instance is validated against a set
  * of invariants before being built and returned. Each instance can be guaranteed to
- * hold one valid policy stanza as outlined in the external/sepolicy/mac_permissions.xml
+ * hold one valid policy stanza as outlined in the system/sepolicy/mac_permissions.xml
  * file.
  * <p>
  * The following is an example of how to use {@link Policy.PolicyBuilder} to create a
@@ -519,7 +519,7 @@
      * A nested builder class to create {@link Policy} instances. A {@link Policy}
      * class instance represents one valid policy stanza found in a mac_permissions.xml
      * file. A valid policy stanza is defined to be a signer stanza which obeys the rules
-     * outlined in external/sepolicy/mac_permissions.xml. The {@link #build} method
+     * outlined in system/sepolicy/mac_permissions.xml. The {@link #build} method
      * ensures a set of invariants are upheld enforcing the correct stanza structure
      * before returning a valid Policy object.
      */
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 847f993..7debf9b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2686,7 +2686,7 @@
 
     void writePermissionLPr(XmlSerializer serializer, BasePermission bp)
             throws XmlPullParserException, java.io.IOException {
-        if (bp.type != BasePermission.TYPE_BUILTIN && bp.sourcePackage != null) {
+        if (bp.sourcePackage != null) {
             serializer.startTag(null, TAG_ITEM);
             serializer.attribute(null, ATTR_NAME, bp.name);
             serializer.attribute(null, "package", bp.sourcePackage);
@@ -3348,8 +3348,12 @@
                 final String ptype = parser.getAttributeValue(null, "type");
                 if (name != null && sourcePackage != null) {
                     final boolean dynamic = "dynamic".equals(ptype);
-                    final BasePermission bp = new BasePermission(name.intern(), sourcePackage,
-                            dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
+                    BasePermission bp = out.get(name);
+                    // If the permission is builtin, do not clobber it.
+                    if (bp == null || bp.type != BasePermission.TYPE_BUILTIN) {
+                        bp = new BasePermission(name.intern(), sourcePackage,
+                                dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
+                    }
                     bp.protectionLevel = readInt(parser, null, "protection",
                             PermissionInfo.PROTECTION_NORMAL);
                     bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 58559a5..b9b65eb 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -37,7 +38,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.Predicate;
 
@@ -51,6 +52,7 @@
     private static final String TAG_INTENT_EXTRAS = "intent-extras";
     private static final String TAG_EXTRAS = "extras";
     private static final String TAG_SHORTCUT = "shortcut";
+    private static final String TAG_CATEGORIES = "categories";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
@@ -67,6 +69,11 @@
     private static final String ATTR_ICON_RES = "icon-res";
     private static final String ATTR_BITMAP_PATH = "bitmap-path";
 
+    private static final String NAME_CATEGORIES = "categories";
+
+    private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
+    private static final String ATTR_NAME_XMLUTILS = "name";
+
     /**
      * All the shortcuts from the package, keyed on IDs.
      */
@@ -305,7 +312,7 @@
      * and return true.  Otherwise just return false.
      */
     public boolean tryApiCall(@NonNull ShortcutService s) {
-        if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
+        if (getApiCallCount(s) >= s.mMaxUpdatesPerInterval) {
             return false;
         }
         mApiCallCount++;
@@ -485,6 +492,16 @@
             ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
         }
 
+        {
+            final List<String> cat = si.getCategories();
+            if (cat != null && cat.size() > 0) {
+                out.startTag(null, TAG_CATEGORIES);
+                XmlUtils.writeStringArrayXml(cat.toArray(new String[cat.size()]),
+                        NAME_CATEGORIES, out);
+                out.endTag(null, TAG_CATEGORIES);
+            }
+        }
+
         ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
                 si.getIntentPersistableExtras());
         ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
@@ -550,6 +567,7 @@
         int flags;
         int iconRes;
         String bitmapPath;
+        String[] categories = null;
 
         id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
         activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -583,11 +601,21 @@
                 case TAG_EXTRAS:
                     extras = PersistableBundle.restoreFromXml(parser);
                     continue;
+                case TAG_CATEGORIES:
+                    // This just contains string-array.
+                    continue;
+                case TAG_STRING_ARRAY_XMLUTILS:
+                    if (NAME_CATEGORIES.equals(ShortcutService.parseStringAttribute(parser,
+                            ATTR_NAME_XMLUTILS))) {
+                        categories = XmlUtils.readThisStringArrayXml(parser, TAG_STRING_ARRAY_XMLUTILS, null);
+                    }
+                    continue;
             }
             throw ShortcutService.throwForInvalidTag(depth, tag);
         }
         return new ShortcutInfo(
-                userId, id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
+                userId, id, packageName, activityComponent, /* icon =*/ null, title, text,
+                (categories == null ? null : Arrays.asList(categories)), intent,
                 intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
                 iconRes, bitmapPath);
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 1641e1d..ac6510a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -126,10 +126,10 @@
     static final boolean DEBUG_LOAD = false; // STOPSHIP if true
 
     @VisibleForTesting
-    static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
+    static final long DEFAULT_RESET_INTERVAL_SEC = 60 * 60; // 1 hour
 
     @VisibleForTesting
-    static final int DEFAULT_MAX_DAILY_UPDATES = 10;
+    static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 2;
 
     @VisibleForTesting
     static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
@@ -180,7 +180,7 @@
         /**
          * Key name for the max number of modifying API calls per app for every interval. (int)
          */
-        String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
+        String KEY_MAX_UPDATES_PER_INTERVAL = "max_updates_per_interval";
 
         /**
          * Key name for the max icon dimensions in DP, for non-low-memory devices.
@@ -232,9 +232,9 @@
     private int mMaxDynamicShortcuts;
 
     /**
-     * Max number of updating API calls that each application can make a day.
+     * Max number of updating API calls that each application can make during the interval.
      */
-    int mMaxDailyUpdates;
+    int mMaxUpdatesPerInterval;
 
     /**
      * Actual throttling-reset interval.  By default it's a day.
@@ -426,8 +426,8 @@
                 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
                 * 1000L);
 
-        mMaxDailyUpdates = Math.max(0, (int) parser.getLong(
-                ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES));
+        mMaxUpdatesPerInterval = Math.max(0, (int) parser.getLong(
+                ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL, DEFAULT_MAX_UPDATES_PER_INTERVAL));
 
         mMaxDynamicShortcuts = Math.max(0, (int) parser.getLong(
                 ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
@@ -1319,10 +1319,13 @@
     }
 
     @Override
-    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
+    public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
+        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+        final int size = newShortcuts.size();
+
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
@@ -1330,12 +1333,15 @@
             if (!ps.tryApiCall(this)) {
                 return false;
             }
+            for (int i = 0; i < size; i++) {
+                final ShortcutInfo newShortcut = newShortcuts.get(i);
 
-            // Validate the shortcut.
-            fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
+                // Validate the shortcut.
+                fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
 
-            // Add it.
-            ps.addDynamicShortcut(this, newShortcut);
+                // Add it.
+                ps.addDynamicShortcut(this, newShortcut);
+            }
         }
         userPackageChanged(packageName, userId);
 
@@ -1343,19 +1349,22 @@
     }
 
     @Override
-    public void deleteDynamicShortcut(String packageName, String shortcutId,
+    public void removeDynamicShortcuts(String packageName, List shortcutIds,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
+        Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
 
         synchronized (mLock) {
-            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
+            for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+                getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this,
+                        Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
+            }
         }
         userPackageChanged(packageName, userId);
     }
 
     @Override
-    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
+    public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
         synchronized (mLock) {
@@ -1409,7 +1418,7 @@
         verifyCaller(packageName, userId);
 
         synchronized (mLock) {
-            return mMaxDailyUpdates
+            return mMaxUpdatesPerInterval
                     - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
         }
     }
@@ -2079,8 +2088,8 @@
             pw.println(mSaveDelayMillis);
             pw.print("    resetInterval:");
             pw.println(mResetInterval);
-            pw.print("    maxDailyUpdates:");
-            pw.println(mMaxDailyUpdates);
+            pw.print("    maxUpdatesPerInterval:");
+            pw.println(mMaxUpdatesPerInterval);
             pw.print("    maxDynamicShortcuts:");
             pw.println(mMaxDynamicShortcuts);
             pw.println();
@@ -2423,8 +2432,8 @@
     }
 
     @VisibleForTesting
-    int getMaxDailyUpdatesForTest() {
-        return mMaxDailyUpdates;
+    int getMaxUpdatesPerIntervalForTest() {
+        return mMaxUpdatesPerInterval;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
index 160d44c..553c5de 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
@@ -27,8 +27,11 @@
 import android.graphics.drawable.ColorDrawable;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.vr.IVrManager;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -45,6 +48,7 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.R;
+import com.android.server.vr.VrManagerService;
 
 /**
  *  Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
@@ -66,6 +70,7 @@
     private long mPanicTime;
     private WindowManager mWindowManager;
     private int mCurrentUserId;
+    private IVrManager mVrManager;
 
     public ImmersiveModeConfirmation(Context context) {
         mContext = context;
@@ -112,6 +117,21 @@
         }
     }
 
+    private boolean getVrMode() {
+        boolean vrMode = false;
+        if (mVrManager == null) {
+            // lazily grab this service since it may not be available at construction time
+            mVrManager = (IVrManager) IVrManager.Stub.asInterface(
+                ServiceManager.getService(VrManagerService.VR_MANAGER_BINDER_SERVICE));
+        }
+        if (mVrManager != null) {
+            try {
+                vrMode = mVrManager.getVrModeState();
+            } catch (RemoteException ex) { }
+        }
+        return vrMode;
+    }
+
     public void immersiveModeChanged(String pkg, boolean isImmersiveMode,
             boolean userSetupComplete) {
         mHandler.removeMessages(H.SHOW);
@@ -119,7 +139,10 @@
             final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
             if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s",
                     disabled, mConfirmed));
-            if (!disabled && (DEBUG_SHOW_EVERY_TIME || !mConfirmed) && userSetupComplete) {
+            if (!disabled
+                    && (DEBUG_SHOW_EVERY_TIME || !mConfirmed)
+                    && userSetupComplete
+                    && !getVrMode()) {
                 mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs);
             }
         } else {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f004b45..49ff385 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -50,6 +50,8 @@
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.lang.StringBuilder;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -103,6 +105,7 @@
             new RemoteCallbackList<>();
     private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
     private String mPreviousNotificationPolicyAccessPackage;
+    private String mPreviousCoarseLocationPackage;
     private String mPreviousManageOverlayPackage;
 
     private static final int MSG_VR_STATE_CHANGE = 0;
@@ -186,6 +189,32 @@
             return VrManagerService.this.getVrMode();
         }
 
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("permission denied: can't dump VrManagerService from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+            pw.print("mVrModeEnabled=");
+            pw.println(mVrModeEnabled);
+            pw.print("mCurrentVrModeUser=");
+            pw.println(mCurrentVrModeUser);
+            pw.print("mRemoteCallbacks=");
+            int i=mRemoteCallbacks.beginBroadcast(); // create the broadcast item array
+            while(i-->0) {
+                pw.print(mRemoteCallbacks.getBroadcastItem(i));
+                if (i>0) pw.print(", ");
+            }
+            mRemoteCallbacks.finishBroadcast();
+            pw.println();
+            pw.print("mCurrentVrService=");
+            pw.println(mCurrentVrService != null ? mCurrentVrService.getComponent() : "(none)");
+            pw.print("mCurrentVrModeComponent=");
+            pw.println(mCurrentVrModeComponent);
+        }
+
     };
 
     private void enforceCallerPermission(String permission) {
@@ -418,6 +447,7 @@
 
         mWasDefaultGranted = true;
 
+        grantCoarseLocationAccess(pName, userId);
         grantOverlayAccess(pName, userId);
         grantNotificationPolicyAccess(pName);
         grantNotificationListenerAccess(pName, userId);
@@ -447,6 +477,7 @@
 
         String pName = component.getPackageName();
         if (mWasDefaultGranted) {
+            revokeCoarseLocationAccess(userId);
             revokeOverlayAccess(userId);
             revokeNotificationPolicyAccess(pName);
             revokeNotificiationListenerAccess();
@@ -455,6 +486,27 @@
 
     }
 
+    private void grantCoarseLocationAccess(String pkg, UserHandle userId) {
+        PackageManager pm = mContext.getPackageManager();
+        boolean prev = (PackageManager.PERMISSION_GRANTED ==
+                pm.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, pkg));
+        mPreviousCoarseLocationPackage = null;
+        if (!prev) {
+            pm.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION,
+                    userId);
+            mPreviousCoarseLocationPackage = pkg;
+        }
+    }
+
+    private void revokeCoarseLocationAccess(UserHandle userId) {
+        PackageManager pm = mContext.getPackageManager();
+        if (mPreviousCoarseLocationPackage != null) {
+            pm.revokeRuntimePermission(mPreviousCoarseLocationPackage,
+                    android.Manifest.permission.ACCESS_COARSE_LOCATION, userId);
+            mPreviousCoarseLocationPackage = null;
+        }
+    }
+
     private void grantOverlayAccess(String pkg, UserHandle userId) {
         PackageManager pm = mContext.getPackageManager();
         boolean prev = (PackageManager.PERMISSION_GRANTED ==
@@ -476,7 +528,6 @@
         }
     }
 
-
     private void grantNotificationPolicyAccess(String pkg) {
         NotificationManager nm = mContext.getSystemService(NotificationManager.class);
         boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index fe32104..10e30ed 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -16,9 +16,12 @@
 
 package com.android.server.wallpaper;
 
-import static android.app.WallpaperManager.FLAG_SYSTEM;
 import static android.app.WallpaperManager.FLAG_LOCK;
-import static android.os.ParcelFileDescriptor.*;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.os.ParcelFileDescriptor.MODE_CREATE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -41,27 +44,26 @@
 import android.content.ServiceConnection;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
-import android.os.RemoteException;
-import android.os.FileObserver;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallbackList;
+import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -79,35 +81,64 @@
 import android.view.IWindowManager;
 import android.view.WindowManager;
 
-import java.io.BufferedOutputStream;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
+import com.android.internal.R;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.server.EventLogTags;
+import com.android.server.SystemService;
+
+import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.JournaledFile;
-import com.android.internal.R;
-import com.android.server.EventLogTags;
-
-import libcore.io.IoUtils;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub {
     static final String TAG = "WallpaperManagerService";
     static final boolean DEBUG = false;
 
+    public static class Lifecycle extends SystemService {
+        private WallpaperManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new WallpaperManagerService(getContext());
+            publishBinderService(Context.WALLPAPER_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mService.systemReady();
+            } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+                mService.switchUser(UserHandle.USER_SYSTEM, null);
+            }
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mService.onUnlockUser(userHandle);
+        }
+    }
+
     final Object mLock = new Object();
 
     /**
@@ -417,6 +448,7 @@
     final AppOpsManager mAppOpsManager;
     WallpaperData mLastWallpaper;
     IWallpaperManagerCallback mKeyguardListener;
+    boolean mWaitingForUnlock;
 
     /**
      * ID of the current wallpaper, changed every time anything sets a wallpaper.
@@ -744,8 +776,9 @@
             if (wallpaper.wallpaperComponent != null
                     && isPackageModified(wallpaper.wallpaperComponent.getPackageName())) {
                 try {
-                    mContext.getPackageManager().getServiceInfo(
-                            wallpaper.wallpaperComponent, 0);
+                    mContext.getPackageManager().getServiceInfo(wallpaper.wallpaperComponent,
+                            PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
                 } catch (NameNotFoundException e) {
                     Slog.w(TAG, "Wallpaper component gone, removing: "
                             + wallpaper.wallpaperComponent);
@@ -755,8 +788,9 @@
             if (wallpaper.nextWallpaperComponent != null
                     && isPackageModified(wallpaper.nextWallpaperComponent.getPackageName())) {
                 try {
-                    mContext.getPackageManager().getServiceInfo(
-                            wallpaper.nextWallpaperComponent, 0);
+                    mContext.getPackageManager().getServiceInfo(wallpaper.nextWallpaperComponent,
+                            PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
                 } catch (NameNotFoundException e) {
                     wallpaper.nextWallpaperComponent = null;
                 }
@@ -793,7 +827,7 @@
         }
     }
 
-    public void systemRunning() {
+    void systemReady() {
         if (DEBUG) Slog.v(TAG, "systemReady");
         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
         // If we think we're going to be using the system image wallpaper imagery, make
@@ -818,9 +852,6 @@
                 Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
             }
         }
-        switchWallpaper(wallpaper, null);
-        wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
-        wallpaper.wallpaperObserver.startWatching();
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -828,7 +859,7 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
+                final String action = intent.getAction();
                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
                     onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                             UserHandle.USER_NULL));
@@ -892,6 +923,14 @@
         mLockWallpaperMap.remove(userId);
     }
 
+    void onUnlockUser(int userId) {
+        synchronized (mLock) {
+            if (mCurrentUserId == userId && mWaitingForUnlock) {
+                switchUser(userId, null);
+            }
+        }
+    }
+
     void onRemoveUser(int userId) {
         if (userId < 1) return;
 
@@ -919,18 +958,34 @@
 
     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
         synchronized (mLock) {
-            RuntimeException e = null;
-            try {
-                ComponentName cname = wallpaper.wallpaperComponent != null ?
-                        wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
-                if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
-                    return;
+            mWaitingForUnlock = false;
+            final ComponentName cname = wallpaper.wallpaperComponent != null ?
+                    wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
+            if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
+                // We failed to bind the desired wallpaper, but that might
+                // happen if the wallpaper isn't direct-boot aware
+                ServiceInfo si = null;
+                try {
+                    si = mIPackageManager.getServiceInfo(cname,
+                            PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
+                } catch (RemoteException ignored) {
                 }
-            } catch (RuntimeException e1) {
-                e = e1;
+
+                if (si == null) {
+                    Slog.w(TAG, "Failure starting previous wallpaper; clearing");
+                    clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
+                } else {
+                    Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
+                    // We might end up persisting the current wallpaper data
+                    // while locked, so pretend like the component was actually
+                    // bound into place
+                    wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
+                    final WallpaperData fallback = new WallpaperData(wallpaper.userId,
+                            WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+                    bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
+                    mWaitingForUnlock = true;
+                }
             }
-            Slog.w(TAG, "Failure starting previous wallpaper", e);
-            clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
         }
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index cd976e7..e17ea49 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -119,6 +119,8 @@
     }
 
     private void updateFallbackStateOnBoot() {
+        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
         WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
         updateFallbackState(webviewProviders, true);
     }
@@ -243,8 +245,16 @@
                     synchronized(mLock) {
                         try {
                             newPackage = findPreferredWebViewPackage();
-                            if (mCurrentWebViewPackage != null)
+                            if (mCurrentWebViewPackage != null) {
                                 oldProviderName = mCurrentWebViewPackage.packageName;
+                                if (changedState == WebViewUpdateService.PACKAGE_CHANGED
+                                        && newPackage.packageName.equals(oldProviderName)) {
+                                    // If we don't change package name we should only rerun the
+                                    // preparation phase if the current package has been replaced
+                                    // (not if it has been enabled/disabled).
+                                    return;
+                                }
+                            }
                             // Only trigger update actions if the updated package is the one
                             // that will be used, or the one that was in use before the
                             // update, or if we haven't seen a valid WebView package before.
@@ -497,8 +507,15 @@
                     mWebViewPackageDirty = false;
                     // If we have changed provider since we started the relro creation we need to
                     // redo the whole process using the new package instead.
-                    PackageInfo newPackage = findPreferredWebViewPackage();
-                    onWebViewProviderChanged(newPackage);
+                    try {
+                        PackageInfo newPackage = findPreferredWebViewPackage();
+                        onWebViewProviderChanged(newPackage);
+                    } catch (WebViewFactory.MissingWebViewPackageException e) {
+                        // If we can't find any valid WebView package we are now in a state where
+                        // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+                        // should simply wait until we receive an intent declaring a new package was
+                        // installed.
+                    }
                 } else {
                     mLock.notifyAll();
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 28379f4..1f16481 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -290,7 +290,7 @@
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             TaskStack stack = mStacks.get(stackNdx);
             stack.getBounds(mTmpRect);
-            if (!mTmpRect.contains(x, y)) {
+            if (!mTmpRect.contains(x, y) || stack.isAdjustedForMinimizedDockedStack()) {
                 continue;
             }
             final ArrayList<Task> tasks = stack.getTasks();
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6ac71c7..ff537be 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -270,6 +270,19 @@
         mDockedStackListeners.finishBroadcast();
     }
 
+    void notifyDockSideChanged(int newDockSide) {
+        final int size = mDockedStackListeners.beginBroadcast();
+        for (int i = 0; i < size; ++i) {
+            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+            try {
+                listener.onDockSideChanged(newDockSide);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
+            }
+        }
+        mDockedStackListeners.finishBroadcast();
+    }
+
     void registerDockedStackListener(IDockedStackListener listener) {
         mDockedStackListeners.register(listener);
         notifyDockedDividerVisibilityChanged(wasVisible());
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index eea0e73..be9fb26 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -294,7 +294,8 @@
                 final WindowState child = windows.get(winNdx);
                 final InputChannel inputChannel = child.mInputChannel;
                 final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
-                if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
+                if (inputChannel == null || inputWindowHandle == null || child.mRemoved
+                        || child.isAdjustedForMinimizedDock()) {
                     // Skip this window because it cannot possibly receive input.
                     continue;
                 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 446b74b..7074a83 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,6 +16,20 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -35,20 +49,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 public class TaskStack implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
 
@@ -379,10 +379,15 @@
             return false;
         }
 
+        final int oldDockSide = mStackId == DOCKED_STACK_ID ? getDockSide() : DOCKED_INVALID;
         mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         if (mStackId == DOCKED_STACK_ID) {
             repositionDockedStackAfterRotation(mTmpRect2);
             snapDockedStackAfterRotation(mTmpRect2);
+            final int newDockSide = getDockSide(mTmpRect2);
+            if (oldDockSide != newDockSide) {
+                mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
+            }
         }
 
         if (scheduleResize) {
@@ -910,7 +915,7 @@
         // Calculate the content bounds excluding the area occupied by IME
         getDisplayContent().getContentRect(displayContentRect);
         contentBounds.set(displayContentRect);
-        int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+        int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
 
         // if IME window is animating, get its actual vertical shown position (but no smaller than
         // the final target vertical position)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 57f38d1..0a0f0f0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.animation.ValueAnimator;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
@@ -153,6 +154,8 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.Socket;
 import java.text.DateFormat;
 import java.util.ArrayList;
@@ -332,6 +335,14 @@
 
     private static final String PROPERTY_BUILD_DATE_UTC = "ro.build.date.utc";
 
+    // Enums for animation scale update types.
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
+    private @interface UpdateAnimationScaleMode {};
+    private static final int WINDOW_ANIMATION_SCALE = 0;
+    private static final int TRANSITION_ANIMATION_SCALE = 1;
+    private static final int ANIMATION_DURATION_SCALE = 2;
+
     final private KeyguardDisableHandler mKeyguardDisableHandler;
 
     final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -613,18 +624,42 @@
     private final class SettingsObserver extends ContentObserver {
         private final Uri mDisplayInversionEnabledUri =
                 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+        private final Uri mWindowAnimationScaleUri =
+                Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE);
+        private final Uri mTransitionAnimationScaleUri =
+                Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE);
+        private final Uri mAnimationDurationScaleUri =
+                Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
 
         public SettingsObserver() {
             super(new Handler());
             ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(mWindowAnimationScaleUri, false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(mTransitionAnimationScaleUri, false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(mAnimationDurationScaleUri, false, this,
+                    UserHandle.USER_ALL);
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (mDisplayInversionEnabledUri.equals(uri)) {
                 updateCircularDisplayMaskIfNeeded();
+            } else {
+                @UpdateAnimationScaleMode
+                final int mode;
+                if (uri.equals(mWindowAnimationScaleUri)) {
+                    mode = WINDOW_ANIMATION_SCALE;
+                } else if (uri.equals(mTransitionAnimationScaleUri)) {
+                    mode = TRANSITION_ANIMATION_SCALE;
+                } else { // uri.equals(mAnimationDurationScaleUri)
+                    mode = ANIMATION_DURATION_SCALE;
+                }
+                Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
+                mH.sendMessage(m);
             }
         }
     }
@@ -3905,16 +3940,6 @@
                 return;
             }
 
-            if (transferStartingWindow(transferFrom, wtoken)) {
-                return;
-            }
-
-            // There is no existing starting window, and the caller doesn't
-            // want us to create one, so that's it!
-            if (!createIfNeeded) {
-                return;
-            }
-
             // If this is a translucent window, then don't
             // show a starting window -- the current effect (a full-screen
             // opaque starting window that fades away to the real contents
@@ -3960,6 +3985,16 @@
                 }
             }
 
+            if (transferStartingWindow(transferFrom, wtoken)) {
+                return;
+            }
+
+            // There is no existing starting window, and the caller doesn't
+            // want us to create one, so that's it!
+            if (!createIfNeeded) {
+                return;
+            }
+
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
             wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
                     labelRes, icon, logo, windowFlags);
@@ -5986,7 +6021,13 @@
                 "screenshotApplications()")) {
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
-        return screenshotApplicationsInner(appToken, displayId, width, height, false, frameScale);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
+            return screenshotApplicationsInner(appToken, displayId, width, height, false,
+                    frameScale);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
     }
 
     Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
@@ -7748,6 +7789,8 @@
         public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
         public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
 
+        public static final int UPDATE_ANIMATION_SCALE = 51;
+
         /**
          * Used to denote that an integer field in a message will not be used.
          */
@@ -8030,6 +8073,36 @@
                     break;
                 }
 
+                case UPDATE_ANIMATION_SCALE: {
+                    @UpdateAnimationScaleMode
+                    final int mode = msg.arg1;
+                    switch (mode) {
+                        case WINDOW_ANIMATION_SCALE: {
+                            mWindowAnimationScaleSetting = Settings.Global.getFloat(
+                                    mContext.getContentResolver(),
+                                    Settings.Global.WINDOW_ANIMATION_SCALE,
+                                    mWindowAnimationScaleSetting);
+                            break;
+                        }
+                        case TRANSITION_ANIMATION_SCALE: {
+                            mTransitionAnimationScaleSetting = Settings.Global.getFloat(
+                                    mContext.getContentResolver(),
+                                    Settings.Global.TRANSITION_ANIMATION_SCALE,
+                                    mTransitionAnimationScaleSetting);
+                            break;
+                        }
+                        case ANIMATION_DURATION_SCALE: {
+                            mAnimatorDurationScaleSetting = Settings.Global.getFloat(
+                                    mContext.getContentResolver(),
+                                    Settings.Global.ANIMATOR_DURATION_SCALE,
+                                    mAnimatorDurationScaleSetting);
+                            dispatchNewAnimatorScaleLocked(null);
+                            break;
+                        }
+                    }
+                    break;
+                }
+
                 case FORCE_GC: {
                     synchronized (mWindowMap) {
                         // Since we're holding both mWindowMap and mAnimator we don't need to
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c991130..8371cfe 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -774,10 +774,6 @@
 
         applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
 
-        // Offset the actual frame by the amount layout frame is off.
-        mFrame.offset(-layoutXDiff, -layoutYDiff);
-        mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
-
         // Calculate the outsets before the content frame gets shrinked to the window frame.
         if (hasOutsets) {
             mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
@@ -816,20 +812,20 @@
                 mMovedByResize = true;
             }
         } else {
-            mContentFrame.set(Math.max(mContentFrame.left, layoutContainingFrame.left),
-                    Math.max(mContentFrame.top, layoutContainingFrame.top),
-                    Math.min(mContentFrame.right, layoutContainingFrame.right),
-                    Math.min(mContentFrame.bottom, layoutContainingFrame.bottom));
+            mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
+                    Math.max(mContentFrame.top, mFrame.top),
+                    Math.min(mContentFrame.right, mFrame.right),
+                    Math.min(mContentFrame.bottom, mFrame.bottom));
 
-            mVisibleFrame.set(Math.max(mVisibleFrame.left, layoutContainingFrame.left),
-                    Math.max(mVisibleFrame.top, layoutContainingFrame.top),
-                    Math.min(mVisibleFrame.right, layoutContainingFrame.right),
-                    Math.min(mVisibleFrame.bottom, layoutContainingFrame.bottom));
+            mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
+                    Math.max(mVisibleFrame.top, mFrame.top),
+                    Math.min(mVisibleFrame.right, mFrame.right),
+                    Math.min(mVisibleFrame.bottom, mFrame.bottom));
 
-            mStableFrame.set(Math.max(mStableFrame.left, layoutContainingFrame.left),
-                    Math.max(mStableFrame.top, layoutContainingFrame.top),
-                    Math.min(mStableFrame.right, layoutContainingFrame.right),
-                    Math.min(mStableFrame.bottom, layoutContainingFrame.bottom));
+            mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
+                    Math.max(mStableFrame.top, mFrame.top),
+                    Math.min(mStableFrame.right, mFrame.right),
+                    Math.min(mStableFrame.bottom, mFrame.bottom));
         }
 
         if (fullscreenTask && !windowsAreFloating) {
@@ -857,30 +853,33 @@
             getDisplayContent().getLogicalDisplayRect(mTmpRect);
             // Override right and/or bottom insets in case if the frame doesn't fit the screen in
             // non-fullscreen mode.
-            boolean overrideRightInset = !fullscreenTask && layoutContainingFrame.right > mTmpRect.right;
-            boolean overrideBottomInset = !fullscreenTask && layoutContainingFrame.bottom > mTmpRect.bottom;
-            mContentInsets.set(mContentFrame.left - layoutContainingFrame.left,
-                    mContentFrame.top - layoutContainingFrame.top,
+            boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right;
+            boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom;
+            mContentInsets.set(mContentFrame.left - mFrame.left,
+                    mContentFrame.top - mFrame.top,
                     overrideRightInset ? mTmpRect.right - mContentFrame.right
-                            : layoutContainingFrame.right - mContentFrame.right,
+                            : mFrame.right - mContentFrame.right,
                     overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
-                            : layoutContainingFrame.bottom - mContentFrame.bottom);
+                            : mFrame.bottom - mContentFrame.bottom);
 
-            mVisibleInsets.set(mVisibleFrame.left - layoutContainingFrame.left,
-                    mVisibleFrame.top - layoutContainingFrame.top,
+            mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
+                    mVisibleFrame.top - mFrame.top,
                     overrideRightInset ? mTmpRect.right - mVisibleFrame.right
-                            : layoutContainingFrame.right - mVisibleFrame.right,
+                            : mFrame.right - mVisibleFrame.right,
                     overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
-                            : layoutContainingFrame.bottom - mVisibleFrame.bottom);
+                            : mFrame.bottom - mVisibleFrame.bottom);
 
-            mStableInsets.set(Math.max(mStableFrame.left - layoutContainingFrame.left, 0),
-                    Math.max(mStableFrame.top - layoutContainingFrame.top, 0),
+            mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
+                    Math.max(mStableFrame.top - mFrame.top, 0),
                     overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
-                            : Math.max(layoutContainingFrame.right - mStableFrame.right, 0),
+                            : Math.max(mFrame.right - mStableFrame.right, 0),
                     overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
-                            :  Math.max(layoutContainingFrame.bottom - mStableFrame.bottom, 0));
+                            :  Math.max(mFrame.bottom - mStableFrame.bottom, 0));
         }
 
+        // Offset the actual frame by the amount layout frame is off.
+        mFrame.offset(-layoutXDiff, -layoutYDiff);
+        mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
         mContentFrame.offset(-layoutXDiff, -layoutYDiff);
         mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
         mStableFrame.offset(-layoutXDiff, -layoutYDiff);
@@ -1390,6 +1389,11 @@
         return configChanged;
     }
 
+    boolean isAdjustedForMinimizedDock() {
+        return mAppToken != null && mAppToken.mTask != null
+                && mAppToken.mTask.mStack.isAdjustedForMinimizedDock();
+    }
+
     void removeLocked() {
         disposeInputChannel();
 
@@ -1776,7 +1780,8 @@
         return isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE)
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
-                && (mAppToken == null || mAppToken.windowsAreFocusable());
+                && (mAppToken == null || mAppToken.windowsAreFocusable())
+                && !isAdjustedForMinimizedDock();
     }
 
     @Override
@@ -2405,8 +2410,8 @@
             pw.print(prefix); pw.print("mToken="); pw.println(mToken);
             pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
             if (mAppToken != null) {
-                pw.print(prefix); pw.print("mAppToken="); pw.print(mAppToken);
-                pw.print(" isAnimatingWithSavedSurface()=");
+                pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
+                pw.print(prefix); pw.print(" isAnimatingWithSavedSurface()=");
                 pw.print(isAnimatingWithSavedSurface());
                 pw.print(" mAppDied=");pw.println(mAppDied);
             }
@@ -2491,6 +2496,7 @@
                     pw.print(" content="); mContentInsets.printShortString(pw);
                     pw.print(" visible="); mVisibleInsets.printShortString(pw);
                     pw.print(" stable="); mStableInsets.printShortString(pw);
+                    pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
                     pw.print(" outsets="); mOutsets.printShortString(pw);
                     pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9c25f63..0245513 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.StackId;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
@@ -1032,7 +1033,7 @@
                         // relative to their containing frame. We need to offset the difference
                         // between the containing frame as used to calculate the crop and our
                         // bounds to compensate for this.
-                        if (mWin.isChildWindow() && mWin.layoutInParentFrame()) {
+                        if (mWin.layoutInParentFrame()) {
                             mClipRect.offset( (mWin.mContainingFrame.left - mWin.mFrame.left),
                                     mWin.mContainingFrame.top - mWin.mFrame.top );
                         }
@@ -1164,8 +1165,8 @@
             final float scale = w.mInvGlobalScale;
             mSystemDecorRect.left = (int) (mSystemDecorRect.left * scale - 0.5f);
             mSystemDecorRect.top = (int) (mSystemDecorRect.top * scale - 0.5f);
-            mSystemDecorRect.right = (int) ((mSystemDecorRect.right+1) * scale - 0.5f);
-            mSystemDecorRect.bottom = (int) ((mSystemDecorRect.bottom+1) * scale - 0.5f);
+            mSystemDecorRect.right = (int) ((mSystemDecorRect.right + 1) * scale - 0.5f);
+            mSystemDecorRect.bottom = (int) ((mSystemDecorRect.bottom + 1) * scale - 0.5f);
         }
     }
 
@@ -1178,8 +1179,8 @@
             return;
         }
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" +
-                mLastClipRect);
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG,
+                "Updating crop win=" + w + " mLastCrop=" + mLastClipRect);
 
         // Need to recompute a new system decor rect each time.
         if (!w.isDefaultDisplay()) {
@@ -1204,8 +1205,8 @@
         } else {
             // Crop to the system decor specified by policy.
             calculateSystemDecorRect();
-            if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop for " + w + ", mDecorFrame="
-                    + w.mDecorFrame + ", mSystemDecorRect=" + mSystemDecorRect);
+            if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
+                    + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
         }
 
         final boolean fullscreen = w.isFrameFullscreen(displayInfo);
@@ -1215,8 +1216,8 @@
         // We use the clip rect as provided by the tranformation for non-fullscreen windows to
         // avoid premature clipping with the system decor rect.
         clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : mSystemDecorRect);
-        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Initial clip rect: " + clipRect + ", mHasClipRect="
-                + mHasClipRect + ", fullscreen=" + fullscreen);
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect
+                + " mHasClipRect=" + mHasClipRect + " fullscreen=" + fullscreen);
 
         if (isFreeformResizing && !w.isChildWindow()) {
             // For freeform resizing non child windows, we are using the big surface positioned
@@ -1243,7 +1244,8 @@
 
         finalClipRect.setEmpty();
         adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing);
-        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect);
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG,
+                "win=" + w + " Clip rect after stack adjustment=" + clipRect);
 
         w.transformFromScreenToSurfaceSpace(clipRect);
 
@@ -1254,6 +1256,8 @@
     }
 
     void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "updateSurfaceWindowCrop: win=" + mWin
+                + " clipRect=" + clipRect + " finalClipRect=" + finalClipRect);
         if (!clipRect.equals(mLastClipRect)) {
             mLastClipRect.set(clipRect);
             mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
@@ -1288,19 +1292,20 @@
         }
 
         final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
-        if (w == winShowWhenLocked) {
+        if (w == winShowWhenLocked && mPolicy.isKeyguardShowingOrOccluded()) {
             return;
         }
 
         final TaskStack stack = task.mStack;
         stack.getDimBounds(mTmpStackBounds);
+        final Rect surfaceInsets = w.getAttrs().surfaceInsets;
         // When we resize we use the big surface approach, which means we can't trust the
         // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
         // hardcoding it, we use surface coordinates.
         final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() :
-                w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
+                w.mFrame.left + mWin.mXOffset - surfaceInsets.left;
         final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
-                w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
+                w.mFrame.top + mWin.mYOffset - surfaceInsets.top;
 
         // If we are animating, we either apply the clip before applying all the animation
         // transformation or after all the transformation.
@@ -1311,6 +1316,15 @@
         if (useFinalClipRect) {
             finalClipRect.set(mTmpStackBounds);
         } else {
+            if (StackId.hasWindowShadow(stack.mStackId)
+                    && !StackId.isTaskResizeAllowed(stack.mStackId)) {
+                // The windows in this stack display drop shadows and the fill the entire stack
+                // area. Adjust the stack bounds we will use to cropping take into account the
+                // offsets we use to display the drop shadow so it doesn't get cropped.
+                mTmpStackBounds.inset(-surfaceInsets.left, -surfaceInsets.top,
+                        -surfaceInsets.right, -surfaceInsets.bottom);
+            }
+
             clipRect.left = Math.max(0,
                     Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
             clipRect.top = Math.max(0,
@@ -1356,21 +1370,19 @@
             // past where the system would have cropped us
             mTmpClipRect.set(0, 0, mTmpSize.width(), mTmpSize.height());
             mTmpFinalClipRect.setEmpty();
-            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
         } else {
             mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
                     recoveringMemory);
-            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
         }
 
+        updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
 
         mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * extraHScale,
                 mDtDx * w.mVScale * extraVScale,
                 mDsDy * w.mHScale * extraHScale,
                 mDtDy * w.mVScale * extraVScale, recoveringMemory);
         mSurfaceResized = mSurfaceController.setSizeInTransaction(
-                mTmpSize.width(), mTmpSize.height(),
-                recoveringMemory);
+                mTmpSize.width(), mTmpSize.height(), recoveringMemory);
 
         if (mSurfaceResized) {
             mReportSurfaceResized = true;
@@ -1775,7 +1787,7 @@
             pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
                     pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
                     pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
-                    pw.print(" mAnimation="); pw.println(mAnimation);
+                    pw.print(" mAnimation="); pw.print(mAnimation);
                     pw.print(" mStackClip="); pw.println(mStackClip);
         }
         if (mHasTransformation || mHasLocalTransformation) {
@@ -1793,9 +1805,9 @@
             pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
             pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
             pw.print(" last="); mLastSystemDecorRect.printShortString(pw);
-            if (mHasClipRect) {
-                pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
-            }
+            pw.print(" mHasClipRect="); pw.print(mHasClipRect);
+            pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
+
             if (!mLastFinalClipRect.isEmpty()) {
                 pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
             }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 78b0844..93eb82f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -562,7 +562,7 @@
     method_reportNavigationMessages = env->GetMethodID(
             clazz,
             "reportNavigationMessage",
-            "(Landroid/location/GnssNavigationMessageEvent;)V");
+            "(Landroid/location/GnssNavigationMessage;)V");
 
     err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
     if (err == 0) {
@@ -1486,26 +1486,6 @@
     return object.get();
 }
 
-static void set_navigation_message(jobject navigationMessage) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jclass navigationMessageEventClass =
-            env->FindClass("android/location/GnssNavigationMessageEvent");
-    jmethodID navigationMessageEventCtor = env->GetMethodID(
-            navigationMessageEventClass,
-            "<init>",
-            "(Landroid/location/GnssNavigationMessage;)V");
-    jobject navigationMessageEvent = env->NewObject(
-            navigationMessageEventClass,
-            navigationMessageEventCtor,
-            navigationMessage);
-    env->CallVoidMethod(mCallbacksObj,
-                        method_reportNavigationMessages,
-                        navigationMessageEvent);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    env->DeleteLocalRef(navigationMessageEventClass);
-    env->DeleteLocalRef(navigationMessageEvent);
-}
-
 static void navigation_message_callback(GpsNavigationMessage* message) {
     if (message == NULL) {
         ALOGE("Invalid Navigation Message provided to callback");
@@ -1517,7 +1497,9 @@
     }
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject navigationMessage = translate_gps_navigation_message(env, message);
-    set_navigation_message(navigationMessage);
+    env->CallVoidMethod(mCallbacksObj,
+                        method_reportNavigationMessages,
+                        navigationMessage);
     env->DeleteLocalRef(navigationMessage);
 }
 
@@ -1532,7 +1514,9 @@
     }
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     jobject navigationMessage = translate_gnss_navigation_message(env, message);
-    set_navigation_message(navigationMessage);
+    env->CallVoidMethod(mCallbacksObj,
+                        method_reportNavigationMessages,
+                        navigationMessage);
     env->DeleteLocalRef(navigationMessage);
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d229633..d3d05f3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -211,7 +211,7 @@
     private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
             = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
 
-    private static final int MONITORING_CERT_NOTIFICATION_ID = R.string.ssl_ca_cert_warning;
+    private static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning;
     private static final int PROFILE_WIPED_NOTIFICATION_ID = 1001;
 
     private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
@@ -655,8 +655,8 @@
         Bundle userRestrictions;
 
         // Support text provided by the admin to display to the user.
-        String shortSupportMessage = null;
-        String longSupportMessage = null;
+        CharSequence shortSupportMessage = null;
+        CharSequence longSupportMessage = null;
 
         // Background color of confirm credentials screen. Default: teal.
         static final int DEF_ORGANIZATION_COLOR = Color.parseColor("#00796B");
@@ -870,12 +870,12 @@
             }
             if (!TextUtils.isEmpty(shortSupportMessage)) {
                 out.startTag(null, TAG_SHORT_SUPPORT_MESSAGE);
-                out.text(shortSupportMessage);
+                out.text(shortSupportMessage.toString());
                 out.endTag(null, TAG_SHORT_SUPPORT_MESSAGE);
             }
             if (!TextUtils.isEmpty(longSupportMessage)) {
                 out.startTag(null, TAG_LONG_SUPPORT_MESSAGE);
-                out.text(longSupportMessage);
+                out.text(longSupportMessage.toString());
                 out.endTag(null, TAG_LONG_SUPPORT_MESSAGE);
             }
             if (parentAdmin != null) {
@@ -2696,23 +2696,25 @@
             // Build and show a warning notification
             int smallIconId;
             String contentText;
-            // TODO Why does it use the DO name?  The cert APIs are all for PO. b/25772443
-            final String ownerName = getDeviceOwnerName();
-            if (isManagedProfile(userHandle.getIdentifier())) {
-                contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_administrator);
+            if (getProfileOwner(userHandle.getIdentifier()) != null) {
+                contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed,
+                        getProfileOwnerName(userHandle.getIdentifier()));
                 smallIconId = R.drawable.stat_sys_certificate_info;
-            } else if (ownerName != null) {
-                contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed, ownerName);
+            } else if (getDeviceOwnerUserId() == userHandle.getIdentifier()) {
+                contentText = mContext.getString(R.string.ssl_ca_cert_noti_managed,
+                        getDeviceOwnerName());
                 smallIconId = R.drawable.stat_sys_certificate_info;
             } else {
                 contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_unknown);
                 smallIconId = android.R.drawable.stat_sys_warning;
             }
 
+            final int numberOfCertificates = pendingCertificates.size();
             Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO);
             dialogIntent.setFlags(
                     Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
             dialogIntent.setPackage("com.android.settings");
+            dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, numberOfCertificates);
             PendingIntent notifyIntent = PendingIntent.getActivityAsUser(mContext, 0,
                     dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null, userHandle);
 
@@ -2726,7 +2728,8 @@
             }
             final Notification noti = new Notification.Builder(userContext)
                 .setSmallIcon(smallIconId)
-                .setContentTitle(mContext.getString(R.string.ssl_ca_cert_warning))
+                .setContentTitle(mContext.getResources().getQuantityText(
+                        R.plurals.ssl_ca_cert_warning, numberOfCertificates))
                 .setContentText(contentText)
                 .setContentIntent(notifyIntent)
                 .setPriority(Notification.PRIORITY_HIGH)
@@ -8501,7 +8504,7 @@
     }
 
     @Override
-    public void setShortSupportMessage(@NonNull ComponentName who, String message) {
+    public void setShortSupportMessage(@NonNull ComponentName who, CharSequence message) {
         if (!mHasFeature) {
             return;
         }
@@ -8518,7 +8521,7 @@
     }
 
     @Override
-    public String getShortSupportMessage(@NonNull ComponentName who) {
+    public CharSequence getShortSupportMessage(@NonNull ComponentName who) {
         if (!mHasFeature) {
             return null;
         }
@@ -8531,7 +8534,7 @@
     }
 
     @Override
-    public void setLongSupportMessage(@NonNull ComponentName who, String message) {
+    public void setLongSupportMessage(@NonNull ComponentName who, CharSequence message) {
         if (!mHasFeature) {
             return;
         }
@@ -8548,7 +8551,7 @@
     }
 
     @Override
-    public String getLongSupportMessage(@NonNull ComponentName who) {
+    public CharSequence getLongSupportMessage(@NonNull ComponentName who) {
         if (!mHasFeature) {
             return null;
         }
@@ -8561,7 +8564,7 @@
     }
 
     @Override
-    public String getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
+    public CharSequence getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
         if (!mHasFeature) {
             return null;
         }
@@ -8579,7 +8582,7 @@
     }
 
     @Override
-    public String getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
+    public CharSequence getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
         if (!mHasFeature) {
             return null;
         }
@@ -8657,7 +8660,7 @@
     }
 
     @Override
-    public void setOrganizationName(@NonNull ComponentName who, String text) {
+    public void setOrganizationName(@NonNull ComponentName who, CharSequence text) {
         if (!mHasFeature) {
             return;
         }
@@ -8668,14 +8671,15 @@
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             if (!TextUtils.equals(admin.organizationName, text)) {
-                admin.organizationName = TextUtils.nullIfEmpty(text);
+                admin.organizationName = (text == null || text.length() == 0)
+                        ? null : text.toString();
                 saveSettingsLocked(userHandle);
             }
         }
     }
 
     @Override
-    public String getOrganizationName(@NonNull ComponentName who) {
+    public CharSequence getOrganizationName(@NonNull ComponentName who) {
         if (!mHasFeature) {
             return null;
         }
@@ -8689,7 +8693,7 @@
     }
 
     @Override
-    public String getOrganizationNameForUser(int userHandle) {
+    public CharSequence getOrganizationNameForUser(int userHandle) {
         if (!mHasFeature) {
             return null;
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e7ae2b0..8c2f559 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
-import android.app.IAlarmManager;
 import android.app.INotificationManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
@@ -55,13 +54,11 @@
 import com.android.internal.os.ZygoteInit;
 import com.android.internal.widget.ILockSettings;
 import com.android.server.accessibility.AccessibilityManagerService;
-import com.android.server.accounts.AccountManagerService;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.audio.AudioService;
 import com.android.server.camera.CameraService;
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.connectivity.MetricsLoggerService;
-import com.android.server.content.ContentService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
@@ -97,7 +94,6 @@
 import com.android.server.twilight.TwilightService;
 import com.android.server.usage.UsageStatsService;
 import com.android.server.vr.VrManagerService;
-import com.android.server.wallpaper.WallpaperManagerService;
 import com.android.server.webkit.WebViewUpdateService;
 import com.android.server.wm.WindowManagerService;
 
@@ -157,8 +153,12 @@
             "com.google.android.clockwork.ThermalObserver";
     private static final String WEAR_BLUETOOTH_SERVICE_CLASS =
             "com.google.android.clockwork.bluetooth.WearBluetoothService";
+    private static final String ACCOUNT_SERVICE_CLASS =
+            "com.android.server.accounts.AccountManagerService$Lifecycle";
     private static final String CONTENT_SERVICE_CLASS =
             "com.android.server.content.ContentService$Lifecycle";
+    private static final String WALLPAPER_SERVICE_CLASS =
+            "com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -187,6 +187,7 @@
     private PackageManagerService mPackageManagerService;
     private PackageManager mPackageManager;
     private ContentResolver mContentResolver;
+    private EntropyMixer mEntropyMixer;
 
     private boolean mOnlyCore;
     private boolean mFirstBoot;
@@ -497,10 +498,7 @@
      */
     private void startOtherServices() {
         final Context context = mSystemContext;
-        AccountManagerService accountManager = null;
-        ContentService contentService = null;
         VibratorService vibrator = null;
-        IAlarmManager alarm = null;
         IMountService mountService = null;
         NetworkManagementService networkManagement = null;
         NetworkStatsService networkStats = null;
@@ -516,8 +514,6 @@
         TelephonyRegistry telephonyRegistry = null;
         ConsumerIrService consumerIr = null;
         MmsServiceBroker mmsService = null;
-        EntropyMixer entropyMixer = null;
-        VrManagerService vrManagerService = null;
         HardwarePropertiesManagerService hardwarePropertiesService = null;
 
         boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
@@ -556,7 +552,7 @@
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
             traceBeginAndSlog("StartEntropyMixer");
-            entropyMixer = new EntropyMixer(context);
+            mEntropyMixer = new EntropyMixer(context);
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
             mContentResolver = context.getContentResolver();
@@ -566,13 +562,7 @@
 
             // The AccountManager must come before the ContentService
             traceBeginAndSlog("StartAccountManagerService");
-            try {
-                // TODO: seems like this should be disable-able, but req'd by ContentService
-                accountManager = new AccountManagerService(context);
-                ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
-            } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting Account Manager", e);
-            }
+            mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
             traceBeginAndSlog("StartContentService");
@@ -593,9 +583,9 @@
             ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
+            traceBeginAndSlog("StartAlarmManagerService");
             mSystemServiceManager.startService(AlarmManagerService.class);
-            alarm = IAlarmManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ALARM_SERVICE));
+            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
             traceBeginAndSlog("InitWatchdog");
             final Watchdog watchdog = Watchdog.getInstance();
@@ -652,7 +642,6 @@
 
         StatusBarManagerService statusBar = null;
         INotificationManager notification = null;
-        WallpaperManagerService wallpaper = null;
         LocationManagerService location = null;
         CountryDetectorService countryDetector = null;
         ILockSettings lockSettings = null;
@@ -823,7 +812,7 @@
                 mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                 mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
                 mSystemServiceManager.startService(
-                            "com.android.server.wifi.WifiScanningService");
+                            "com.android.server.wifi.scanner.WifiScanningService");
 
                 if (!disableRtt) {
                     mSystemServiceManager.startService("com.android.server.wifi.RttService");
@@ -886,24 +875,6 @@
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
             }
 
-            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeAccountManagerServiceReady");
-            try {
-                if (accountManager != null)
-                    accountManager.systemReady();
-            } catch (Throwable e) {
-                reportWtf("making Account Manager Service ready", e);
-            }
-            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
-            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeContentServiceReady");
-            try {
-                if (contentService != null)
-                    contentService.systemReady();
-            } catch (Throwable e) {
-                reportWtf("making Content Service ready", e);
-            }
-            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
             mSystemServiceManager.startService(NotificationManagerService.class);
             notification = INotificationManager.Stub.asInterface(
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -946,12 +917,7 @@
             if (!disableNonCoreServices && context.getResources().getBoolean(
                         R.bool.config_enableWallpaperService)) {
                 traceBeginAndSlog("StartWallpaperManagerService");
-                try {
-                    wallpaper = new WallpaperManagerService(context);
-                    ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
-                } catch (Throwable e) {
-                    reportWtf("starting Wallpaper Service", e);
-                }
+                mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
             }
 
@@ -1278,12 +1244,10 @@
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
         final ConnectivityService connectivityF = connectivity;
         final NetworkScoreService networkScoreF = networkScore;
-        final WallpaperManagerService wallpaperF = wallpaper;
         final LocationManagerService locationF = location;
         final CountryDetectorService countryDetectorF = countryDetector;
         final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
         final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
-        final StatusBarManagerService statusBarF = statusBar;
         final AssetAtlasService atlasF = atlas;
         final InputManagerService inputManagerF = inputManager;
         final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
@@ -1371,11 +1335,6 @@
                         SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
 
                 try {
-                    if (wallpaperF != null) wallpaperF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying WallpaperService running", e);
-                }
-                try {
                     if (locationF != null) locationF.systemRunning();
                 } catch (Throwable e) {
                     reportWtf("Notifying Location Service running", e);
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 54aeb3d..b6ad25b 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -355,6 +355,8 @@
 
     public void startProvisioning(ProvisioningConfiguration req) {
         getNetworkInterface();
+
+        mCallback.setNeighborDiscoveryOffload(true);
         sendMessage(CMD_START, new ProvisioningConfiguration(req));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 2f20a4b..26eed24 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -70,6 +70,7 @@
 import com.android.internal.util.WakeupMessage;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
 import com.android.server.net.NetworkPinner;
 
 import java.net.InetAddress;
@@ -223,11 +224,15 @@
         private final NetworkCapabilities mNetworkCapabilities;
         private final IdleableHandlerThread mHandlerThread;
         private final ConditionVariable mDisconnected = new ConditionVariable();
+        private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
         private int mScore;
         private NetworkAgent mNetworkAgent;
         private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
         private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
         private Integer mExpectedKeepaliveSlot = null;
+        // Contains the redirectUrl from networkStatus(). Before reading, wait for
+        // mNetworkStatusReceived.
+        private String mRedirectUrl;
 
         MockNetworkAgent(int transport) {
             final int type = transportToLegacyType(transport);
@@ -266,6 +271,12 @@
                 public void stopPacketKeepalive(Message msg) {
                     onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
                 }
+
+                @Override
+                public void networkStatus(int status, String redirectUrl) {
+                    mRedirectUrl = redirectUrl;
+                    mNetworkStatusReceived.open();
+                }
             };
             // Waits for the NetworkAgent to be registered, which includes the creation of the
             // NetworkMonitor.
@@ -340,13 +351,15 @@
             if (callback != null) mCm.unregisterNetworkCallback(callback);
         }
 
-        public void connectWithCaptivePortal() {
+        public void connectWithCaptivePortal(String redirectUrl) {
             mWrappedNetworkMonitor.gen204ProbeResult = 200;
+            mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl;
             connect(false);
             waitFor(new Criteria() { public boolean get() {
                 NetworkCapabilities caps = mCm.getNetworkCapabilities(getNetwork());
                 return caps != null && caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL);} });
             mWrappedNetworkMonitor.gen204ProbeResult = 500;
+            mWrappedNetworkMonitor.gen204ProbeRedirectUrl = null;
         }
 
         public void disconnect() {
@@ -381,6 +394,11 @@
         public void setExpectedKeepaliveSlot(Integer slot) {
             mExpectedKeepaliveSlot = slot;
         }
+
+        public String waitForRedirectUrl() {
+            assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
+            return mRedirectUrl;
+        }
     }
 
     /**
@@ -543,6 +561,7 @@
     private class WrappedNetworkMonitor extends NetworkMonitor {
         // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
         public int gen204ProbeResult = 500;
+        public String gen204ProbeRedirectUrl = null;
 
         public WrappedNetworkMonitor(Context context, Handler handler,
             NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest) {
@@ -550,8 +569,8 @@
         }
 
         @Override
-        protected int isCaptivePortal() {
-            return gen204ProbeResult;
+        protected CaptivePortalProbeResult isCaptivePortal() {
+            return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
         }
 
         @Override
@@ -1344,8 +1363,10 @@
         // Bring up a network with a captive portal.
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connectWithCaptivePortal();
+        String firstRedirectUrl = "http://example.com/firstPath";
+        mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
         captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
+        assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
 
         // Take down network.
         // Expect onLost callback.
@@ -1355,8 +1376,10 @@
         // Bring up a network with a captive portal.
         // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connectWithCaptivePortal();
+        String secondRedirectUrl = "http://example.com/secondPath";
+        mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
         captivePortalCallback.expectCallback(CallbackState.AVAILABLE);
+        assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
 
         // Make captive portal disappear then revalidate.
         // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index dae8447..7d28e39 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -87,6 +87,12 @@
                 return new File(mStorageDir,
                         super.getLockPasswordFilename(userId).replace('/', '-')).getAbsolutePath();
             }
+
+            @Override
+            String getChildProfileLockFile(int userId) {
+                return new File(mStorageDir,
+                        super.getChildProfileLockFile(userId).replace('/', '-')).getAbsolutePath();
+            }
         };
     }
 
@@ -235,6 +241,27 @@
         assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
     }
 
+    public void testLockType_WriteProfileWritesParent() {
+        mStorage.writePasswordHash("parentpassword".getBytes(), 10);
+        mStorage.writePatternHash("12345678".getBytes(), 20);
+
+        assertEquals(2, mStorage.getStoredCredentialType(10));
+        assertEquals(1, mStorage.getStoredCredentialType(20));
+        mStorage.clearCache();
+        assertEquals(2, mStorage.getStoredCredentialType(10));
+        assertEquals(1, mStorage.getStoredCredentialType(20));
+    }
+
+    public void testProfileLock_ReadWriteChildProfileLock() {
+        assertFalse(mStorage.hasChildProfileLock(20));
+        mStorage.writeChildProfileLock(20, "profilepassword".getBytes());
+        assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+        assertTrue(mStorage.hasChildProfileLock(20));
+        mStorage.clearCache();
+        assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+        assertTrue(mStorage.hasChildProfileLock(20));
+    }
+
     public void testPassword_WriteParentWritesProfile() {
         mStorage.writePasswordHash("profilepassword".getBytes(), 2);
         mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 2fd11da..ce02a79 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -216,7 +216,8 @@
         String injectShortcutManagerConstants() {
             return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
                     + ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
-                    + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=" + MAX_DAILY_UPDATES + ","
+                    + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "="
+                    + MAX_UPDATES_PER_INTERVAL + ","
                     + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=" + MAX_ICON_DIMENSION + ","
                     + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "="
                     + MAX_ICON_DIMENSION_LOWRAM + ","
@@ -465,7 +466,7 @@
 
     private static final int MAX_SHORTCUTS = 10;
 
-    private static final int MAX_DAILY_UPDATES = 3;
+    private static final int MAX_UPDATES_PER_INTERVAL = 3;
 
     private static final int MAX_ICON_DIMENSION = 128;
 
@@ -1074,7 +1075,7 @@
 
         assertResetTimes(START_TIME + INTERVAL, START_TIME + 2 * INTERVAL);
 
-        // Advance further; 4 days since start.
+        // Advance further; 4 hours since start.
         mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
 
         assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
@@ -1111,14 +1112,14 @@
         mService.updateConfigurationLocked(
                 ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
                         + ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
-                        + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=5,"
+                        + ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=5,"
                         + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
                         + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
                         + ConfigConstants.KEY_ICON_FORMAT + "=WEBP,"
                         + ConfigConstants.KEY_ICON_QUALITY + "=75");
         assertEquals(123000, mService.getResetIntervalForTest());
         assertEquals(4, mService.getMaxDynamicShortcutsForTest());
-        assertEquals(5, mService.getMaxDailyUpdatesForTest());
+        assertEquals(5, mService.getMaxUpdatesPerIntervalForTest());
         assertEquals(100, mService.getMaxIconDimensionForTest());
         assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
         assertEquals(75, mService.getIconPersistQualityForTest());
@@ -1134,8 +1135,8 @@
         assertEquals(ShortcutService.DEFAULT_MAX_SHORTCUTS_PER_APP,
                 mService.getMaxDynamicShortcutsForTest());
 
-        assertEquals(ShortcutService.DEFAULT_MAX_DAILY_UPDATES,
-                mService.getMaxDailyUpdatesForTest());
+        assertEquals(ShortcutService.DEFAULT_MAX_UPDATES_PER_INTERVAL,
+                mService.getMaxUpdatesPerIntervalForTest());
 
         assertEquals(50, mService.getMaxIconDimensionForTest());
 
@@ -1154,7 +1155,7 @@
 
     /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
     public void testGetRemainingCallCount() {
-        assertEquals(MAX_DAILY_UPDATES, mManager.getRemainingCallCount());
+        assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
     }
 
     /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
@@ -1241,61 +1242,71 @@
                 mManager.getDynamicShortcuts()),
                 "shortcut1");
 
-        assertTrue(mManager.addDynamicShortcut(si2));
+        assertTrue(mManager.addDynamicShortcuts(list(si2, si3)));
         assertEquals(1, mManager.getRemainingCallCount());
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
-                "shortcut1", "shortcut2");
+                "shortcut1", "shortcut2", "shortcut3");
 
-        // Add with the same ID
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("shortcut1")));
+        // This should not crash.  It'll still consume the quota.
+        assertTrue(mManager.addDynamicShortcuts(list()));
         assertEquals(0, mManager.getRemainingCallCount());
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
-                "shortcut1", "shortcut2");
+                "shortcut1", "shortcut2", "shortcut3");
+
+        mInjectedCurrentTimeLillis += INTERVAL; // reset
+
+        // Add with the same ID
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("shortcut1"))));
+        assertEquals(2, mManager.getRemainingCallCount());
+        assertShortcutIds(assertAllNotKeyFieldsOnly(
+                mManager.getDynamicShortcuts()),
+                "shortcut1", "shortcut2", "shortcut3");
 
         // TODO Check max number
 
         // TODO Check fields.
 
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
-            assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+            assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
         });
     }
 
-    public void testDeleteDynamicShortcut() {
+    public void testDeleteDynamicShortcuts() {
         final ShortcutInfo si1 = makeShortcut("shortcut1");
         final ShortcutInfo si2 = makeShortcut("shortcut2");
         final ShortcutInfo si3 = makeShortcut("shortcut3");
+        final ShortcutInfo si4 = makeShortcut("shortcut4");
 
-        assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
+        assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3, si4)));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
-                "shortcut1", "shortcut2", "shortcut3");
+                "shortcut1", "shortcut2", "shortcut3", "shortcut4");
 
         assertEquals(2, mManager.getRemainingCallCount());
 
-        mManager.deleteDynamicShortcut("shortcut1");
+        mManager.removeDynamicShortcuts(list("shortcut1"));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
-                "shortcut2", "shortcut3");
+                "shortcut2", "shortcut3", "shortcut4");
 
-        mManager.deleteDynamicShortcut("shortcut1");
+        mManager.removeDynamicShortcuts(list("shortcut1"));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
-                "shortcut2", "shortcut3");
+                "shortcut2", "shortcut3", "shortcut4");
 
-        mManager.deleteDynamicShortcut("shortcutXXX");
+        mManager.removeDynamicShortcuts(list("shortcutXXX"));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
-                "shortcut2", "shortcut3");
+                "shortcut2", "shortcut3", "shortcut4");
 
-        mManager.deleteDynamicShortcut("shortcut2");
+        mManager.removeDynamicShortcuts(list("shortcut2", "shortcut4"));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()),
                 "shortcut3");
 
-        mManager.deleteDynamicShortcut("shortcut3");
+        mManager.removeDynamicShortcuts(list("shortcut3"));
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mManager.getDynamicShortcuts()));
 
@@ -1315,7 +1326,7 @@
 
         assertEquals(2, mManager.getRemainingCallCount());
 
-        mManager.deleteAllDynamicShortcuts();
+        mManager.removeAllDynamicShortcuts();
         assertEquals(0, mManager.getDynamicShortcuts().size());
         assertEquals(2, mManager.getRemainingCallCount());
 
@@ -1383,7 +1394,7 @@
         assertEquals(0, mManager.getRemainingCallCount());
         assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
 
-        // 4 days later...
+        // 4 hours later...
         mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
         assertTrue(mManager.setDynamicShortcuts(list(si1)));
         assertEquals(2, mManager.getRemainingCallCount());
@@ -1791,13 +1802,13 @@
                     getCallingUser());
         });
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
-            mManager.deleteDynamicShortcut("s1");
-            mManager.deleteDynamicShortcut("s2");
+            mManager.removeDynamicShortcuts(list("s1"));
+            mManager.removeDynamicShortcuts(list("s2"));
         });
         runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
-            mManager.deleteDynamicShortcut("s1");
-            mManager.deleteDynamicShortcut("s3");
-            mManager.deleteDynamicShortcut("s5");
+            mManager.removeDynamicShortcuts(list("s1"));
+            mManager.removeDynamicShortcuts(list("s3"));
+            mManager.removeDynamicShortcuts(list("s5"));
         });
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
             assertShortcutIds(assertAllDynamic(
@@ -2089,7 +2100,7 @@
         // Delete some.
         setCaller(CALLING_PACKAGE_1);
         assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
-        mManager.deleteDynamicShortcut("s2");
+        mManager.removeDynamicShortcuts(list("s2"));
         assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
 
         dumpsysOnLogcat();
@@ -2154,19 +2165,19 @@
         // Delete some.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
-            mManager.deleteDynamicShortcut("s2");
+            mManager.removeDynamicShortcuts(list("s2"));
             assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
         });
 
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
             assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
-            mManager.deleteDynamicShortcut("s3");
+            mManager.removeDynamicShortcuts(list("s3"));
             assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
         });
 
         runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
             assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
-            mManager.deleteDynamicShortcut("s2");
+            mManager.removeDynamicShortcuts(list("s2"));
             assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
         });
 
@@ -2218,7 +2229,7 @@
         // Delete some.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
-            mManager.deleteDynamicShortcut("s3");
+            mManager.removeDynamicShortcuts(list("s3"));
             assertShortcutIds(mManager.getPinnedShortcuts(), "s3");
         });
 
@@ -2226,8 +2237,8 @@
 
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
             assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
-            mManager.deleteDynamicShortcut("s1");
-            mManager.deleteDynamicShortcut("s3");
+            mManager.removeDynamicShortcuts(list("s1"));
+            mManager.removeDynamicShortcuts(list("s3"));
             assertShortcutIds(mManager.getPinnedShortcuts(), "s1", "s2");
         });
 
@@ -2336,13 +2347,13 @@
 
         // Delete all dynamic.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
 
             assertEquals(0, mManager.getDynamicShortcuts().size());
             assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
         });
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
 
             assertEquals(0, mManager.getDynamicShortcuts().size());
             assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s2", "s1");
@@ -2377,7 +2388,7 @@
         });
         // Re-publish s1.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+            assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
             assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()), "s1");
             assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()), "s1", "s2", "s3");
@@ -2979,7 +2990,7 @@
 
         // Just to make it complicated, delete some.
         setCaller(CALLING_PACKAGE_1);
-        mManager.deleteDynamicShortcut("s2");
+        mManager.removeDynamicShortcuts(list("s2"));
 
         // intent and check.
         setCaller(LAUNCHER_1);
@@ -3051,11 +3062,11 @@
                 any(UserHandle.class)
         );
 
-        // Test for addDynamicShortcut.
+        // Test for addDynamicShortcuts.
         reset(c0);
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
-            dumpsysOnLogcat("before addDynamicShortcut");
-            assertTrue(mManager.addDynamicShortcut(makeShortcut("s4")));
+            dumpsysOnLogcat("before addDynamicShortcuts");
+            assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s4"))));
         });
 
         waitOnMainThread();
@@ -3071,7 +3082,7 @@
         // Test for remove
         reset(c0);
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
-            mManager.deleteDynamicShortcut("s1");
+            mManager.removeDynamicShortcuts(list("s1"));
         });
 
         waitOnMainThread();
@@ -3104,7 +3115,7 @@
         // Test for deleteAll
         reset(c0);
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
         });
 
         waitOnMainThread();
@@ -3178,7 +3189,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3195,7 +3206,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3213,7 +3224,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3233,7 +3244,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3253,7 +3264,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3477,16 +3488,16 @@
 
         // Remove all dynamic shortcuts; now all shortcuts are just pinned.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
         });
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
         });
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
         });
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
         });
 
 
@@ -4009,22 +4020,22 @@
 
     public void testHandlePackageDelete() {
         setCaller(CALLING_PACKAGE_1, USER_0);
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
         setCaller(CALLING_PACKAGE_2, USER_0);
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
         setCaller(CALLING_PACKAGE_3, USER_0);
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
         setCaller(CALLING_PACKAGE_1, USER_10);
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
         setCaller(CALLING_PACKAGE_2, USER_10);
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
         setCaller(CALLING_PACKAGE_3, USER_10);
-        assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcut("s1"))));
 
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
@@ -4980,6 +4991,7 @@
                 .setTitle("title")
                 .setText("text")
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setWeight(123)
                 .setExtras(pb)
                 .build();
@@ -4995,6 +5007,7 @@
         assertEquals("content://a.b.c/", si.getIcon().getUriString());
         assertEquals("title", si.getTitle());
         assertEquals("text", si.getText());
+        assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getWeight());
@@ -5016,6 +5029,7 @@
                 .setIcon(Icon.createWithContentUri("content://a.b.c/"))
                 .setTitle("title")
                 .setText("text")
+                .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setWeight(123)
                 .setExtras(pb)
@@ -5034,6 +5048,7 @@
         assertEquals("content://a.b.c/", si.getIcon().getUriString());
         assertEquals("title", si.getTitle());
         assertEquals("text", si.getText());
+        assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getWeight());
@@ -5051,6 +5066,7 @@
         assertEquals(null, si.getIcon());
         assertEquals("title", si.getTitle());
         assertEquals("text", si.getText());
+        assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getWeight());
@@ -5058,7 +5074,8 @@
 
         assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
         assertEquals(null, si.getBitmapPath());
-        assertEquals(0, si.getIconResourceId());
+
+        assertEquals(456, si.getIconResourceId());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
 
@@ -5068,13 +5085,15 @@
         assertEquals(null, si.getIcon());
         assertEquals("title", si.getTitle());
         assertEquals("text", si.getText());
+        assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals(null, si.getIntent());
         assertEquals(123, si.getWeight());
         assertEquals(1, si.getExtras().getInt("k"));
 
         assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
         assertEquals(null, si.getBitmapPath());
-        assertEquals(0, si.getIconResourceId());
+
+        assertEquals(456, si.getIconResourceId());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
 
@@ -5084,13 +5103,56 @@
         assertEquals(null, si.getIcon());
         assertEquals(null, si.getTitle());
         assertEquals(null, si.getText());
+        assertEquals(null, si.getCategories());
         assertEquals(null, si.getIntent());
         assertEquals(0, si.getWeight());
         assertEquals(null, si.getExtras());
 
         assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
         assertEquals(null, si.getBitmapPath());
-        assertEquals(0, si.getIconResourceId());
+
+        assertEquals(456, si.getIconResourceId());
+    }
+
+    public void testShortcutInfoClone_minimum() {
+        PersistableBundle pb = new PersistableBundle();
+        pb.putInt("k", 1);
+        ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+                .setId("id")
+                .setTitle("title")
+                .setIntent(makeIntent("action", ShortcutActivity.class))
+                .build();
+        ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals("title", si.getTitle());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals(null, si.getCategories());
+
+        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals("title", si.getTitle());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals(null, si.getCategories());
+
+        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals("title", si.getTitle());
+        assertEquals(null, si.getIntent());
+        assertEquals(null, si.getCategories());
+
+        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(null, si.getTitle());
+        assertEquals(null, si.getIntent());
+        assertEquals(null, si.getCategories());
     }
 
     public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException {
@@ -5102,6 +5164,7 @@
                 .setIcon(Icon.createWithContentUri("content://a.b.c/"))
                 .setTitle("title")
                 .setText("text")
+                .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setWeight(123)
                 .setExtras(pb)
@@ -5115,38 +5178,57 @@
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setActivityComponent(new ComponentName("x", "y")).build());
+        assertEquals("text", si.getText());
         assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
+        assertEquals("text", si.getText());
         assertEquals("content://x.y.z/", si.getIcon().getUriString());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setTitle("xyz").build());
+        assertEquals("text", si.getText());
         assertEquals("xyz", si.getTitle());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setText("xxx").build());
+        assertEquals(123, si.getWeight());
         assertEquals("xxx", si.getText());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setCategories(list()).build());
+        assertEquals("text", si.getText());
+        assertEquals(list(), si.getCategories());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setCategories(list("x")).build());
+        assertEquals("text", si.getText());
+        assertEquals(list("x"), si.getCategories());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setIntent(makeIntent("action2", ShortcutActivity.class)).build());
+        assertEquals("text", si.getText());
         assertEquals("action2", si.getIntent().getAction());
         assertEquals(null, si.getIntent().getStringExtra("key"));
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build());
+        assertEquals("text", si.getText());
         assertEquals("action3", si.getIntent().getAction());
         assertEquals("x", si.getIntent().getStringExtra("key"));
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setWeight(999).build());
+        assertEquals("text", si.getText());
         assertEquals(999, si.getWeight());
 
 
@@ -5156,8 +5238,11 @@
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
                 .setExtras(pb2).build());
+        assertEquals("text", si.getText());
         assertEquals(99, si.getExtras().getInt("x"));
 
+        // Make sure the timestamp gets updated too.
+
         final long timestamp = si.getLastChangedTimestamp();
         Thread.sleep(2);
 
@@ -5181,12 +5266,13 @@
                 .setIcon(bmp32x32)
                 .setTitle("title")
                 .setText("text")
+                .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setWeight(123)
                 .setExtras(pb)
                 .build();
 
-        mManager.addDynamicShortcut(sorig);
+        mManager.addDynamicShortcuts(list(sorig));
 
         Thread.sleep(2);
         final long now = System.currentTimeMillis();
@@ -5207,6 +5293,7 @@
         assertEquals(null, si.getIcon());
         assertEquals("title", si.getTitle());
         assertEquals("text", si.getText());
+        assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getWeight());
@@ -5232,12 +5319,13 @@
                 .setIcon(bmp32x32)
                 .setTitle("title")
                 .setText("text")
+                .setCategories(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
                 .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
                 .setWeight(123)
                 .setExtras(pb)
                 .build();
 
-        mManager.addDynamicShortcut(sorig);
+        mManager.addDynamicShortcuts(list(sorig));
 
         // Dynamic shortcuts won't be backed up, so we need to pin it.
         setCaller(LAUNCHER_1, USER_0);
@@ -5246,6 +5334,8 @@
         // Do backup & restore.
         backupAndRestore();
 
+        mService.handleUnlockUser(USER_0); // Load user-0.
+
         ShortcutInfo si;
         si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
 
@@ -5255,6 +5345,7 @@
         assertEquals(null, si.getIcon());
         assertEquals("title", si.getTitle());
         assertEquals("text", si.getText());
+        assertEquals(list(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getWeight());
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
new file mode 100644
index 0000000..26b87c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.webkit.WebViewProviderInfo;
+
+import java.util.HashMap;
+
+public class TestSystemImpl implements SystemInterface {
+    private String mUserProvider = "";
+    private final WebViewProviderInfo[] mPackageConfigs;
+    HashMap<String, PackageInfo> mPackages = new HashMap();
+    private boolean mFallbackLogicEnabled;
+    private final int mNumRelros;
+    private final boolean mIsDebuggable;
+
+    public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
+            int numRelros, boolean isDebuggable) {
+        mPackageConfigs = packageConfigs;
+        mFallbackLogicEnabled = fallbackLogicEnabled;
+        mNumRelros = numRelros;
+        mIsDebuggable = isDebuggable;
+    }
+
+    @Override
+    public WebViewProviderInfo[] getWebViewPackages() {
+        return mPackageConfigs;
+    }
+
+    @Override
+    public int onWebViewProviderChanged(PackageInfo packageInfo) {
+        return mNumRelros;
+    }
+
+    @Override
+    public String getUserChosenWebViewProvider(Context context) { return mUserProvider; }
+
+    @Override
+    public void updateUserSetting(Context context, String newProviderName) {
+        mUserProvider = newProviderName;
+    }
+
+    @Override
+    public void killPackageDependents(String packageName) {}
+
+    @Override
+    public boolean isFallbackLogicEnabled() {
+        return mFallbackLogicEnabled;
+    }
+
+    @Override
+    public void enableFallbackLogic(boolean enable) {
+        mFallbackLogicEnabled = enable;
+    }
+
+    @Override
+    public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
+        enablePackageForAllUsers(context, packageName, false);
+    }
+
+    @Override
+    public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
+        enablePackageForUser(packageName, enable, 0);
+    }
+
+    @Override
+    public void enablePackageForUser(String packageName, boolean enable, int userId) {
+        PackageInfo packageInfo = mPackages.get(packageName);
+        if (packageInfo == null) {
+            throw new IllegalArgumentException("There is no package called " + packageName);
+        }
+        packageInfo.applicationInfo.enabled = enable;
+        setPackageInfo(packageInfo);
+    }
+
+    @Override
+    public boolean systemIsDebuggable() { return mIsDebuggable; }
+
+    @Override
+    public PackageInfo getPackageInfoForProvider(WebViewProviderInfo info) throws
+            NameNotFoundException {
+        PackageInfo ret = mPackages.get(info.packageName);
+        if (ret == null) throw new NameNotFoundException(info.packageName);
+        return ret;
+    }
+
+    public void setPackageInfo(PackageInfo pi) {
+        mPackages.put(pi.packageName, pi);
+    }
+
+    @Override
+    public int getFactoryPackageVersion(String packageName) {
+        return 0;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
new file mode 100644
index 0000000..c00520d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.os.Bundle;
+import android.util.Base64;
+import android.test.AndroidTestCase;
+
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.hamcrest.Description;
+
+import org.mockito.Mockito;
+import org.mockito.Matchers;
+import org.mockito.ArgumentMatcher;
+
+
+/**
+ * Tests for WebViewUpdateService
+ */
+public class WebViewUpdateServiceTest extends AndroidTestCase {
+    private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
+
+    private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
+    private TestSystemImpl mTestSystemImpl;
+
+    private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    /**
+     * Creates a new instance.
+     */
+    public WebViewUpdateServiceTest() {
+    }
+
+    private void setupWithPackages(WebViewProviderInfo[] packages) {
+        setupWithPackages(packages, true);
+    }
+
+    private void setupWithPackages(WebViewProviderInfo[] packages,
+            boolean fallbackLogicEnabled) {
+        setupWithPackages(packages, fallbackLogicEnabled, 1);
+    }
+
+    private void setupWithPackages(WebViewProviderInfo[] packages,
+            boolean fallbackLogicEnabled, int numRelros) {
+        setupWithPackages(packages, fallbackLogicEnabled, numRelros,
+                true /* isDebuggable == true -> don't check package signatures */);
+    }
+
+    private void setupWithPackages(WebViewProviderInfo[] packages,
+            boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) {
+        TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
+                isDebuggable);
+        mTestSystemImpl = Mockito.spy(testing);
+        mWebViewUpdateServiceImpl =
+            new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+    }
+
+    private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
+        for(WebViewProviderInfo wpi : providers) {
+            mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
+                        true /* valid */));
+        }
+    }
+
+    private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
+            WebViewProviderInfo[] webviewPackages) {
+        checkCertainPackageUsedAfterWebViewPreparation(expectedProviderName, webviewPackages, 1);
+    }
+
+    private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
+            WebViewProviderInfo[] webviewPackages, int numRelros) {
+        setupWithPackages(webviewPackages, true, numRelros);
+        // Add (enabled and valid) package infos for each provider
+        setEnabledAndValidPackageInfos(webviewPackages);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
+
+        for (int n = 0; n < numRelros; n++) {
+            mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+        }
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+        assertEquals(expectedProviderName, response.packageInfo.packageName);
+    }
+
+    // For matching the package name of a PackageInfo
+    private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> {
+        private final String mPackageName;
+
+        IsPackageInfoWithName(String name) {
+            mPackageName = name;
+        }
+
+        @Override
+        public boolean matches(Object p) {
+            return ((PackageInfo) p).packageName.equals(mPackageName);
+        }
+
+        // Provide a more useful description in case of mismatch
+        @Override
+        public void describeTo (Description description) {
+            description.appendText(String.format("PackageInfo with name '%s'", mPackageName));
+        }
+    }
+
+    private static PackageInfo createPackageInfo(
+            String packageName, boolean enabled, boolean valid) {
+        PackageInfo p = new PackageInfo();
+        p.packageName = packageName;
+        p.applicationInfo = new ApplicationInfo();
+        p.applicationInfo.enabled = enabled;
+        p.applicationInfo.metaData = new Bundle();
+        if (valid) {
+            // no flag means invalid
+            p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
+        }
+        return p;
+    }
+
+    private static PackageInfo createPackageInfo(
+            String packageName, boolean enabled, boolean valid, Signature[] signatures) {
+        PackageInfo p = createPackageInfo(packageName, enabled, valid);
+        p.signatures = signatures;
+        return p;
+    }
+
+
+    // ****************
+    // Tests
+    // ****************
+
+
+    public void testWithSinglePackage() {
+        String testPackageName = "test.package.name";
+        checkCertainPackageUsedAfterWebViewPreparation(testPackageName,
+                new WebViewProviderInfo[] {
+                    new WebViewProviderInfo(testPackageName, "",
+                            true /*default available*/, false /* fallback */, null)});
+    }
+
+    public void testDefaultPackageUsedOverNonDefault() {
+        String defaultPackage = "defaultPackage";
+        String nonDefaultPackage = "nonDefaultPackage";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+            new WebViewProviderInfo(defaultPackage, "", true, false, null)};
+        checkCertainPackageUsedAfterWebViewPreparation(defaultPackage, packages);
+    }
+
+    public void testSeveralRelros() {
+        String singlePackage = "singlePackage";
+        checkCertainPackageUsedAfterWebViewPreparation(
+                singlePackage,
+                new WebViewProviderInfo[] {
+                    new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
+                2);
+    }
+
+    // Ensure that package with valid signatures is chosen rather than package with invalid
+    // signatures.
+    public void testWithSignatures() {
+        String validPackage = "valid package";
+        String invalidPackage = "invalid package";
+
+        Signature validSignature = new Signature("11");
+        Signature invalidExpectedSignature = new Signature("22");
+        Signature invalidPackageSignature = new Signature("33");
+
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
+                        Base64.encodeToString(
+                                invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
+            new WebViewProviderInfo(validPackage, "", true, false, new String[]{
+                        Base64.encodeToString(
+                                validSignature.toByteArray(), Base64.DEFAULT)})
+        };
+        setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
+                false /* isDebuggable */);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
+                    true /* valid */, new Signature[]{invalidPackageSignature}));
+        mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
+                    true /* valid */, new Signature[]{validSignature}));
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(validPackage)));
+
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+        assertEquals(validPackage, response.packageInfo.packageName);
+
+        WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
+        assertEquals(1, validPackages.length);
+        assertEquals(validPackage, validPackages[0].packageName);
+    }
+
+    public void testFailWaitingForRelro() {
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo("packagename", "", true, true, null)};
+        setupWithPackages(packages);
+        setEnabledAndValidPackageInfos(packages);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
+
+        // Never call notifyRelroCreation()
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
+    }
+
+    public void testFailListingEmptyWebviewPackages() {
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
+        setupWithPackages(packages);
+        setEnabledAndValidPackageInfos(packages);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
+                Matchers.anyObject());
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+    }
+
+    public void testFailListingInvalidWebviewPackage() {
+        WebViewProviderInfo wpi = new WebViewProviderInfo("", "", true, true, null);
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
+        setupWithPackages(packages);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true, false));
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+    }
+
+    // Test that switching provider using changeProviderAndSetting works.
+    public void testSwitchingProvider() {
+        String firstPackage = "first";
+        String secondPackage = "second";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(firstPackage, "", true, false, null),
+            new WebViewProviderInfo(secondPackage, "", true, false, null)};
+        checkSwitchingProvider(packages, firstPackage, secondPackage);
+    }
+
+    public void testSwitchingProviderToNonDefault() {
+        String defaultPackage = "defaultPackage";
+        String nonDefaultPackage = "nonDefaultPackage";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(defaultPackage, "", true, false, null),
+            new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
+        checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
+    }
+
+    private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
+            String finalPackage) {
+        checkCertainPackageUsedAfterWebViewPreparation(initialPackage, packages);
+
+        mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(finalPackage)));
+
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+        WebViewProviderResponse secondResponse = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, secondResponse.status);
+        assertEquals(finalPackage, secondResponse.packageInfo.packageName);
+
+        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
+    }
+
+    // Change provider during relro creation by using changeProviderAndSetting
+    public void testSwitchingProviderDuringRelroCreation() {
+        checkChangingProviderDuringRelroCreation(true);
+    }
+
+    // Change provider during relro creation by enabling a provider
+    public void testChangingProviderThroughEnablingDuringRelroCreation() {
+        checkChangingProviderDuringRelroCreation(false);
+    }
+
+    private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
+        String firstPackage = "first";
+        String secondPackage = "second";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(firstPackage, "", true, false, null),
+            new WebViewProviderInfo(secondPackage, "", true, false, null)};
+        setupWithPackages(packages);
+        if (settingsChange) {
+            // Have all packages be enabled, so that we can change provider however we want to
+            setEnabledAndValidPackageInfos(packages);
+        } else {
+            // Have all packages be disabled so that we can change one to enabled later
+            for(WebViewProviderInfo wpi : packages) {
+                mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
+                            false /* enabled */, true /* valid */));
+            }
+        }
+
+        CountDownLatch countdown = new CountDownLatch(1);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+        assertEquals(firstPackage, mWebViewUpdateServiceImpl.getCurrentWebViewPackageName());
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                WebViewProviderResponse threadResponse =
+                    mWebViewUpdateServiceImpl.waitForAndGetProvider();
+                assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
+                assertEquals(secondPackage, threadResponse.packageInfo.packageName);
+                // Verify that we killed the first package
+                Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
+                countdown.countDown();
+            }
+        }).start();
+        try {
+            Thread.sleep(1000); // Let the new thread run / be blocked
+        } catch (InterruptedException e) {
+        }
+
+        if (settingsChange) {
+            mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+        } else {
+            // Switch provider by enabling the second one
+            mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+                        true /* valid */));
+            mWebViewUpdateServiceImpl.packageStateChanged(
+                    secondPackage, WebViewUpdateService.PACKAGE_CHANGED);
+        }
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+        // first package done, should start on second
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
+
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+        // second package done, the other thread should now be unblocked
+        try {
+            countdown.await();
+        } catch (InterruptedException e) {
+        }
+    }
+
+    public void testRunFallbackLogicIfEnabled() {
+        checkFallbackLogicBeingRun(true);
+    }
+
+    public void testDontRunFallbackLogicIfDisabled() {
+        checkFallbackLogicBeingRun(false);
+    }
+
+    private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) {
+        String primaryPackage = "primary";
+        String fallbackPackage = "fallback";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null),
+            new WebViewProviderInfo(
+                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+        setupWithPackages(packages, fallbackLogicEnabled);
+        setEnabledAndValidPackageInfos(packages);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        // Verify that we disable the fallback package if fallback logic enabled, and don't disable
+        // the fallback package if that logic is disabled
+        if (fallbackLogicEnabled) {
+            Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+                    Matchers.anyObject(), Mockito.eq(fallbackPackage));
+        } else {
+            Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+                    Matchers.anyObject(), Matchers.anyObject());
+        }
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+
+        // Enable fallback package
+        mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
+                        true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(
+                fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED);
+
+        if (fallbackLogicEnabled) {
+            // Check that we have now disabled the fallback package twice
+            Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers(
+                    Matchers.anyObject(), Mockito.eq(fallbackPackage));
+        } else {
+            // Check that we still haven't disabled any package
+            Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+                    Matchers.anyObject(), Matchers.anyObject());
+        }
+    }
+
+    /**
+     * Scenario for installing primary package when fallback enabled.
+     * 1. Start with only fallback installed
+     * 2. Install non-fallback
+     * 3. Fallback should be disabled
+     */
+    public void testInstallingNonFallbackPackage() {
+        String primaryPackage = "primary";
+        String fallbackPackage = "fallback";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null),
+            new WebViewProviderInfo(
+                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+        setupWithPackages(packages, true /* isFallbackLogicEnabled */);
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */));
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+                Matchers.anyObject(), Matchers.anyObject());
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(fallbackPackage)));
+
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+        assertEquals(fallbackPackage, response.packageInfo.packageName);
+
+        // Install primary package
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_ADDED);
+
+        // Verify fallback disabled and primary package used as provider
+        Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+                Matchers.anyObject(), Mockito.eq(fallbackPackage));
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+
+        // Finish the webview preparation and ensure primary package used and fallback killed
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+        response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+        assertEquals(primaryPackage, response.packageInfo.packageName);
+        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
+    }
+
+    public void testFallbackChangesEnabledState() {
+        String primaryPackage = "primary";
+        String fallbackPackage = "fallback";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null),
+            new WebViewProviderInfo(
+                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+        setupWithPackages(packages, true /* fallbackLogicEnabled */);
+        setEnabledAndValidPackageInfos(packages);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        // Verify fallback disabled at boot when primary package enabled
+        Mockito.verify(mTestSystemImpl).enablePackageForUser(
+                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+                Matchers.anyInt());
+
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_CHANGED);
+
+        // Verify fallback becomes enabled when primary package becomes disabled
+        Mockito.verify(mTestSystemImpl).enablePackageForUser(
+                Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
+                Matchers.anyInt());
+
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_CHANGED);
+
+        // Verify fallback is disabled a second time when primary package becomes enabled
+        Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
+                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+                Matchers.anyInt());
+    }
+
+    public void testAddUserWhenFallbackLogicEnabled() {
+        checkAddingNewUser(true);
+    }
+
+    public void testAddUserWhenFallbackLogicDisabled() {
+        checkAddingNewUser(false);
+    }
+
+    public void checkAddingNewUser(boolean fallbackLogicEnabled) {
+        String primaryPackage = "primary";
+        String fallbackPackage = "fallback";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null),
+            new WebViewProviderInfo(
+                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+        setupWithPackages(packages, fallbackLogicEnabled);
+        setEnabledAndValidPackageInfos(packages);
+        int newUser = 100;
+        mWebViewUpdateServiceImpl.handleNewUser(newUser);
+        if (fallbackLogicEnabled) {
+            // Verify fallback package becomes disabled for new user
+            Mockito.verify(mTestSystemImpl).enablePackageForUser(
+                    Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+                    Mockito.eq(newUser));
+        } else {
+            // Verify that we don't disable fallback for new user
+            Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
+                    Mockito.anyObject(), Matchers.anyBoolean() /* enable */,
+                    Matchers.anyInt() /* user */);
+        }
+    }
+
+    /**
+     * Timing dependent test where we verify that the list of valid webview packages becoming empty
+     * at a certain point doesn't crash us or break our state.
+     */
+    public void testNotifyRelroDoesntCrashIfNoPackages() {
+        String firstPackage = "first";
+        String secondPackage = "second";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(firstPackage, "", true /* default available */,
+                    false /* fallback */, null),
+            new WebViewProviderInfo(secondPackage, "", true /* default available */,
+                    false /* fallback */, null)};
+        setupWithPackages(packages);
+        // Add (enabled and valid) package infos for each provider
+        setEnabledAndValidPackageInfos(packages);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+
+        // Make packages invalid to cause exception to be thrown
+        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+                    false /* valid */));
+        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+                    false /* valid */));
+
+        // This shouldn't throw an exception!
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+
+        // Now make a package valid again and verify that we can switch back to that
+        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+                    true /* valid */));
+
+        mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
+                WebViewUpdateService.PACKAGE_ADDED);
+
+        // Second time we call onWebViewProviderChanged for firstPackage
+        Mockito.verify(mTestSystemImpl, Mockito.times(2)).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+        response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+        assertEquals(firstPackage, response.packageInfo.packageName);
+    }
+
+    // TODO (gsennton) add more tests for ensuring killPackageDependents is called / not called
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index adc7c21..c18c13c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -426,6 +426,13 @@
     public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
             "carrier_metered_apn_types_strings";
 
+    /**
+     * CDMA carrier ERI (Enhanced Roaming Indicator) file name
+     * @hide
+     */
+    public static final String KEY_CARRIER_ERI_FILE_NAME_STRING =
+            "carrier_eri_file_name_string";
+
     /* The following 3 fields are related to carrier visual voicemail. */
 
     /**
@@ -705,6 +712,7 @@
                 "max_retries=3, 5000, 5000, 5000");
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
+        sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
         sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
         sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{"default", "mms", "dun", "supl"});
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 865af78..638b286 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -717,16 +717,11 @@
      */
     /** {@hide} */
     public String getDeviceSoftwareVersion(int slotId) {
-        // FIXME methods taking slot id should not use subscription, instead us Uicc directly
-        int[] subId = SubscriptionManager.getSubId(slotId);
-        if (subId == null || subId.length == 0) {
-            return null;
-        }
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
-            if (info == null)
-                return null;
-            return info.getDeviceSvnUsingSubId(subId[0], mContext.getOpPackageName());
+            return telephony.getDeviceSoftwareVersionForSlot(slotId, getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -798,12 +793,11 @@
      */
     /** {@hide} */
     public String getImei(int slotId) {
-        int[] subId = SubscriptionManager.getSubId(slotId);
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
         try {
-            IPhoneSubInfo info = getSubscriberInfo();
-            if (info == null)
-                return null;
-            return info.getImeiForSubscriber(subId[0], mContext.getOpPackageName());
+            return telephony.getImeiForSlot(slotId, getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -1014,22 +1008,32 @@
         } else {
             phoneId = SubscriptionManager.getPhoneId(subId);
         }
+
+        return getCurrentPhoneTypeForSlot(phoneId);
+    }
+
+    /**
+     * See getCurrentPhoneType.
+     *
+     * @hide
+     */
+    public int getCurrentPhoneTypeForSlot(int slotId) {
         try{
             ITelephony telephony = getITelephony();
-            if (telephony != null && subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                return telephony.getActivePhoneTypeForSubscriber(subId);
+            if (telephony != null) {
+                return telephony.getActivePhoneTypeForSlot(slotId);
             } else {
                 // This can happen when the ITelephony interface is not up yet.
-                return getPhoneTypeFromProperty(phoneId);
+                return getPhoneTypeFromProperty(slotId);
             }
         } catch (RemoteException ex) {
             // This shouldn't happen in the normal case, as a backup we
             // read from the system property.
-            return getPhoneTypeFromProperty(phoneId);
+            return getPhoneTypeFromProperty(slotId);
         } catch (NullPointerException ex) {
             // This shouldn't happen in the normal case, as a backup we
             // read from the system property.
-            return getPhoneTypeFromProperty(phoneId);
+            return getPhoneTypeFromProperty(slotId);
         }
     }
 
@@ -2555,20 +2559,31 @@
      * @param subId whose call state is returned
      */
     public int getCallState(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getCallStateForSlot(phoneId);
+    }
+
+    /**
+     * See getCallState.
+     *
+     * @hide
+     */
+    public int getCallStateForSlot(int slotId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return CALL_STATE_IDLE;
-            return telephony.getCallStateForSubscriber(subId);
+            return telephony.getCallStateForSlot(slotId);
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return CALL_STATE_IDLE;
         } catch (NullPointerException ex) {
           // the phone process is restarting.
           return CALL_STATE_IDLE;
-      }
+        }
     }
 
+
     /** Data connection activity: No traffic. */
     public static final int DATA_ACTIVITY_NONE = 0x00000000;
     /** Data connection activity: Currently receiving IP PPP traffic. */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2727319..b41d361 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -360,9 +360,9 @@
      int getCallState();
 
     /**
-     * Returns the call state for a subId.
+     * Returns the call state for a slot.
      */
-     int getCallStateForSubscriber(int subId);
+     int getCallStateForSlot(int slotId);
 
      int getDataActivity();
      int getDataState();
@@ -375,12 +375,12 @@
     int getActivePhoneType();
 
     /**
-     * Returns the current active phone type as integer for particular subId.
+     * Returns the current active phone type as integer for particular slot.
      * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE
      * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE
-     * @param subId user preferred subId.
+     * @param slotId - slot to query.
      */
-    int getActivePhoneTypeForSubscriber(int subId);
+    int getActivePhoneTypeForSlot(int slotId);
 
     /**
      * Returns the CDMA ERI icon index to display
@@ -992,6 +992,26 @@
     String getDeviceId(String callingPackage);
 
     /**
+     * Returns the IMEI for the given slot.
+     *
+     * @param slotId - device slot.
+     * @param callingPackage The package making the call.
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     */
+    String getImeiForSlot(int slotId, String callingPackage);
+
+    /**
+     * Returns the device software version.
+     *
+     * @param slotId - device slot.
+     * @param callingPackage The package making the call.
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     */
+    String getDeviceSoftwareVersionForSlot(int slotId, String callingPackage);
+
+    /**
      * Returns the subscription ID associated with the specified PhoneAccount.
      */
     int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 91e891f..2e5ed3f 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -691,6 +691,15 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * @hide - to match hiding in superclass
+     */
+    @Override
+    public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+            IPackageDataObserver observer) {
+        throw new UnsupportedOperationException();
+    }
+
     /** {@hide} */
     @Override
     public void freeStorageAndNotify(String volumeUuid, long idealStorageSize,
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 18fd985..b9e9ac8 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -374,6 +374,15 @@
         </activity>
 
         <activity
+                android:name="GetBitmapSurfaceViewActivity"
+                android:label="SurfaceView/GetBitmap with Camera source">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="GLTextureViewActivity"
                 android:label="TextureView/OpenGL">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
new file mode 100644
index 0000000..d3cd7db
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.PixelCopy;
+import android.graphics.PixelCopy.OnPixelCopyFinished;
+import android.graphics.PixelCopy.Response;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class GetBitmapSurfaceViewActivity extends Activity implements SurfaceHolder.Callback {
+    private Camera mCamera;
+    private SurfaceView mSurfaceView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        FrameLayout content = new FrameLayout(this);
+
+        mSurfaceView = new SurfaceView(this);
+        mSurfaceView.getHolder().addCallback(this);
+
+        Button button = new Button(this);
+        button.setText("Copy bitmap to /sdcard/surfaceview.png");
+        button.setOnClickListener((View v) -> {
+            Bitmap b = Bitmap.createBitmap(
+                            mSurfaceView.getWidth(),
+                            mSurfaceView.getHeight(),
+                            Bitmap.Config.ARGB_8888);
+            PixelCopy.request(mSurfaceView, b,
+                    mOnCopyFinished, mSurfaceView.getHandler());
+        });
+
+        content.addView(mSurfaceView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+        content.addView(button, new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
+                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+        setContentView(content);
+    }
+
+    private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
+        @Override
+        public void onPixelCopyFinished(Response response) {
+            if (!response.success) {
+                Toast.makeText(GetBitmapSurfaceViewActivity.this,
+                        "Failed to copy", Toast.LENGTH_SHORT).show();
+                return;
+            }
+            try {
+                try (FileOutputStream out = new FileOutputStream(
+                        Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+                    response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    };
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        mCamera = Camera.open();
+
+        try {
+            mCamera.setPreviewSurface(holder.getSurface());
+        } catch (IOException t) {
+            android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
+        }
+
+        mCamera.startPreview();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        mCamera.stopPreview();
+        mCamera.release();
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
index b1431c5..5c30fab 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
@@ -17,18 +17,29 @@
 package com.android.test.hwui;
 
 import android.app.Activity;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.PixelCopy;
+import android.graphics.PixelCopy.OnPixelCopyFinished;
+import android.graphics.PixelCopy.Response;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
-import android.view.Gravity;
+import android.os.Environment;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceHolder.Callback;
 import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Toast;
 
-@SuppressWarnings({"UnusedDeclaration"})
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
 public class HardwareCanvasSurfaceViewActivity extends Activity implements Callback {
     private SurfaceView mSurfaceView;
     private HardwareCanvasSurfaceViewActivity.RenderingThread mThread;
@@ -42,13 +53,49 @@
         mSurfaceView = new SurfaceView(this);
         mSurfaceView.getHolder().addCallback(this);
 
-        content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+        Button button = new Button(this);
+        button.setText("Copy bitmap to /sdcard/surfaceview.png");
+        button.setOnClickListener((View v) -> {
+            Bitmap b = Bitmap.createBitmap(
+                            mSurfaceView.getWidth(),
+                            mSurfaceView.getHeight(),
+                            Bitmap.Config.ARGB_8888);
+            PixelCopy.request(mSurfaceView, b,
+                    mOnCopyFinished, mSurfaceView.getHandler());
+        });
+
+        LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+        layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        layout.addView(mSurfaceView, LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.MATCH_PARENT);
+
+        content.addView(layout, new FrameLayout.LayoutParams(
                 FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.MATCH_PARENT,
-                Gravity.CENTER));
+                FrameLayout.LayoutParams.MATCH_PARENT));
         setContentView(content);
     }
 
+    private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
+        @Override
+        public void onPixelCopyFinished(Response response) {
+            if (!response.success) {
+                Toast.makeText(HardwareCanvasSurfaceViewActivity.this,
+                        "Failed to copy", Toast.LENGTH_SHORT).show();
+                return;
+            }
+            try {
+                try (FileOutputStream out = new FileOutputStream(
+                        Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+                    response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    };
+
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
         mThread = new RenderingThread(holder.getSurface());
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 005a483..c1f0038 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -37,6 +37,8 @@
         final int parentLeft, parentTop;
         final Matrix matrix;
         final String className;
+        final float textSize;
+        final int textColor;
         final CharSequence text;
         final int scrollY;
         final int[] lineCharOffsets;
@@ -50,6 +52,8 @@
             this.parentTop = parentTop;
             this.matrix = new Matrix(matrix);
             this.className = node.getClassName();
+            this.textSize = node.getTextSize();
+            this.textColor = node.getTextColor();
             this.text = node.getText() != null ? node.getText() : node.getContentDescription();
             this.scrollY = node.getScrollY();
             this.lineCharOffsets = node.getTextLineCharOffsets();
@@ -113,7 +117,9 @@
             TextEntry te = mTextRects.get(i);
             Log.d(TAG, "View " + te.className + " " + te.bounds.toShortString()
                     + " in " + te.parentLeft + "," + te.parentTop
-                    + " matrix=" + te.matrix.toShortString() + ": "
+                    + " matrix=" + te.matrix.toShortString()
+                    + " size=" + te.textSize + " color=#" + Integer.toHexString(te.textColor)
+                    + ": "
                     + te.text);
             if (te.lineCharOffsets != null && te.lineBaselines != null) {
                 final int num = te.lineCharOffsets.length < te.lineBaselines.length
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index b5ed1b5..81ab3cc 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -1,13 +1,17 @@
 #!/usr/bin/env python
 
 import collections
+import copy
 import glob
+import itertools
 from os import path
 import sys
 from xml.etree import ElementTree
 
 from fontTools import ttLib
 
+EMOJI_VS = 0xFE0F
+
 LANG_TO_SCRIPT = {
     'as': 'Beng',
     'bn': 'Beng',
@@ -57,13 +61,26 @@
     return LANG_TO_SCRIPT[lang]
 
 
-def get_best_cmap(font):
+def printable(inp):
+    if type(inp) is set:  # set of character sequences
+        return '{' + ', '.join([printable(seq) for seq in inp]) + '}'
+    if type(inp) is tuple:  # character sequence
+        return '<' + (', '.join([printable(ch) for ch in inp])) + '>'
+    else:  # single character
+        return 'U+%04X' % inp
+
+
+def open_font(font):
     font_file, index = font
     font_path = path.join(_fonts_dir, font_file)
     if index is not None:
-        ttfont = ttLib.TTFont(font_path, fontNumber=index)
+        return ttLib.TTFont(font_path, fontNumber=index)
     else:
-        ttfont = ttLib.TTFont(font_path)
+        return ttLib.TTFont(font_path)
+
+
+def get_best_cmap(font):
+    ttfont = open_font(font)
     all_unicode_cmap = None
     bmp_cmap = None
     for cmap in ttfont['cmap'].tables:
@@ -79,6 +96,52 @@
     return all_unicode_cmap.cmap if all_unicode_cmap else bmp_cmap.cmap
 
 
+def get_variation_sequences_cmap(font):
+    ttfont = open_font(font)
+    vs_cmap = None
+    for cmap in ttfont['cmap'].tables:
+        specifier = (cmap.format, cmap.platformID, cmap.platEncID)
+        if specifier == (14, 0, 5):
+            assert vs_cmap is None, 'More than one VS cmap in %s' % (font, )
+            vs_cmap = cmap
+    return vs_cmap
+
+
+def get_emoji_map(font):
+    # Add normal characters
+    emoji_map = copy.copy(get_best_cmap(font))
+    reverse_cmap = {glyph: code for code, glyph in emoji_map.items()}
+
+    # Add variation sequences
+    vs_dict = get_variation_sequences_cmap(font).uvsDict
+    for vs in vs_dict:
+        for base, glyph in vs_dict[vs]:
+            if glyph is None:
+                emoji_map[(base, vs)] = emoji_map[base]
+            else:
+                emoji_map[(base, vs)] = glyph
+
+    # Add GSUB rules
+    ttfont = open_font(font)
+    for lookup in ttfont['GSUB'].table.LookupList.Lookup:
+        assert lookup.LookupType == 4, 'We only understand type 4 lookups'
+        for subtable in lookup.SubTable:
+            ligatures = subtable.ligatures
+            for first_glyph in ligatures:
+                for ligature in ligatures[first_glyph]:
+                    sequence = [first_glyph] + ligature.Component
+                    sequence = [reverse_cmap[glyph] for glyph in sequence]
+                    sequence = tuple(sequence)
+                    # Make sure no starting subsequence of 'sequence' has been
+                    # seen before.
+                    for sub_len in range(2, len(sequence)+1):
+                        subsequence = sequence[:sub_len]
+                        assert subsequence not in emoji_map
+                    emoji_map[sequence] = ligature.LigGlyph
+
+    return emoji_map
+
+
 def assert_font_supports_any_of_chars(font, chars):
     best_cmap = get_best_cmap(font)
     for char in chars:
@@ -101,6 +164,13 @@
             'U+%04X was found in %s' % (char, font))
 
 
+def assert_font_supports_all_sequences(font, sequences):
+    vs_dict = get_variation_sequences_cmap(font).uvsDict
+    for base, vs in sorted(sequences):
+        assert vs in vs_dict and (base, None) in vs_dict[vs], (
+            '<U+%04X, U+%04X> was not found in %s' % (base, vs, font))
+
+
 def check_hyphens(hyphens_dir):
     # Find all the scripts that need automatic hyphenation
     scripts = set()
@@ -119,6 +189,16 @@
             assert_font_supports_any_of_chars(font, HYPHENS)
 
 
+class FontRecord(object):
+    def __init__(self, name, scripts, variant, weight, style, font):
+        self.name = name
+        self.scripts = scripts
+        self.variant = variant
+        self.weight = weight
+        self.style = style
+        self.font = font
+
+
 def parse_fonts_xml(fonts_xml_path):
     global _script_to_font_map, _fallback_chain
     _script_to_font_map = collections.defaultdict(set)
@@ -159,7 +239,7 @@
             if index:
                 index = int(index)
 
-            _fallback_chain.append((
+            _fallback_chain.append(FontRecord(
                 name,
                 frozenset(scripts),
                 variant,
@@ -175,39 +255,72 @@
                 _script_to_font_map[script].add((font_file, index))
 
 
-def check_emoji_availability():
-    emoji_fonts = [font[5] for font in _fallback_chain if 'Zsye' in font[1]]
+def check_emoji_coverage(all_emoji, equivalent_emoji):
+    emoji_fonts = [
+        record.font for record in _fallback_chain
+        if 'Zsye' in record.scripts]
     assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
     emoji_font = emoji_fonts[0]
-    emoji_chars = _emoji_properties['Emoji']
-    assert_font_supports_all_of_chars(emoji_font, emoji_chars)
+    coverage = get_emoji_map(emoji_font)
+
+    for sequence in all_emoji:
+        assert sequence in coverage, (
+            '%s is not supported in the emoji font.' % printable(sequence))
+
+    for sequence in coverage:
+        if sequence in {0x0000, 0x000D, 0x0020}:
+            # The font needs to support a few extra characters, which is OK
+            continue
+        assert sequence in all_emoji, (
+            'Emoji font should not support %s.' % printable(sequence))
+
+    for first, second in sorted(equivalent_emoji.items()):
+        assert coverage[first] == coverage[second], (
+            '%s and %s should map to the same glyph.' % (
+                printable(first),
+                printable(second)))
+
+    for glyph in set(coverage.values()):
+        maps_to_glyph = [seq for seq in coverage if coverage[seq] == glyph]
+        if len(maps_to_glyph) > 1:
+            # There are more than one sequences mapping to the same glyph. We
+            # need to make sure they were expected to be equivalent.
+            equivalent_seqs = set()
+            for seq in maps_to_glyph:
+                equivalent_seq = seq
+                while equivalent_seq in equivalent_emoji:
+                    equivalent_seq = equivalent_emoji[equivalent_seq]
+                equivalent_seqs.add(equivalent_seq)
+            assert len(equivalent_seqs) == 1, (
+                'The sequences %s should not result in the same glyph %s' % (
+                    printable(equivalent_seqs),
+                    glyph))
 
 
-def check_emoji_defaults():
-    default_emoji_chars = _emoji_properties['Emoji_Presentation']
-    missing_text_chars = _emoji_properties['Emoji'] - default_emoji_chars
+def check_emoji_defaults(default_emoji):
+    missing_text_chars = _emoji_properties['Emoji'] - default_emoji
     emoji_font_seen = False
-    for name, scripts, variant, weight, style, font in _fallback_chain:
-        if 'Zsye' in scripts:
+    for record in _fallback_chain:
+        if 'Zsye' in record.scripts:
             emoji_font_seen = True
             # No need to check the emoji font
             continue
         # For later fonts, we only check them if they have a script
         # defined, since the defined script may get them to a higher
         # score even if they appear after the emoji font.
-        if emoji_font_seen and not scripts:
+        if emoji_font_seen and not record.scripts:
             continue
 
         # Check default emoji-style characters
-        assert_font_supports_none_of_chars(font, sorted(default_emoji_chars))
+        assert_font_supports_none_of_chars(record.font, sorted(default_emoji))
 
         # Mark default text-style characters appearing in fonts above the emoji
         # font as seen
         if not emoji_font_seen:
-            missing_text_chars -= set(get_best_cmap(font))
+            missing_text_chars -= set(get_best_cmap(record.font))
 
-    # Noto does not have monochrome symbols for Unicode 7.0 wingdings and
-    # webdings
+    # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
+    # webdings yet.
     missing_text_chars -= _chars_by_age['7.0']
     # TODO: Remove these after b/26113320 is fixed
     missing_text_chars -= {
@@ -236,31 +349,175 @@
             line = line.strip()
             if not line:
                 continue
-            char_range, prop = line.split(';')
-            char_range = char_range.strip()
+
+            chars, prop = line.split(';')
+            chars = chars.strip()
             prop = prop.strip()
-            if '..' in char_range:
-                char_start, char_end = char_range.split('..')
-            else:
-                char_start = char_end = char_range
-            char_start = int(char_start, 16)
-            char_end = int(char_end, 16)
-            char_range = xrange(char_start, char_end+1)
+
+            if ' ' in chars:  # character sequence
+                sequence = [int(ch, 16) for ch in chars.split(' ')]
+                additions = [tuple(sequence)]
+            elif '..' in chars:  # character range
+                char_start, char_end = chars.split('..')
+                char_start = int(char_start, 16)
+                char_end = int(char_end, 16)
+                additions = xrange(char_start, char_end+1)
+            else:  # singe character
+                additions = [int(chars, 16)]
             if reverse:
-                output_dict[prop].update(char_range)
+                output_dict[prop].update(additions)
             else:
-                for char in char_range:
-                    assert char not in output_dict
-                    output_dict[char] = prop
+                for addition in additions:
+                    assert addition not in output_dict
+                    output_dict[addition] = prop
     return output_dict
 
 
+def parse_standardized_variants(file_path):
+    emoji_set = set()
+    text_set = set()
+    with open(file_path) as datafile:
+        for line in datafile:
+            if '#' in line:
+                line = line[:line.index('#')]
+            line = line.strip()
+            if not line:
+                continue
+            sequence, description, _ = line.split(';')
+            sequence = sequence.strip().split(' ')
+            base = int(sequence[0], 16)
+            vs = int(sequence[1], 16)
+            description = description.strip()
+            if description == 'text style':
+                text_set.add((base, vs))
+            elif description == 'emoji style':
+                emoji_set.add((base, vs))
+    return text_set, emoji_set
+
+
 def parse_ucd(ucd_path):
     global _emoji_properties, _chars_by_age
+    global _text_variation_sequences, _emoji_variation_sequences
+    global _emoji_sequences, _emoji_zwj_sequences
     _emoji_properties = parse_unicode_datafile(
         path.join(ucd_path, 'emoji-data.txt'), reverse=True)
     _chars_by_age = parse_unicode_datafile(
         path.join(ucd_path, 'DerivedAge.txt'), reverse=True)
+    sequences = parse_standardized_variants(
+        path.join(ucd_path, 'StandardizedVariants.txt'))
+    _text_variation_sequences, _emoji_variation_sequences = sequences
+    _emoji_sequences = parse_unicode_datafile(
+        path.join(ucd_path, 'emoji-sequences.txt'))
+    _emoji_zwj_sequences = parse_unicode_datafile(
+        path.join(ucd_path, 'emoji-zwj-sequences.txt'))
+
+
+def flag_sequence(territory_code):
+    return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
+
+
+UNSUPPORTED_FLAGS = frozenset({
+    flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('DG'),
+    flag_sequence('EA'), flag_sequence('EH'), flag_sequence('FK'),
+    flag_sequence('GF'), flag_sequence('GP'), flag_sequence('GS'),
+    flag_sequence('MF'), flag_sequence('MQ'), flag_sequence('NC'),
+    flag_sequence('PM'), flag_sequence('RE'), flag_sequence('TF'),
+    flag_sequence('WF'), flag_sequence('XK'), flag_sequence('YT'),
+})
+
+EQUIVALENT_FLAGS = {
+    flag_sequence('BV'): flag_sequence('NO'),
+    flag_sequence('CP'): flag_sequence('FR'),
+    flag_sequence('HM'): flag_sequence('AU'),
+    flag_sequence('SJ'): flag_sequence('NO'),
+    flag_sequence('UM'): flag_sequence('US'),
+}
+
+COMBINING_KEYCAP = 0x20E3
+
+LEGACY_ANDROID_EMOJI = {
+    0xFE4E5: flag_sequence('JP'),
+    0xFE4E6: flag_sequence('US'),
+    0xFE4E7: flag_sequence('FR'),
+    0xFE4E8: flag_sequence('DE'),
+    0xFE4E9: flag_sequence('IT'),
+    0xFE4EA: flag_sequence('GB'),
+    0xFE4EB: flag_sequence('ES'),
+    0xFE4EC: flag_sequence('RU'),
+    0xFE4ED: flag_sequence('CN'),
+    0xFE4EE: flag_sequence('KR'),
+    0xFE82C: (ord('#'), COMBINING_KEYCAP),
+    0xFE82E: (ord('1'), COMBINING_KEYCAP),
+    0xFE82F: (ord('2'), COMBINING_KEYCAP),
+    0xFE830: (ord('3'), COMBINING_KEYCAP),
+    0xFE831: (ord('4'), COMBINING_KEYCAP),
+    0xFE832: (ord('5'), COMBINING_KEYCAP),
+    0xFE833: (ord('6'), COMBINING_KEYCAP),
+    0xFE834: (ord('7'), COMBINING_KEYCAP),
+    0xFE835: (ord('8'), COMBINING_KEYCAP),
+    0xFE836: (ord('9'), COMBINING_KEYCAP),
+    0xFE837: (ord('0'), COMBINING_KEYCAP),
+}
+
+ZWJ_IDENTICALS = {
+    # KISS
+    (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
+    # COUPLE WITH HEART
+    (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F468): 0x1F491,
+    # FAMILY
+    (0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F466): 0x1F46A,
+}
+
+def compute_expected_emoji():
+    equivalent_emoji = {}
+    sequence_pieces = set()
+    all_sequences = set()
+    all_sequences.update(_emoji_variation_sequences)
+
+    for sequence in _emoji_sequences.keys():
+        sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
+        all_sequences.add(sequence)
+        sequence_pieces.update(sequence)
+
+    for sequence in _emoji_zwj_sequences.keys():
+        sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
+        all_sequences.add(sequence)
+        sequence_pieces.update(sequence)
+        # Add reverse of all emoji ZWJ sequences, which are added to the fonts
+        # as a workaround to get the sequences work in RTL text.
+        reversed_seq = tuple(reversed(sequence))
+        all_sequences.add(reversed_seq)
+        equivalent_emoji[reversed_seq] = sequence
+
+    # Add all two-letter flag sequences, as even the unsupported ones should
+    # resolve to a flag tofu.
+    all_letters = [chr(code) for code in range(ord('A'), ord('Z')+1)]
+    all_two_letter_codes = itertools.product(all_letters, repeat=2)
+    all_flags = {flag_sequence(code) for code in all_two_letter_codes}
+    all_sequences.update(all_flags)
+    tofu_flags = UNSUPPORTED_FLAGS | (all_flags - set(_emoji_sequences.keys()))
+
+    all_emoji = (
+        _emoji_properties['Emoji'] |
+        all_sequences |
+        sequence_pieces |
+        set(LEGACY_ANDROID_EMOJI.keys()))
+    default_emoji = (
+        _emoji_properties['Emoji_Presentation'] |
+        all_sequences |
+        set(LEGACY_ANDROID_EMOJI.keys()))
+
+    first_tofu_flag = sorted(tofu_flags)[0]
+    for flag in tofu_flags:
+        if flag != first_tofu_flag:
+            equivalent_emoji[flag] = first_tofu_flag
+    equivalent_emoji.update(EQUIVALENT_FLAGS)
+    equivalent_emoji.update(LEGACY_ANDROID_EMOJI)
+    equivalent_emoji.update(ZWJ_IDENTICALS)
+    for seq in _emoji_variation_sequences:
+        equivalent_emoji[seq] = seq[0]
+
+    return all_emoji, default_emoji, equivalent_emoji
 
 
 def main():
@@ -278,8 +535,9 @@
     if check_emoji == 'true':
         ucd_path = sys.argv[3]
         parse_ucd(ucd_path)
-        check_emoji_availability()
-        check_emoji_defaults()
+        all_emoji, default_emoji, equivalent_emoji = compute_expected_emoji()
+        check_emoji_coverage(all_emoji, equivalent_emoji)
+        check_emoji_defaults(default_emoji)
 
 
 if __name__ == '__main__':
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 7412bc2..50efc7f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -245,6 +245,13 @@
         return sFontLocation;
     }
 
+    // ---- delegate methods ----
+    @LayoutlibDelegate
+    /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex) {
+        final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mNativePtr);
+        return delegate != null && delegate.addFont(path, ttcIndex);
+    }
+
     // ---- native methods ----
 
     @LayoutlibDelegate
@@ -270,16 +277,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nAddFont(long nativeFamily, final String path, int ttcIndex) {
-        // FIXME: support ttc fonts. Hack JRE??
-        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
-        if (delegate != null) {
-            if (sFontLocation == null) {
-                delegate.mPostInitRunnables.add(() -> delegate.addFont(path));
-                return true;
-            }
-            return delegate.addFont(path);
-        }
+    /*package*/ static boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex) {
+        assert false : "The only client of this method has been overriden.";
         return false;
     }
 
@@ -390,6 +389,15 @@
         mPostInitRunnables = null;
     }
 
+    private boolean addFont(final String path, int ttcIndex) {
+        // FIXME: support ttc fonts. Hack JRE??
+        if (sFontLocation == null) {
+            mPostInitRunnables.add(() -> addFont(path));
+            return true;
+        }
+        return addFont(path);
+    }
+
      private boolean addFont(@NonNull String path) {
          return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC));
      }
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index d8ff57b..7fa7235 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -90,7 +90,7 @@
     }
 
     @LayoutlibDelegate
-    static long nCreateRenderer(long rootGroupPtr) {
+    static long nCreateTree(long rootGroupPtr) {
         VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
         return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup));
     }
diff --git a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
index 6c34c70..6d3bb4c 100644
--- a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
@@ -64,15 +64,14 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nParseStringForPath(long pathPtr, @NonNull String pathString, int
+    /*package*/ static void nParseStringForPath(long pathPtr, @NonNull String pathString, int
             stringLength) {
         Path_Delegate path_delegate = Path_Delegate.getDelegate(pathPtr);
         if (path_delegate == null) {
-            return false;
+            return;
         }
         assert pathString.length() == stringLength;
         PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate);
-        return true;
     }
 
     @LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 9e50ee8..3b88290 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -537,7 +537,7 @@
      * Note that while this can be called several time, the first call to {@link #cleanupThread()}
      * will do the clean-up, and make the thread unable to do further scene actions.
      */
-    public static void prepareThread() {
+    public synchronized static void prepareThread() {
         // we need to make sure the Looper has been initialized for this thread.
         // this is required for View that creates Handler objects.
         if (Looper.myLooper() == null) {
@@ -551,7 +551,7 @@
      * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single
      * call to this will prevent the thread from doing further scene actions
      */
-    public static void cleanupThread() {
+    public synchronized static void cleanupThread() {
         // clean up the looper
         Looper_Accessor.cleanupThread();
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index fea633e..308488a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -70,23 +70,6 @@
     }
 
     @Override
-    public Map<String, String> getDefaultProperties(Object viewObject) {
-        return mSession.getDefaultProperties(viewObject);
-    }
-
-    @Override
-    public Result getProperty(Object objectView, String propertyName) {
-        // pass
-        return super.getProperty(objectView, propertyName);
-    }
-
-    @Override
-    public Result setProperty(Object objectView, String propertyName, String propertyValue) {
-        // pass
-        return super.setProperty(objectView, propertyName, propertyValue);
-    }
-
-    @Override
     public Result render(long timeout, boolean forceMeasure) {
         try {
             Bridge.prepareThread();
@@ -213,6 +196,10 @@
         }
     }
 
+    public RenderSessionImpl getSessionImpl() {
+        return mSession;
+    }
+
     /*package*/ BridgeRenderSession(RenderSessionImpl scene, Result lastResult) {
         mSession = scene;
         if (scene != null) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 89272fa..fd95bd5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -27,6 +27,7 @@
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.android.PropertiesMap.Property;
 import com.android.layoutlib.bridge.android.view.WindowManagerImpl;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.Stack;
@@ -275,7 +276,7 @@
         return mRenderResources;
     }
 
-    public Map<String, String> getDefaultPropMap(Object key) {
+    public PropertiesMap getDefaultPropMap(Object key) {
         return mDefaultPropMaps.get(key);
     }
 
@@ -731,16 +732,10 @@
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
                         "Failed to find the style corresponding to the id " + defStyleAttr, null);
             } else {
-                if (defaultPropMap != null) {
-                    String defStyleName = defStyleAttribute.getFirst();
-                    if (defStyleAttribute.getSecond()) {
-                        defStyleName = "android:" + defStyleName;
-                    }
-                    defaultPropMap.put("style", defStyleName);
-                }
+                String defStyleName = defStyleAttribute.getFirst();
 
                 // look for the style in the current theme, and its parent:
-                ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute.getFirst(),
+                ResourceValue item = mRenderResources.findItemInTheme(defStyleName,
                         defStyleAttribute.getSecond());
 
                 if (item != null) {
@@ -750,6 +745,12 @@
                     if (item instanceof StyleResourceValue) {
                         defStyleValues = (StyleResourceValue) item;
                     }
+                    if (defaultPropMap != null) {
+                        if (defStyleAttribute.getSecond()) {
+                            defStyleName = "android:" + defStyleName;
+                        }
+                        defaultPropMap.put("style", new Property(defStyleName, item.getValue()));
+                    }
                 } else {
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
                             String.format(
@@ -776,7 +777,8 @@
                         item = mRenderResources.getStyle(value.getSecond(), isFrameworkRes);
                         if (item != null) {
                             if (defaultPropMap != null) {
-                                defaultPropMap.put("style", item.getName());
+                                String name = item.getName();
+                                defaultPropMap.put("style", new Property(name, name));
                             }
 
                             defStyleValues = item;
@@ -855,13 +857,14 @@
                     // if we found a value, we make sure this doesn't reference another value.
                     // So we resolve it.
                     if (resValue != null) {
-                        // put the first default value, before the resolution.
-                        if (defaultPropMap != null) {
-                            defaultPropMap.put(attrName, resValue.getValue());
-                        }
-
+                        String preResolve = resValue.getValue();
                         resValue = mRenderResources.resolveResValue(resValue);
 
+                        if (defaultPropMap != null) {
+                            defaultPropMap.put(attrName,
+                                    new Property(preResolve, resValue.getValue()));
+                        }
+
                         // If the value is a reference to another theme attribute that doesn't
                         // exist, we should log a warning and omit it.
                         String val = resValue.getValue();
@@ -949,10 +952,11 @@
 
                 if (resValue != null) {
                     // Add it to defaultPropMap before resolving
-                    defaultPropMap.put(attrName, resValue.getValue());
+                    String preResolve = resValue.getValue();
                     // resolve it to make sure there are no references left.
-                    ta.bridgeSetValue(i, attrName, attribute.getSecond(),
-                            mRenderResources.resolveResValue(resValue));
+                    resValue = mRenderResources.resolveResValue(resValue);
+                    ta.bridgeSetValue(i, attrName, attribute.getSecond(), resValue);
+                    defaultPropMap.put(attrName, new Property(preResolve, resValue.getValue()));
                 }
             }
         }
@@ -1915,11 +1919,4 @@
         }
 
     }
-
-    /**
-     * An alias used for the value in {@code {@link #mDefaultPropMaps}}
-     */
-    private static class PropertiesMap extends HashMap<String, String> {
-    }
-
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 42c0ae0..0a64b63 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -645,6 +645,11 @@
     }
 
     @Override
+    public void deleteApplicationCacheFilesAsUser(String packageName, int userId,
+            IPackageDataObserver observer) {
+    }
+
+    @Override
     public void freeStorageAndNotify(String volumeUuid, long freeStorageSize,
             IPackageDataObserver observer) {
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/PropertiesMap.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/PropertiesMap.java
new file mode 100644
index 0000000..a38d579
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/PropertiesMap.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android;
+
+import com.android.layoutlib.bridge.android.PropertiesMap.Property;
+
+import java.util.HashMap;
+
+/**
+ * An alias used for the value in {@link BridgeContext#mDefaultPropMaps}
+ */
+public class PropertiesMap extends HashMap<String, Property> {
+
+    public static class Property {
+        public final String resource;
+        public final String value;
+
+        public Property(String resource, String value) {
+            this.resource = resource;
+            this.value = value;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 0c53753..2d38831 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -286,7 +286,7 @@
         return mParams;
     }
 
-    protected BridgeContext getContext() {
+    public BridgeContext getContext() {
         return mContext;
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 866b248..11fabc6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1415,10 +1415,6 @@
         return mSystemViewInfoList;
     }
 
-    public Map<String, String> getDefaultProperties(Object viewObject) {
-        return getContext().getDefaultPropMap(viewObject);
-    }
-
     public void setScene(RenderSession session) {
         mScene = session;
     }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 483bddc..061bed7 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -192,6 +192,7 @@
         "android.graphics.BitmapFactory#setDensityFromOptions",
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
         "android.graphics.drawable.GradientDrawable#buildRing",
+        "android.graphics.FontFamily#addFont",
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.graphics.Typeface#makeFamilyFromParsed",
         "android.os.Handler#sendMessageAtTime",
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index f8c1ea3..1a8197c 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -687,7 +687,7 @@
         Bundle scanParams = new Bundle();
         scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
         scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
-        sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
+        mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
     }
 
     /**
@@ -700,7 +700,7 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
+        mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
     }
     /**
      * reports currently available scan results on appropriate listeners
@@ -708,7 +708,7 @@
      */
     public boolean getScanResults() {
         validateChannel();
-        Message reply = sAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+        Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
         return reply.what == CMD_OP_SUCCEEDED;
     }
 
@@ -741,7 +741,7 @@
         Bundle scanParams = new Bundle();
         scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
         scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
-        sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
+        mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
     }
 
     /**
@@ -754,7 +754,7 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
+        mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
     }
 
     private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
@@ -764,7 +764,7 @@
         scanSettings.isPnoScan = true;
         pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);
         pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
-        sAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
+        mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
     }
     /**
      * Start wifi connected PNO scan
@@ -820,7 +820,7 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key);
+        mAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key);
     }
 
     /** specifies information about an access point of interest */
@@ -956,7 +956,7 @@
         int key = addListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key);
+        mAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key);
     }
 
     /**
@@ -968,14 +968,14 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key);
+        mAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key);
     }
 
     /** @hide */
     @SystemApi
     public void configureWifiChange(WifiChangeSettings settings) {
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+        mAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
     }
 
     /** interface to receive hotlist events on; use this on {@link #setHotlist} */
@@ -1060,7 +1060,7 @@
         HotlistSettings settings = new HotlistSettings();
         settings.bssidInfos = bssidInfos;
         settings.apLostThreshold = apLostThreshold;
-        sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings);
+        mAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings);
     }
 
     /**
@@ -1072,7 +1072,7 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key);
+        mAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key);
     }
 
 
@@ -1137,17 +1137,14 @@
     private IWifiScanner mService;
 
     private static final int INVALID_KEY = 0;
-    private static int sListenerKey = 1;
+    private int mListenerKey = 1;
 
-    private static final SparseArray sListenerMap = new SparseArray();
-    private static final Object sListenerMapLock = new Object();
+    private final SparseArray mListenerMap = new SparseArray();
+    private final Object mListenerMapLock = new Object();
 
-    private static AsyncChannel sAsyncChannel;
-    private static CountDownLatch sConnected;
-
-    private static final Object sThreadRefLock = new Object();
-    private static int sThreadRefCount;
-    private static Handler sInternalHandler;
+    private AsyncChannel mAsyncChannel;
+    private final CountDownLatch mConnected;
+    private final Handler mInternalHandler;
 
     /**
      * Create a new WifiScanner instance.
@@ -1156,10 +1153,11 @@
      * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
      * @param context the application context
      * @param service the Binder interface
+     * @param looper the Looper used to deliver callbacks
      * @hide
      */
-    public WifiScanner(Context context, IWifiScanner service) {
-        this(context, service, null, true);
+    public WifiScanner(Context context, IWifiScanner service, Looper looper) {
+        this(context, service, looper, true);
     }
 
     /**
@@ -1167,8 +1165,7 @@
      *
      * @param context The application context.
      * @param service The IWifiScanner Binder interface
-     * @param looper Looper for running WifiScanner operations. If null, a handler thread will be
-     *          created for running WifiScanner operations.
+     * @param looper the Looper used to deliver callbacks
      * @param waitForConnection If true, this will not return until a connection to Wifi Scanner
      *          service is established.
      * @hide
@@ -1178,50 +1175,34 @@
             boolean waitForConnection) {
         mContext = context;
         mService = service;
-        init(looper, waitForConnection);
-    }
 
-    private void init(Looper looper, boolean waitForConnection) {
-        synchronized (sThreadRefLock) {
-            if (++sThreadRefCount == 1) {
-                Messenger messenger = null;
-                try {
-                    messenger = mService.getMessenger();
-                } catch (RemoteException e) {
-                    /* do nothing */
-                } catch (SecurityException e) {
-                    /* do nothing */
-                }
+        Messenger messenger = null;
+        try {
+            messenger = mService.getMessenger();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
 
-                if (messenger == null) {
-                    sAsyncChannel = null;
-                    return;
-                }
+        if (messenger == null) {
+            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
+        }
 
-                sAsyncChannel = new AsyncChannel();
-                sConnected = new CountDownLatch(1);
+        mAsyncChannel = new AsyncChannel();
+        mConnected = new CountDownLatch(1);
 
-                if (looper == null) {
-                    HandlerThread thread = new HandlerThread("WifiScanner");
-                    thread.start();
-                    sInternalHandler = new ServiceHandler(thread.getLooper());
-                } else {
-                    sInternalHandler = new ServiceHandler(looper);
-                }
-                sAsyncChannel.connect(mContext, sInternalHandler, messenger);
-                if (waitForConnection) {
-                    try {
-                        sConnected.await();
-                    } catch (InterruptedException e) {
-                        Log.e(TAG, "interrupted wait at init");
-                    }
-                }
+        mInternalHandler = new ServiceHandler(looper);
+        mAsyncChannel.connect(mContext, mInternalHandler, messenger);
+        if (waitForConnection) {
+            try {
+                mConnected.await();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "interrupted wait at init");
             }
         }
     }
 
     private void validateChannel() {
-        if (sAsyncChannel == null) throw new IllegalStateException(
+        if (mAsyncChannel == null) throw new IllegalStateException(
                 "No permission to access and change wifi or a bad initialization");
     }
 
@@ -1229,7 +1210,7 @@
     // send an error message to internal handler; Otherwise add the listener to the listener map and
     // return the key of the listener.
     private int addListener(ActionListener listener) {
-        synchronized (sListenerMap) {
+        synchronized (mListenerMapLock) {
             boolean keyExists = (getListenerKey(listener) != INVALID_KEY);
             // Note we need to put the listener into listener map even if it's a duplicate as the
             // internal handler will need the key to find the listener. In case of duplicates,
@@ -1239,7 +1220,7 @@
                 if (DBG) Log.d(TAG, "listener key already exists");
                 OperationResult operationResult = new OperationResult(REASON_DUPLICATE_REQEUST,
                         "Outstanding request with same key not stopped yet");
-                Message message = Message.obtain(sInternalHandler, CMD_OP_FAILED, 0, key,
+                Message message = Message.obtain(mInternalHandler, CMD_OP_FAILED, 0, key,
                         operationResult);
                 message.sendToTarget();
                 return INVALID_KEY;
@@ -1249,55 +1230,55 @@
         }
     }
 
-    private static int putListener(Object listener) {
+    private int putListener(Object listener) {
         if (listener == null) return INVALID_KEY;
         int key;
-        synchronized (sListenerMapLock) {
+        synchronized (mListenerMapLock) {
             do {
-                key = sListenerKey++;
+                key = mListenerKey++;
             } while (key == INVALID_KEY);
-            sListenerMap.put(key, listener);
+            mListenerMap.put(key, listener);
         }
         return key;
     }
 
-    private static Object getListener(int key) {
+    private Object getListener(int key) {
         if (key == INVALID_KEY) return null;
-        synchronized (sListenerMapLock) {
-            Object listener = sListenerMap.get(key);
+        synchronized (mListenerMapLock) {
+            Object listener = mListenerMap.get(key);
             return listener;
         }
     }
 
-    private static int getListenerKey(Object listener) {
+    private int getListenerKey(Object listener) {
         if (listener == null) return INVALID_KEY;
-        synchronized (sListenerMapLock) {
-            int index = sListenerMap.indexOfValue(listener);
+        synchronized (mListenerMapLock) {
+            int index = mListenerMap.indexOfValue(listener);
             if (index == -1) {
                 return INVALID_KEY;
             } else {
-                return sListenerMap.keyAt(index);
+                return mListenerMap.keyAt(index);
             }
         }
     }
 
-    private static Object removeListener(int key) {
+    private Object removeListener(int key) {
         if (key == INVALID_KEY) return null;
-        synchronized (sListenerMapLock) {
-            Object listener = sListenerMap.get(key);
-            sListenerMap.remove(key);
+        synchronized (mListenerMapLock) {
+            Object listener = mListenerMap.get(key);
+            mListenerMap.remove(key);
             return listener;
         }
     }
 
-    private static int removeListener(Object listener) {
+    private int removeListener(Object listener) {
         int key = getListenerKey(listener);
         if (key == INVALID_KEY) {
             Log.e(TAG, "listener cannot be found");
             return key;
         }
-        synchronized (sListenerMapLock) {
-            sListenerMap.remove(key);
+        synchronized (mListenerMapLock) {
+            mListenerMap.remove(key);
             return key;
         }
     }
@@ -1338,7 +1319,7 @@
                 };
     }
 
-    private static class ServiceHandler extends Handler {
+    private class ServiceHandler extends Handler {
         ServiceHandler(Looper looper) {
             super(looper);
         }
@@ -1347,14 +1328,14 @@
             switch (msg.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+                        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                     } else {
                         Log.e(TAG, "Failed to set up channel connection");
                         // This will cause all further async API calls on the WifiManager
                         // to fail and throw an exception
-                        sAsyncChannel = null;
+                        mAsyncChannel = null;
                     }
-                    sConnected.countDown();
+                    mConnected.countDown();
                     return;
                 case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                     return;
@@ -1362,7 +1343,7 @@
                     Log.e(TAG, "Channel connection lost");
                     // This will cause all further async API calls on the WifiManager
                     // to fail and throw an exception
-                    sAsyncChannel = null;
+                    mAsyncChannel = null;
                     getLooper().quit();
                     return;
             }