Merge "Add volatile qualifier to boolean variable" into nyc-dev
diff --git a/Android.mk b/Android.mk
index ae5d67e..2017404 100644
--- a/Android.mk
+++ b/Android.mk
@@ -442,8 +442,6 @@
 	telephony/java/com/android/internal/telephony/ISms.aidl \
 	telephony/java/com/android/internal/telephony/ISub.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
-	telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl \
-	telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 732c500..baef0a4 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);
@@ -6313,6 +6313,8 @@
     method public static final long getMinimumPeriod();
     method public int getNetworkType();
     method public android.content.ComponentName getService();
+    method public long getTriggerContentMaxDelay();
+    method public long getTriggerContentUpdateDelay();
     method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
     method public boolean isPeriodic();
     method public boolean isPersisted();
@@ -6343,6 +6345,8 @@
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
     method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean);
+    method public android.app.job.JobInfo.Builder setTriggerContentMaxDelay(long);
+    method public android.app.job.JobInfo.Builder setTriggerContentUpdateDelay(long);
   }
 
   public static final class JobInfo.TriggerContentUri implements android.os.Parcelable {
@@ -8615,6 +8619,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 +8714,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 +9503,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 +10045,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 +10071,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 +10089,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 +14443,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 +22973,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 +23003,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 +29593,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 +49071,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 +49942,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 +57603,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[]);
@@ -58042,6 +58059,7 @@
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
     method public boolean replace(K, V, V);
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
   }
 
   public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58085,6 +58103,7 @@
     method public synchronized boolean remove(java.lang.Object, java.lang.Object);
     method public synchronized boolean replace(K, V, V);
     method public synchronized V replace(K, V);
+    method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
     method public synchronized int size();
     method public java.util.Collection<V> values();
   }
@@ -58625,6 +58644,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[]);
@@ -59066,6 +59097,7 @@
     method public java.util.NavigableSet<K> navigableKeySet();
     method public java.util.Map.Entry<K, V> pollFirstEntry();
     method public java.util.Map.Entry<K, V> pollLastEntry();
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
     method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
     method public java.util.SortedMap<K, V> subMap(K, K);
     method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -59167,6 +59199,7 @@
     ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
   }
 
 }
@@ -60140,18 +60173,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);
@@ -61524,6 +61545,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();
@@ -61533,6 +61555,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 56b2f58..d8545f1 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);
@@ -6581,6 +6581,8 @@
     method public static final long getMinimumPeriod();
     method public int getNetworkType();
     method public android.content.ComponentName getService();
+    method public long getTriggerContentMaxDelay();
+    method public long getTriggerContentUpdateDelay();
     method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
     method public boolean isPeriodic();
     method public boolean isPersisted();
@@ -6611,6 +6613,8 @@
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
     method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean);
+    method public android.app.job.JobInfo.Builder setTriggerContentMaxDelay(long);
+    method public android.app.job.JobInfo.Builder setTriggerContentUpdateDelay(long);
   }
 
   public static final class JobInfo.TriggerContentUri implements android.os.Parcelable {
@@ -8934,6 +8938,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 +9840,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 +10444,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 +10470,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 +10488,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 +14850,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 +24629,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 +24663,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 +31898,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 +52183,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 +53054,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 +60715,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[]);
@@ -61155,6 +61171,7 @@
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
     method public boolean replace(K, V, V);
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
   }
 
   public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -61198,6 +61215,7 @@
     method public synchronized boolean remove(java.lang.Object, java.lang.Object);
     method public synchronized boolean replace(K, V, V);
     method public synchronized V replace(K, V);
+    method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
     method public synchronized int size();
     method public java.util.Collection<V> values();
   }
@@ -61738,6 +61756,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[]);
@@ -62179,6 +62209,7 @@
     method public java.util.NavigableSet<K> navigableKeySet();
     method public java.util.Map.Entry<K, V> pollFirstEntry();
     method public java.util.Map.Entry<K, V> pollLastEntry();
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
     method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
     method public java.util.SortedMap<K, V> subMap(K, K);
     method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -62280,6 +62311,7 @@
     ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
   }
 
 }
@@ -63253,18 +63285,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);
@@ -64637,6 +64657,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();
@@ -64646,6 +64667,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 679347d..6a0f071 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);
@@ -6317,6 +6317,8 @@
     method public static final long getMinimumPeriod();
     method public int getNetworkType();
     method public android.content.ComponentName getService();
+    method public long getTriggerContentMaxDelay();
+    method public long getTriggerContentUpdateDelay();
     method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
     method public boolean isPeriodic();
     method public boolean isPersisted();
@@ -6347,6 +6349,8 @@
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
     method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean);
+    method public android.app.job.JobInfo.Builder setTriggerContentMaxDelay(long);
+    method public android.app.job.JobInfo.Builder setTriggerContentUpdateDelay(long);
   }
 
   public static final class JobInfo.TriggerContentUri implements android.os.Parcelable {
@@ -8622,6 +8626,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 +8721,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 +9513,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 +10056,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 +10082,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 +10101,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 +14455,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 +23041,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 +23071,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 +29662,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 +49149,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 +50020,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 +57681,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[]);
@@ -58120,6 +58137,7 @@
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
     method public boolean replace(K, V, V);
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
   }
 
   public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58163,6 +58181,7 @@
     method public synchronized boolean remove(java.lang.Object, java.lang.Object);
     method public synchronized boolean replace(K, V, V);
     method public synchronized V replace(K, V);
+    method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
     method public synchronized int size();
     method public java.util.Collection<V> values();
   }
@@ -58703,6 +58722,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[]);
@@ -59144,6 +59175,7 @@
     method public java.util.NavigableSet<K> navigableKeySet();
     method public java.util.Map.Entry<K, V> pollFirstEntry();
     method public java.util.Map.Entry<K, V> pollLastEntry();
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
     method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
     method public java.util.SortedMap<K, V> subMap(K, K);
     method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -59245,6 +59277,7 @@
     ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
     method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
     method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+    method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
   }
 
 }
@@ -60218,18 +60251,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);
@@ -61602,6 +61623,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();
@@ -61611,6 +61633,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/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 3385a17..e788d27 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -16,6 +16,9 @@
 
 package android.animation;
 
+import android.app.ActivityThread;
+import android.app.Application;
+import android.os.Build;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -133,10 +136,25 @@
     // The total duration of finishing all the Animators in the set.
     private long mTotalDuration = 0;
 
+    // In pre-N releases, calling end() before start() on an animator set is no-op. But that is not
+    // consistent with the behavior for other animator types. In order to keep the behavior
+    // consistent within Animation framework, when end() is called without start(), we will start
+    // the animator set and immediately end it for N and forward.
+    private final boolean mShouldIgnoreEndWithoutStart;
+
     public AnimatorSet() {
         super();
         mNodeMap.put(mDelayAnim, mRootNode);
         mNodes.add(mRootNode);
+        // Set the flag to ignore calling end() without start() for pre-N releases
+        Application app = ActivityThread.currentApplication();
+        if (app == null || app.getApplicationInfo() == null) {
+            mShouldIgnoreEndWithoutStart = true;
+        } else if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+            mShouldIgnoreEndWithoutStart = true;
+        } else {
+            mShouldIgnoreEndWithoutStart = false;
+        }
     }
 
     /**
@@ -365,6 +383,9 @@
      */
     @Override
     public void end() {
+        if (mShouldIgnoreEndWithoutStart && !isStarted()) {
+            return;
+        }
         mTerminated = true;
         if (isStarted()) {
             endRemainingAnimations();
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..c84a0dc 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -138,12 +138,28 @@
      */
     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;
     private final boolean requireCharging;
     private final boolean requireDeviceIdle;
     private final TriggerContentUri[] triggerContentUris;
+    private final long triggerContentUpdateDelay;
+    private final long triggerContentMaxDelay;
     private final boolean hasEarlyConstraint;
     private final boolean hasLateConstraint;
     private final int networkType;
@@ -207,6 +223,22 @@
     }
 
     /**
+     * When triggering on content URI changes, this is the delay from when a change
+     * is detected until the job is scheduled.
+     */
+    public long getTriggerContentUpdateDelay() {
+        return triggerContentUpdateDelay;
+    }
+
+    /**
+     * When triggering on content URI changes, this is the maximum delay we will
+     * use before scheduling the job.
+     */
+    public long getTriggerContentMaxDelay() {
+        return triggerContentMaxDelay;
+    }
+
+    /**
      * One of {@link android.app.job.JobInfo#NETWORK_TYPE_ANY},
      * {@link android.app.job.JobInfo#NETWORK_TYPE_NONE}, or
      * {@link android.app.job.JobInfo#NETWORK_TYPE_UNMETERED}.
@@ -254,7 +286,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();
@@ -306,6 +339,8 @@
         requireCharging = in.readInt() == 1;
         requireDeviceIdle = in.readInt() == 1;
         triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
+        triggerContentUpdateDelay = in.readLong();
+        triggerContentMaxDelay = in.readLong();
         networkType = in.readInt();
         minLatencyMillis = in.readLong();
         maxExecutionDelayMillis = in.readLong();
@@ -329,6 +364,8 @@
         triggerContentUris = b.mTriggerContentUris != null
                 ? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
                 : null;
+        triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
+        triggerContentMaxDelay = b.mTriggerContentMaxDelay;
         networkType = b.mNetworkType;
         minLatencyMillis = b.mMinLatencyMillis;
         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
@@ -356,6 +393,8 @@
         out.writeInt(requireCharging ? 1 : 0);
         out.writeInt(requireDeviceIdle ? 1 : 0);
         out.writeTypedArray(triggerContentUris, flags);
+        out.writeLong(triggerContentUpdateDelay);
+        out.writeLong(triggerContentMaxDelay);
         out.writeInt(networkType);
         out.writeLong(minLatencyMillis);
         out.writeLong(maxExecutionDelayMillis);
@@ -467,6 +506,8 @@
         private boolean mRequiresDeviceIdle;
         private int mNetworkType;
         private ArrayList<TriggerContentUri> mTriggerContentUris;
+        private long mTriggerContentUpdateDelay = -1;
+        private long mTriggerContentMaxDelay = -1;
         private boolean mIsPersisted;
         // One-off parameters.
         private long mMinLatencyMillis;
@@ -573,6 +614,27 @@
         }
 
         /**
+         * Set the delay (in milliseconds) from when a content change is detected until
+         * the job is scheduled.  If there are more changes during that time, the delay
+         * will be reset to start at the time of the most recent change.
+         * @param durationMs Delay after most recent content change, in milliseconds.
+         */
+        public Builder setTriggerContentUpdateDelay(long durationMs) {
+            mTriggerContentUpdateDelay = durationMs;
+            return this;
+        }
+
+        /**
+         * Set the maximum total delay (in milliseconds) that is allowed from the first
+         * time a content change is detected until the job is scheduled.
+         * @param durationMs Delay after initial content change, in milliseconds.
+         */
+        public Builder setTriggerContentMaxDelay(long durationMs) {
+            mTriggerContentMaxDelay = durationMs;
+            return this;
+        }
+
+        /**
          * Specify that this job should recur with the provided interval, not more than once per
          * period. You have no control over when within this interval this job will be executed,
          * only the guarantee that it will be executed at most once within this interval.
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/net/metrics/IpConnectivityEvent.java b/core/java/android/net/metrics/IpConnectivityEvent.java
index ec42890..59c1cfe 100644
--- a/core/java/android/net/metrics/IpConnectivityEvent.java
+++ b/core/java/android/net/metrics/IpConnectivityEvent.java
@@ -28,14 +28,17 @@
     // DHCP = DhcpClient
     // NETMON = NetworkMonitorEvent
     // CONSRV = ConnectivityServiceEvent
+    // IPMGR = IpManager
     public static final String TAG = "IpConnectivityEvent";
     public static final int IPCE_IPRM_BASE = 0*1024;
     public static final int IPCE_DHCP_BASE = 1*1024;
     public static final int IPCE_NETMON_BASE = 2*1024;
     public static final int IPCE_CONSRV_BASE = 3*1024;
+    public static final int IPCE_IPMGR_BASE = 4*1024;
 
     public static final int IPCE_IPRM_PROBE_RESULT = IPCE_IPRM_BASE + 0;
     public static final int IPCE_IPRM_MESSAGE_RECEIVED = IPCE_IPRM_BASE + 1;
+    public static final int IPCE_IPRM_REACHABILITY_LOST = IPCE_IPRM_BASE + 2;
     public static final int IPCE_DHCP_RECV_ERROR = IPCE_DHCP_BASE + 0;
     public static final int IPCE_DHCP_PARSE_ERROR = IPCE_DHCP_BASE + 1;
     public static final int IPCE_DHCP_TIMEOUT = IPCE_DHCP_BASE + 2;
@@ -43,6 +46,9 @@
     public static final int IPCE_NETMON_STATE_CHANGE = IPCE_NETMON_BASE + 0;
     public static final int IPCE_NETMON_CHECK_RESULT = IPCE_NETMON_BASE + 1;
     public static final int IPCE_CONSRV_DEFAULT_NET_CHANGE = IPCE_CONSRV_BASE + 0;
+    public static final int IPCE_IPMGR_PROVISIONING_OK = IPCE_IPMGR_BASE + 0;
+    public static final int IPCE_IPMGR_PROVISIONING_FAIL = IPCE_IPMGR_BASE + 1;
+    public static final int IPCE_IPMGR_COMPLETE_LIFECYCLE = IPCE_IPMGR_BASE + 2;
 
     private static ConnectivityMetricsLogger mMetricsLogger = new ConnectivityMetricsLogger();
 
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
new file mode 100644
index 0000000..6328ccb
--- /dev/null
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -0,0 +1,58 @@
+/*
+ * 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.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class IpManagerEvent extends IpConnectivityEvent implements Parcelable {
+    private String mIfName;
+    private long mDurationMs;
+
+    public IpManagerEvent(String ifName, long duration) {
+        mIfName = ifName;
+        mDurationMs = duration;
+    }
+
+    public IpManagerEvent(Parcel in) {
+        mIfName = in.readString();
+        mDurationMs = in.readLong();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mIfName);
+        out.writeLong(mDurationMs);
+    }
+
+    public static final Parcelable.Creator<IpManagerEvent> CREATOR
+        = new Parcelable.Creator<IpManagerEvent>() {
+        public IpManagerEvent createFromParcel(Parcel in) {
+            return new IpManagerEvent(in);
+        }
+
+        public IpManagerEvent[] newArray(int size) {
+            return new IpManagerEvent[size];
+        }
+    };
+
+    public static void logEvent(int eventType, String ifName, long durationMs) {
+        IpConnectivityEvent.logEvent(eventType, new IpManagerEvent(ifName, durationMs));
+    }
+};
diff --git a/core/java/android/net/metrics/IpReachabilityMonitorLostEvent.java b/core/java/android/net/metrics/IpReachabilityMonitorLostEvent.java
new file mode 100644
index 0000000..0f14210
--- /dev/null
+++ b/core/java/android/net/metrics/IpReachabilityMonitorLostEvent.java
@@ -0,0 +1,56 @@
+/*
+ * 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.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class IpReachabilityMonitorLostEvent extends IpConnectivityEvent
+        implements Parcelable {
+    private String mIfName;
+
+    public IpReachabilityMonitorLostEvent(String ifName) {
+        mIfName = ifName;
+    }
+
+    public IpReachabilityMonitorLostEvent(Parcel in) {
+        mIfName = in.readString();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mIfName);
+    }
+
+    public static final Parcelable.Creator<IpReachabilityMonitorLostEvent> CREATOR
+        = new Parcelable.Creator<IpReachabilityMonitorLostEvent>() {
+        public IpReachabilityMonitorLostEvent createFromParcel(Parcel in) {
+            return new IpReachabilityMonitorLostEvent(in);
+        }
+
+        public IpReachabilityMonitorLostEvent[] newArray(int size) {
+            return new IpReachabilityMonitorLostEvent[size];
+        }
+    };
+
+    public static void logEvent(String ifName) {
+        IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_REACHABILITY_LOST,
+                new IpReachabilityMonitorLostEvent(ifName));
+    }
+};
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/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index 495fea6..a70c24d 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -105,6 +105,18 @@
         out.writeString(mGid2);
     }
 
+    @Override
+    public String toString() {
+      return "CarrierIdentifier{"
+          + "mcc=" + mMcc
+          + ",mnc=" + mMnc
+          + ",spn=" + mSpn
+          + ",imsi=" + mImsi
+          + ",gid1=" + mGid1
+          + ",gid2=" + mGid2
+          + "}";
+    }
+
     /** @hide */
     public void readFromParcel(Parcel in) {
         mMcc = in.readString();
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..a3a40eb 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.
          */
@@ -1764,13 +1774,23 @@
                 title = "";
 
             mTitle = TextUtils.stringOrSpannedString(title);
-            accessibilityTitle = mTitle;
         }
 
         public final CharSequence getTitle() {
             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 +1842,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 +1895,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 +2097,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 +2227,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/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 02d2a8b..c1076e7 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3842,7 +3842,15 @@
          */
         public static RangeInfo obtain(int type, float min, float max, float current) {
             RangeInfo info = sPool.acquire();
-            return (info != null) ? info : new RangeInfo(type, min, max, current);
+            if (info == null) {
+                return new RangeInfo(type, min, max, current);
+            }
+
+            info.mType = type;
+            info.mMin = min;
+            info.mMax = max;
+            info.mCurrent = current;
+            return info;
         }
 
         /**
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/TextClock.java b/core/java/android/widget/TextClock.java
index ff10287..a585d75 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -555,7 +555,15 @@
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
 
-        getContext().registerReceiver(mIntentReceiver, filter, null, getHandler());
+        // OK, this is gross but needed. This class is supported by the
+        // remote views mechanism and as a part of that the remote views
+        // can be inflated by a context for another user without the app
+        // having interact users permission - just for loading resources.
+        // For example, when adding widgets from a managed profile to the
+        // home screen. Therefore, we register the receiver as the user
+        // the app is running as not the one the context is for.
+        getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
+                filter, null, getHandler());
     }
 
     private void registerObserver() {
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/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index a9d5113..7e9587a 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -90,6 +90,15 @@
         return str.toUpperCase();
     }
 
+    // For some locales we want to use a "dialect" form, for instance
+    // "Dari" instead of "Persian (Afghanistan)", or "Moldavian" instead of "Romanian (Moldova)"
+    private static boolean shouldUseDialectName(Locale locale) {
+        final String lang = locale.getLanguage();
+        return "fa".equals(lang) // Persian
+                || "ro".equals(lang) // Romanian
+                || "zh".equals(lang); // Chinese
+    }
+
     /**
      * Returns the locale localized for display in the provided locale.
      *
@@ -99,8 +108,10 @@
      * @return the localized name of the locale.
      */
     public static String getDisplayName(Locale locale, Locale displayLocale, boolean sentenceCase) {
-        String result = ULocale.getDisplayNameWithDialect(locale.toLanguageTag(),
-                ULocale.forLocale(displayLocale));
+        final ULocale displayULocale = ULocale.forLocale(displayLocale);
+        String result = shouldUseDialectName(locale)
+                ? ULocale.getDisplayNameWithDialect(locale.toLanguageTag(), displayULocale)
+                : ULocale.getDisplayName(locale.toLanguageTag(), displayULocale);
         return sentenceCase ? toSentenceCase(result, displayLocale) : result;
     }
 
@@ -112,9 +123,7 @@
      * @return the localized name of the locale.
      */
     public static String getDisplayName(Locale locale, boolean sentenceCase) {
-        String result = ULocale.getDisplayNameWithDialect(locale.toLanguageTag(),
-                ULocale.getDefault());
-        return sentenceCase ? toSentenceCase(result, Locale.getDefault()) : result;
+        return getDisplayName(locale, Locale.getDefault(), sentenceCase);
     }
 
     /**
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/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 21c4d12..b2fc2bb 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -43,4 +43,5 @@
     void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
     void requireStrongAuth(int strongAuthReason, int userId);
     void systemReady();
+    void userPresent(int userId);
 }
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/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index bceeaca..d9b6329 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -172,6 +172,14 @@
         }
     }
 
+    public void userPresent(int userId) {
+        try {
+            getLockSettings().userPresent(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     public static final class RequestThrottledException extends Exception {
         private int mTimeoutMs;
         public RequestThrottledException(int timeoutMs) {
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/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c4347f8..8b9d503 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,9 +17,15 @@
 
 package com.android.internal.widget;
 
+import com.android.internal.R;
+
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -38,7 +44,6 @@
 import android.view.animation.AnimationUtils;
 import android.widget.AbsListView;
 import android.widget.OverScroller;
-import com.android.internal.R;
 
 public class ResolverDrawerLayout extends ViewGroup {
     private static final String TAG = "ResolverDrawerLayout";
@@ -86,6 +91,8 @@
     private final OverScroller mScroller;
     private final VelocityTracker mVelocityTracker;
 
+    private Drawable mScrollIndicatorDrawable;
+
     private OnDismissedListener mOnDismissedListener;
     private RunOnDismissedListener mRunOnDismissedListener;
 
@@ -127,6 +134,8 @@
                 mMaxCollapsedHeight);
         a.recycle();
 
+        mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
+
         mScroller = new OverScroller(context, AnimationUtils.loadInterpolator(context,
                 android.R.interpolator.decelerate_quint));
         mVelocityTracker = VelocityTracker.obtain();
@@ -202,8 +211,7 @@
             }
             final boolean isCollapsedNew = mCollapseOffset != 0;
             if (isCollapsedOld != isCollapsedNew) {
-                notifyViewAccessibilityStateChangedIfNeeded(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                onCollapsedChanged(isCollapsedNew);
             }
         } else {
             // Start out collapsed at first unless we restored state for otherwise
@@ -442,8 +450,7 @@
             mTopOffset += dy;
             final boolean isCollapsedNew = newPos != 0;
             if (isCollapsedOld != isCollapsedNew) {
-                notifyViewAccessibilityStateChangedIfNeeded(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                onCollapsedChanged(isCollapsedNew);
             }
             postInvalidateOnAnimation();
             return dy;
@@ -451,6 +458,15 @@
         return 0;
     }
 
+    private void onCollapsedChanged(boolean isCollapsed) {
+        notifyViewAccessibilityStateChangedIfNeeded(
+                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+
+        if (mScrollIndicatorDrawable != null) {
+            setWillNotDraw(!isCollapsed);
+        }
+    }
+
     void dispatchOnDismissed() {
         if (mOnDismissedListener != null) {
             mOnDismissedListener.onDismissed();
@@ -709,6 +725,15 @@
     }
 
     @Override
+    public void onDrawForeground(Canvas canvas) {
+        if (mScrollIndicatorDrawable != null) {
+            mScrollIndicatorDrawable.draw(canvas);
+        }
+
+        super.onDrawForeground(canvas);
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec);
         int widthSize = sourceWidth;
@@ -794,6 +819,8 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int width = getWidth();
 
+        View indicatorHost = null;
+
         int ypos = mTopOffset;
         int leftEdge = getPaddingLeft();
         int rightEdge = width - getPaddingRight();
@@ -802,6 +829,9 @@
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (lp.hasNestedScrollIndicator) {
+                indicatorHost = child;
+            }
 
             if (child.getVisibility() == GONE) {
                 continue;
@@ -822,6 +852,20 @@
 
             ypos = bottom + lp.bottomMargin;
         }
+
+        if (mScrollIndicatorDrawable != null) {
+            if (indicatorHost != null) {
+                final int left = indicatorHost.getLeft();
+                final int right = indicatorHost.getRight();
+                final int bottom = indicatorHost.getTop();
+                final int top = bottom - mScrollIndicatorDrawable.getIntrinsicHeight();
+                mScrollIndicatorDrawable.setBounds(left, top, right, bottom);
+                setWillNotDraw(!isCollapsed());
+            } else {
+                mScrollIndicatorDrawable = null;
+                setWillNotDraw(true);
+            }
+        }
     }
 
     @Override
@@ -861,6 +905,7 @@
     public static class LayoutParams extends MarginLayoutParams {
         public boolean alwaysShow;
         public boolean ignoreOffset;
+        public boolean hasNestedScrollIndicator;
 
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
@@ -873,6 +918,9 @@
             ignoreOffset = a.getBoolean(
                     R.styleable.ResolverDrawerLayout_LayoutParams_layout_ignoreOffset,
                     false);
+            hasNestedScrollIndicator = a.getBoolean(
+                    R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator,
+                    false);
             a.recycle();
         }
 
@@ -884,6 +932,7 @@
             super(source);
             this.alwaysShow = source.alwaysShow;
             this.ignoreOffset = source.ignoreOffset;
+            this.hasNestedScrollIndicator = source.hasNestedScrollIndicator;
         }
 
         public LayoutParams(MarginLayoutParams source) {
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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/drawable/ic_input_extract_action_done.xml
similarity index 60%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/drawable/ic_input_extract_action_done.xml
index 681ff91..f6e872e 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/drawable/ic_input_extract_action_done.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/drawable/ic_input_extract_action_go.xml
similarity index 60%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/drawable/ic_input_extract_action_go.xml
index 681ff91..edbc826 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/drawable/ic_input_extract_action_go.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/drawable/ic_input_extract_action_return.xml
similarity index 60%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/drawable/ic_input_extract_action_return.xml
index 681ff91..cb2de5a 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/drawable/ic_input_extract_action_return.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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..6494bee
--- /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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
similarity index 62%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/drawable/input_extract_action_bg_material_dark.xml
index 681ff91..9c6a6c3 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
similarity index 64%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
index 681ff91..8449978 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +14,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
similarity index 64%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
index 681ff91..adade104 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +14,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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..ae94503 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -30,33 +30,37 @@
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
         android:elevation="8dp"
-        android:background="@color/white" >
-        <TextView android:id="@+id/profile_button"
-                  android:layout_width="wrap_content"
-                  android:layout_height="48dp"
-                  android:layout_marginEnd="8dp"
-                  android:paddingStart="8dp"
-                  android:paddingEnd="8dp"
-                  android:visibility="gone"
-                  style="?attr/borderlessButtonStyle"
-                  android:textAppearance="?attr/textAppearanceButton"
-                  android:textColor="@color/material_deep_teal_500"
-                  android:gravity="center_vertical"
-                  android:layout_alignParentTop="true"
-                  android:layout_alignParentRight="true"
-                  android:singleLine="true"/>
-        <TextView android:id="@+id/title"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:minHeight="56dp"
-                  android:textAppearance="?attr/textAppearanceMedium"
-                  android:gravity="start|center_vertical"
-                  android:paddingStart="?attr/dialogPreferredPadding"
-                  android:paddingEnd="?attr/dialogPreferredPadding"
-                  android:paddingTop="8dp"
-                  android:layout_below="@id/profile_button"
-                  android:layout_alignParentLeft="true"
-                  android:paddingBottom="8dp" />
+        android:background="@color/white">
+
+        <TextView
+            android:id="@+id/profile_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_marginEnd="8dp"
+            android:paddingStart="8dp"
+            android:paddingEnd="8dp"
+            android:visibility="gone"
+            style="?attr/borderlessButtonStyle"
+            android:textAppearance="?attr/textAppearanceButton"
+            android:textColor="@color/material_deep_teal_500"
+            android:gravity="center_vertical"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentRight="true"
+            android:singleLine="true" />
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minHeight="56dp"
+            android:textAppearance="?attr/textAppearanceMedium"
+            android:gravity="start|center_vertical"
+            android:paddingStart="?attr/dialogPreferredPadding"
+            android:paddingEnd="?attr/dialogPreferredPadding"
+            android:paddingTop="8dp"
+            android:layout_below="@id/profile_button"
+            android:layout_alignParentLeft="true"
+            android:paddingBottom="8dp" />
     </RelativeLayout>
 
     <ListView
@@ -68,11 +72,14 @@
         android:background="@color/white"
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
+        android:scrollIndicators="top|bottom"
         android:divider="@null" />
 
     <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"
@@ -82,11 +89,12 @@
     <LinearLayout
         android:id="@+id/button_bar"
         android:visibility="gone"
-        style="?android:attr/buttonBarStyle"
+        style="?attr/buttonBarStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_ignoreOffset="true"
         android:layout_alwaysShow="true"
+        android:layout_hasNestedScrollIndicator="true"
         android:gravity="end|center_vertical"
         android:orientation="horizontal"
         android:layoutDirection="locale"
@@ -97,26 +105,30 @@
         android:paddingStart="12dp"
         android:paddingEnd="12dp"
         android:elevation="8dp">
-        <Button android:id="@+id/button_once"
-                android:layout_width="wrap_content"
-                android:layout_gravity="start"
-                android:maxLines="2"
-                style="?android:attr/buttonBarNegativeButtonStyle"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="@string/activity_resolver_use_once"
-                android:onClick="onButtonClick" />
-        <Button android:id="@+id/button_always"
-                android:layout_width="wrap_content"
-                android:layout_gravity="end"
-                android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?android:attr/buttonBarPositiveButtonStyle"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="@string/activity_resolver_use_always"
-                android:onClick="onButtonClick" />
+
+        <Button
+            android:id="@+id/button_once"
+            android:layout_width="wrap_content"
+            android:layout_gravity="start"
+            android:maxLines="2"
+            style="?attr/buttonBarNegativeButtonStyle"
+            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/activity_resolver_use_once"
+            android:onClick="onButtonClick" />
+
+        <Button
+            android:id="@+id/button_always"
+            android:layout_width="wrap_content"
+            android:layout_gravity="end"
+            android:maxLines="2"
+            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            style="?attr/buttonBarPositiveButtonStyle"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/activity_resolver_use_always"
+            android:onClick="onButtonClick" />
     </LinearLayout>
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 31361e5..02dc2ed 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -22,8 +22,7 @@
     android:layout_height="match_parent"
     android:maxWidth="@dimen/resolver_max_width"
     android:maxCollapsedHeight="144dp"
-    android:id="@id/contentPanel"
-    >
+    android:id="@id/contentPanel">
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -31,66 +30,75 @@
         android:layout_alwaysShow="true"
         android:orientation="vertical"
         android:background="@color/white"
-        android:elevation="8dp" >
+        android:elevation="8dp">
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="64dp"
-            android:orientation="horizontal" >
+            android:orientation="horizontal">
 
-            <ImageView android:id="@+id/icon"
-                       android:layout_width="24dp"
-                       android:layout_height="24dp"
-                       android:layout_gravity="start|top"
-                       android:layout_marginStart="16dp"
-                       android:layout_marginEnd="16dp"
-                       android:layout_marginTop="20dp"
-                       android:scaleType="fitCenter" />
-            <TextView android:id="@+id/title"
-                      android:layout_width="0dp"
-                      android:layout_weight="1"
-                      android:layout_height="?android:attr/listPreferredItemHeight"
-                      android:layout_marginStart="16dp"
-                      android:textAppearance="?android:attr/textAppearanceMedium"
-                      android:gravity="start|center_vertical"
-                      android:paddingEnd="16dp" />
-            <LinearLayout android:id="@+id/profile_button"
-                          android:layout_width="wrap_content"
-                          android:layout_height="48dp"
-                          android:layout_marginTop="4dp"
-                          android:layout_marginEnd="4dp"
-                          android:paddingStart="8dp"
-                          android:paddingEnd="8dp"
-                          android:paddingTop="4dp"
-                          android:paddingBottom="4dp"
-                          android:focusable="true"
-                          android:visibility="gone"
-                          style="?attr/borderlessButtonStyle">
-                <ImageView android:id="@+id/icon"
-                           android:layout_width="24dp"
-                           android:layout_height="24dp"
-                           android:layout_gravity="start|center_vertical"
-                           android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
-                           android:layout_marginTop="12dp"
-                           android:layout_marginBottom="12dp"
-                           android:scaleType="fitCenter" />
-                <TextView android:id="@id/text1"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:layout_gravity="start|center_vertical"
-                          android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
-                          android:textAppearance="?attr/textAppearanceButton"
-                          android:textColor="?attr/textColorPrimary"
-                          android:minLines="1"
-                          android:maxLines="1"
-                          android:ellipsize="marquee" />
+            <ImageView
+                android:id="@+id/icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="start|top"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="16dp"
+                android:layout_marginTop="20dp"
+                android:scaleType="fitCenter" />
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="?attr/listPreferredItemHeight"
+                android:layout_marginStart="16dp"
+                android:textAppearance="?attr/textAppearanceMedium"
+                android:gravity="start|center_vertical"
+                android:paddingEnd="16dp" />
+
+            <LinearLayout
+                android:id="@+id/profile_button"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:layout_marginTop="4dp"
+                android:layout_marginEnd="4dp"
+                android:paddingStart="8dp"
+                android:paddingEnd="8dp"
+                android:paddingTop="4dp"
+                android:paddingBottom="4dp"
+                android:focusable="true"
+                android:visibility="gone"
+                style="?attr/borderlessButtonStyle">
+
+                <ImageView
+                    android:id="@+id/icon"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:layout_gravity="start|center_vertical"
+                    android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                    android:layout_marginTop="12dp"
+                    android:layout_marginBottom="12dp"
+                    android:scaleType="fitCenter" />
+
+                <TextView
+                    android:id="@id/text1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="start|center_vertical"
+                    android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                    android:textAppearance="?attr/textAppearanceButton"
+                    android:textColor="?attr/textColorPrimary"
+                    android:minLines="1"
+                    android:maxLines="1"
+                    android:ellipsize="marquee" />
             </LinearLayout>
         </LinearLayout>
 
         <LinearLayout
             android:id="@+id/button_bar"
             android:visibility="gone"
-            style="?android:attr/buttonBarStyle"
+            style="?attr/buttonBarStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_alwaysShow="true"
@@ -104,30 +112,36 @@
             android:paddingEnd="12dp"
             android:background="@color/white"
             android:elevation="8dp">
-            <Button android:id="@+id/button_once"
-                    android:layout_width="wrap_content"
-                    android:layout_gravity="start"
-                    android:maxLines="2"
-                    style="?android:attr/buttonBarNegativeButtonStyle"
-                    android:minHeight="@dimen/alert_dialog_button_bar_height"
-                    android:layout_height="wrap_content"
-                    android:enabled="false"
-                    android:text="@string/activity_resolver_use_once"
-                    android:onClick="onButtonClick" />
-            <Button android:id="@+id/button_always"
-                    android:layout_width="wrap_content"
-                    android:layout_gravity="end"
-                    android:maxLines="2"
-                    android:minHeight="@dimen/alert_dialog_button_bar_height"
-                    style="?android:attr/buttonBarPositiveButtonStyle"
-                    android:layout_height="wrap_content"
-                    android:enabled="false"
-                    android:text="@string/activity_resolver_use_always"
-                    android:onClick="onButtonClick" />
+
+            <Button
+                android:id="@+id/button_once"
+                android:layout_width="wrap_content"
+                android:layout_gravity="start"
+                android:maxLines="2"
+                style="?attr/buttonBarNegativeButtonStyle"
+                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/activity_resolver_use_once"
+                android:onClick="onButtonClick" />
+
+            <Button
+                android:id="@+id/button_always"
+                android:layout_width="wrap_content"
+                android:layout_gravity="end"
+                android:maxLines="2"
+                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                style="?attr/buttonBarPositiveButtonStyle"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/activity_resolver_use_always"
+                android:onClick="onButtonClick" />
         </LinearLayout>
-        <View android:layout_width="match_parent"
-              android:layout_height="1dp"
-              android:background="?android:attr/dividerVertical" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="?attr/dividerVertical" />
     </LinearLayout>
 
     <ListView
@@ -139,7 +153,6 @@
         android:background="@color/white"
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
-        android:divider="@null"
-        />
+        android:divider="@null" />
 
 </com.android.internal.widget.ResolverDrawerLayout>
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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/core/res/res/values-w170dp-notround-watch/dimens.xml
similarity index 64%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to core/res/res/values-w170dp-notround-watch/dimens.xml
index 681ff91..c91cbc1 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/core/res/res/values-w170dp-notround-watch/dimens.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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..85e0b88 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8129,10 +8129,16 @@
         <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" />
         <attr name="layout_gravity" />
+        <attr name="layout_hasNestedScrollIndicator" format="boolean" />
     </declare-styleable>
 
     <!-- @hide -->
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..0a99239 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
@@ -4028,6 +4048,9 @@
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
     <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
 
+    <!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
+    <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
+
     <!-- Zen mode condition - summary: time duration in minutes. [CHAR LIMIT=NONE] -->
     <plurals name="zen_mode_duration_minutes_summary">
         <item quantity="one">For one minute (until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g>)</item>
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..541ee75 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" />
@@ -2105,6 +2102,7 @@
   <java-symbol type="dimen" name="timepicker_text_size_normal" />
   <java-symbol type="dimen" name="timepicker_text_size_inner" />
   <java-symbol type="string" name="battery_saver_description" />
+  <java-symbol type="string" name="data_saver_description" />
   <java-symbol type="string" name="zen_mode_forever" />
   <java-symbol type="string" name="zen_mode_forever_dnd" />
   <java-symbol type="string" name="zen_mode_rule_name_combination" />
@@ -2201,14 +2199,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 +2244,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 +2503,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 +2534,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 4f600b4..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;
     }
 
@@ -441,6 +444,9 @@
      * @param layoutDirection the resolved layout direction for the drawable,
      *                        either {@link android.view.View#LAYOUT_DIRECTION_LTR}
      *                        or {@link android.view.View#LAYOUT_DIRECTION_RTL}
+     * @return {@code true} if the layout direction change has caused the
+     *         appearance of the drawable to change such that it needs to be
+     *         re-drawn, {@code false} otherwise
      * @see #getLayoutDirection()
      */
     public final boolean setLayoutDirection(@View.ResolvedLayoutDir int layoutDirection) {
@@ -455,8 +461,9 @@
      * Called when the drawable's resolved layout direction changes.
      *
      * @param layoutDirection the new resolved layout direction
-     * @return true if the layout direction change has caused the appearance of
-     *         the drawable to change and it needs to be re-drawn
+     * @return {@code true} if the layout direction change has caused the
+     *         appearance of the drawable to change such that it needs to be
+     *         re-drawn, {@code false} otherwise
      * @see #setLayoutDirection(int)
      */
     public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
@@ -467,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,
@@ -476,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;
     }
@@ -489,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!
     }
 
@@ -592,7 +600,7 @@
      *
      * @return the current color filter, or {@code null} if none set
      */
-    public ColorFilter getColorFilter() {
+    public @Nullable ColorFilter getColorFilter() {
         return null;
     }
 
@@ -629,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());
     }
 
@@ -677,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);
@@ -692,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;
     }
 
@@ -709,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;
     }
 
@@ -729,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);
@@ -742,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;
     }
 
@@ -839,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
@@ -856,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;
         }
@@ -885,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;
     }
 
@@ -898,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
@@ -906,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.
@@ -986,7 +1003,7 @@
      *
      * @hide
      */
-    public Insets getOpticalInsets() {
+    public @NonNull Insets getOpticalInsets() {
         return Insets.NONE;
     }
 
@@ -1020,7 +1037,7 @@
      * @see ConstantState
      * @see #getConstantState()
      */
-    public Drawable mutate() {
+    public @NonNull Drawable mutate() {
         return this;
     }
 
@@ -1126,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) {
@@ -1222,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);
     }
 
@@ -1254,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
@@ -1269,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();
         }
 
@@ -1288,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);
         }
 
@@ -1303,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;
         }
 
@@ -1327,7 +1344,7 @@
      * @see ConstantState
      * @see Drawable#mutate()
      */
-    public ConstantState getConstantState() {
+    public @Nullable ConstantState getConstantState() {
         return null;
     }
 
@@ -1345,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;
         }
@@ -1365,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);
         }
@@ -1418,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 {
@@ -1440,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/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml
similarity index 70%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to libs/androidfw/tests/data/system/res/values-sv/values.xml
index 681ff91..b97bdb6 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+<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/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl b/libs/hwui/Readback.h
similarity index 63%
rename from telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
rename to libs/hwui/Readback.h
index 1e74b31..ea03c82 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
+++ b/libs/hwui/Readback.h
@@ -14,6 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony;
+#pragma once
 
-parcelable TelephonyEvent;
+#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..691cfa0 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..a33b219 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,7 +16,9 @@
 
 package android.media.tv;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.StringRes;
 import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
@@ -48,6 +50,8 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
@@ -62,6 +66,12 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "TvInputInfo";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TYPE_TUNER, TYPE_OTHER, TYPE_COMPOSITE, TYPE_SVIDEO, TYPE_SCART, TYPE_COMPONENT,
+            TYPE_VGA, TYPE_DVI, TYPE_HDMI, TYPE_DISPLAY_PORT})
+    public @interface Type {}
+
     // Should be in sync with frameworks/base/core/res/res/values/attrs.xml
     /**
      * TV input type: the TV input service is a tuner which provides channels.
@@ -343,6 +353,7 @@
     /**
      * Returns the type of this TV input.
      */
+    @Type
     public int getType() {
         return mType;
     }
@@ -369,8 +380,7 @@
     }
 
     /**
-     * Returns the extras associated with this TV input.
-     * @hide
+     * Returns domain-specific extras associated with this TV input.
      */
     public Bundle getExtras() {
         return mExtras;
@@ -769,7 +779,7 @@
          * @hide
          */
         @SystemApi
-        public Builder setLabel(int resId) {
+        public Builder setLabel(@StringRes int resId) {
             if (mLabel != null) {
                 throw new IllegalStateException("Label text is already set.");
             }
@@ -847,11 +857,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/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index b4536b1..d74dda6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -99,6 +99,13 @@
      */
     public static final int DVB_DEVICE_FRONTEND = DVB_DEVICE_END;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING,
+            VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING,
+            VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY})
+    public @interface VideoUnavailableReason {}
+
     static final int VIDEO_UNAVAILABLE_REASON_START = 0;
     static final int VIDEO_UNAVAILABLE_REASON_END = 4;
 
@@ -135,10 +142,9 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING,
-            VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING,
-            VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY})
-    public @interface VideoUnavailableReason {}
+    @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED,
+            TIME_SHIFT_STATUS_UNAVAILABLE, TIME_SHIFT_STATUS_AVAILABLE})
+    public @interface TimeShiftStatus {}
 
     /**
      * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and
@@ -169,12 +175,6 @@
      */
     public static final int TIME_SHIFT_STATUS_AVAILABLE = 3;
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED,
-            TIME_SHIFT_STATUS_UNAVAILABLE, TIME_SHIFT_STATUS_AVAILABLE})
-    public @interface TimeShiftStatus {}
-
     /**
      * Value returned by {@link TvInputService.Session#onTimeShiftGetCurrentPosition()} and
      * {@link TvInputService.Session#onTimeShiftGetStartPosition()} when time shifting has not
@@ -182,6 +182,12 @@
      */
     public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
+            RECORDING_ERROR_RESOURCE_BUSY})
+    public @interface RecordingError {}
+
     /**
      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
      * {@link TvRecordingClient.RecordingCallback#onError(int)}: The requested operation cannot be
@@ -205,9 +211,8 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
-            RECORDING_ERROR_RESOURCE_BUSY})
-    public @interface RecordingError {}
+    @IntDef({INPUT_STATE_CONNECTED, INPUT_STATE_CONNECTED_STANDBY, INPUT_STATE_DISCONNECTED})
+    public @interface InputState {}
 
     /**
      * State for {@link #getInputState(String)} and
@@ -240,11 +245,6 @@
      */
     public static final int INPUT_STATE_DISCONNECTED = 2;
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({INPUT_STATE_CONNECTED, INPUT_STATE_CONNECTED_STANDBY, INPUT_STATE_DISCONNECTED})
-    public @interface InputState {}
-
     /**
      * Broadcast intent action when the user blocked content ratings change. For use with the
      * {@link #isRatingBlocked}.
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 97ef6d8..3780f6f 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FloatRange;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -823,7 +824,7 @@
          *
          * @param volume A volume value between {@code 0.0f} to {@code 1.0f}.
          */
-        public abstract void onSetStreamVolume(float volume);
+        public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
 
         /**
          * Tunes to a given channel.
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index d718a7e..c9c881c 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -24,12 +25,20 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
  * Encapsulates the format of tracks played in {@link TvInputService}.
  */
 public final class TvTrackInfo implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TYPE_AUDIO, TYPE_VIDEO, TYPE_SUBTITLE})
+    public @interface Type {}
+
     /**
      * The type value for audio tracks.
      */
@@ -96,6 +105,7 @@
      * Returns the type of the track. The type should be one of the followings:
      * {@link #TYPE_AUDIO}, {@link #TYPE_VIDEO} and {@link #TYPE_SUBTITLE}.
      */
+    @Type
     public final int getType() {
         return mType;
     }
@@ -319,7 +329,7 @@
          * @throws IllegalArgumentException if the type is not any of {@link #TYPE_AUDIO},
          *                                  {@link #TYPE_VIDEO} and {@link #TYPE_SUBTITLE}
          */
-        public Builder(int type, @NonNull String id) {
+        public Builder(@Type int type, @NonNull String id) {
             if (type != TYPE_AUDIO
                     && type != TYPE_VIDEO
                     && type != TYPE_SUBTITLE) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 02ee0cc..71b0193 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -271,7 +272,7 @@
      *
      * @param volume A volume value between {@code 0.0f} to {@code 1.0f}.
      */
-    public void setStreamVolume(float volume) {
+    public void setStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume) {
         if (DEBUG) Log.d(TAG, "setStreamVolume(" + volume + ")");
         mStreamVolume = volume;
         if (mSession == null) {
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..22454ad 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;
             }
@@ -518,6 +524,7 @@
             }
 
             if (mActionMode != null) {
+                assert(!mSelected.isEmpty());
                 final String title = Shared.getQuantityString(getActivity(),
                         R.plurals.elements_selected, mSelected.size());
                 mActionMode.setTitle(title);
@@ -803,6 +810,8 @@
                                 // the user cancels the delete.
                                 if (mActionMode != null) {
                                     mActionMode.finish();
+                                } else {
+                                    Log.w(TAG, "Action mode is null before deleting documents.");
                                 }
                                 // Hide the files in the UI...since the operation
                                 // might be queued up on FileOperationService.
@@ -1104,6 +1113,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 +1205,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 +1281,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 +1413,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());
@@ -1421,7 +1439,8 @@
                     // This has to be handled here instead of in a keyboard shortcut, because
                     // keyboard shortcuts all have to be modified with the 'Ctrl' key.
                     if (mSelectionManager.hasSelection()) {
-                        deleteDocuments(mSelectionManager.getSelection());
+                        Selection selection = mSelectionManager.getSelection(new Selection());
+                        deleteDocuments(selection);
                     }
                     // Always handle the key, even if there was nothing to delete. This is a
                     // precaution to prevent other handlers from potentially picking up the event
@@ -1432,9 +1451,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/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 61966b2..09fec81 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -334,6 +334,14 @@
     <!-- An explanation text that the password needs to be entered since profiles have just been switched. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_switch_profiles_password">Password required when you switch profiles</string>
 
+    <!-- An explanation text that the credential needs to be entered because a device admin has
+    locked the device. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_device_admin">Device administrator locked device</string>
+
+    <!-- An explanation text that the credential needs to be entered because the user has clicked
+     the force lock button. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_user_request">Device was locked manually</string>
+
     <!-- An explanation text that the pattern needs to be solved since it hasn't been solved in a while. [CHAR LIMIT=80]-->
     <plurals name="kg_prompt_reason_time_pattern">
         <item quantity="one">Device hasn\'t been unlocked for <xliff:g id="number">%d</xliff:g> hour. Confirm pattern.</item>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 63dec8b..189f5b7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -117,6 +117,10 @@
                 return R.string.kg_prompt_reason_restart_password;
             case PROMPT_REASON_TIMEOUT:
                 return R.string.kg_prompt_reason_timeout_password;
+            case PROMPT_REASON_DEVICE_ADMIN:
+                return R.string.kg_prompt_reason_device_admin;
+            case PROMPT_REASON_USER_REQUEST:
+                return R.string.kg_prompt_reason_user_request;
             case PROMPT_REASON_NONE:
                 return 0;
             default:
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index be2701d..e070492 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -339,6 +339,14 @@
                 mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern,
                         true /* important */);
                 break;
+            case PROMPT_REASON_DEVICE_ADMIN:
+                mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin,
+                        true /* important */);
+                break;
+            case PROMPT_REASON_USER_REQUEST:
+                mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request,
+                        true /* important */);
+                break;
             case PROMPT_REASON_NONE:
                 break;
             default:
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index fe98cb8..f14290a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -108,6 +108,10 @@
                 return R.string.kg_prompt_reason_restart_pin;
             case PROMPT_REASON_TIMEOUT:
                 return R.string.kg_prompt_reason_timeout_pin;
+            case PROMPT_REASON_DEVICE_ADMIN:
+                return R.string.kg_prompt_reason_device_admin;
+            case PROMPT_REASON_USER_REQUEST:
+                return R.string.kg_prompt_reason_user_request;
             case PROMPT_REASON_NONE:
                 return 0;
             default:
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/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml
similarity index 92%
rename from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml
index 681ff91..257bf35 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml
@@ -18,4 +18,4 @@
     android:propertyName="alpha"
     android:valueTo="1"
     android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    android:duration="100" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml
similarity index 92%
rename from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml
index e6deb0f..e032008 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml
@@ -18,4 +18,4 @@
     android:propertyName="alpha"
     android:valueTo="0"
     android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    android:duration="100" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_menu_fade_in_animation.xml
similarity index 92%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to packages/SystemUI/res/anim/tv_pip_menu_fade_in_animation.xml
index 681ff91..257bf35 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/packages/SystemUI/res/anim/tv_pip_menu_fade_in_animation.xml
@@ -18,4 +18,4 @@
     android:propertyName="alpha"
     android:valueTo="1"
     android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    android:duration="100" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_menu_fade_out_animation.xml
similarity index 92%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
copy to packages/SystemUI/res/anim/tv_pip_menu_fade_out_animation.xml
index e6deb0f..e032008 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
+++ b/packages/SystemUI/res/anim/tv_pip_menu_fade_out_animation.xml
@@ -18,4 +18,4 @@
     android:propertyName="alpha"
     android:valueTo="0"
     android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    android:duration="100" />
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
index 405ea0c..0db1a57 100644
--- a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
@@ -14,11 +14,5 @@
      limitations under the License.
 -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-    <size
-        android:width="34dp"
-        android:height="34dp" />
-    <solid
-        android:color="#4DFFFFFF" />
-</shape>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="#9AFFFFFF" android:radius="17dp" />
diff --git a/packages/SystemUI/res/drawable/tv_pip_close_button.xml b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
deleted file mode 100644
index 186a4ba..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_close_button.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:constantSize="true">
-    <item android:state_focused="true">
-        <layer-list>
-            <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_close_white"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-    <item>
-        <layer-list>
-            <item android:drawable="@drawable/ic_close_white"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_full_button.xml b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
deleted file mode 100644
index c48dc828..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_full_button.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:constantSize="true">
-    <item android:state_focused="true">
-        <layer-list>
-            <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_fullscreen_white_24dp"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-    <item>
-        <layer-list>
-            <item android:drawable="@drawable/ic_fullscreen_white_24dp"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml b/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
index e247dec..2b58fc5 100644
--- a/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
@@ -16,8 +16,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
 
-    <gradient
-        android:startColor="#B2000000"
-        android:endColor="#00000000"
-        android:angle="90"/>
+    <stroke android:width="1dp" android:color="#33FFFFFF" />
 </shape>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/drawable/tv_pip_overlay_text_background.xml
similarity index 61%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy to packages/SystemUI/res/drawable/tv_pip_overlay_text_background.xml
index 681ff91..e247dec 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_overlay_text_background.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     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.
@@ -13,9 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
 
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="alpha"
-    android:valueTo="1"
-    android:interpolator="@android:interpolator/fast_out_slow_in"
-    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    <gradient
+        android:startColor="#B2000000"
+        android:endColor="#00000000"
+        android:angle="90"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
deleted file mode 100644
index bcc8973..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:constantSize="true">
-    <item android:state_focused="true">
-        <layer-list>
-            <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_pause_white_24dp"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-    <item>
-        <layer-list>
-            <item android:drawable="@drawable/ic_pause_white_24dp"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_play_button.xml b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
deleted file mode 100644
index f77ea1d..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_play_button.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:constantSize="true">
-    <item android:state_focused="true">
-        <layer-list>
-            <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_play_arrow_white_24dp"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-    <item>
-        <layer-list>
-            <item android:drawable="@drawable/ic_play_arrow_white_24dp"
-                android:top="@dimen/tv_pip_button_icon_padding"
-                android:bottom="@dimen/tv_pip_button_icon_padding"
-                android:left="@dimen/tv_pip_button_icon_padding"
-                android:right="@dimen/tv_pip_button_icon_padding" />
-        </layer-list>
-    </item>
-</selector>
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/layout/tv_pip_control_button.xml b/packages/SystemUI/res/layout/tv_pip_control_button.xml
new file mode 100644
index 0000000..0beeda1
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_control_button.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Layout for {@link com.android.systemui.tv.pip.PipControlButtonView}. -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <ImageView android:id="@+id/button"
+        android:layout_width="34dp"
+        android:layout_height="34dp"
+        android:padding="5dp"
+        android:focusable="true"
+        android:src="@drawable/ic_fullscreen_white_24dp"
+        android:background="@drawable/tv_pip_button_focused"
+        android:layerType="software" />
+
+    <TextView android:id="@+id/desc"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="3dp"
+        android:gravity="center"
+        android:text="@string/pip_fullscreen"
+        android:alpha="0"
+        android:fontFamily="sans-serif"
+        android:textSize="12sp"
+        android:textColor="#EEEEEE" />
+</merge>
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index 563441f..77450e6 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -20,77 +20,26 @@
 <!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <LinearLayout
+    <com.android.systemui.tv.pip.PipControlsButtonView
+        android:id="@+id/full_button"
         android:layout_width="100dp"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:gravity="center">
+        android:src="@drawable/ic_fullscreen_white_24dp"
+        android:text="@string/pip_fullscreen" />
 
-        <ImageView android:id="@+id/full_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:src="@drawable/tv_pip_full_button" />
-
-        <TextView android:id="@+id/full_desc"
-            android:layout_width="100dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="3dp"
-            android:gravity="center"
-            android:visibility="invisible"
-            android:text="@string/pip_fullscreen"
-            android:fontFamily="sans-serif"
-            android:textSize="12sp"
-            android:textColor="#EEEEEE" />
-    </LinearLayout>
-
-    <LinearLayout
+    <com.android.systemui.tv.pip.PipControlsButtonView
+        android:id="@+id/close_button"
         android:layout_width="100dp"
         android:layout_height="wrap_content"
         android:layout_marginStart="-50dp"
-        android:orientation="vertical"
-        android:gravity="center">
+        android:src="@drawable/ic_close_white"
+        android:text="@string/pip_close" />
 
-        <ImageView android:id="@+id/close_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:src="@drawable/tv_pip_close_button" />
-
-        <TextView android:id="@+id/close_desc"
-            android:layout_width="100dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="3dp"
-            android:gravity="center"
-            android:visibility="invisible"
-            android:text="@string/pip_close"
-            android:fontFamily="sans-serif"
-            android:textSize="12sp"
-            android:textColor="#EEEEEE" />
-    </LinearLayout>
-
-    <LinearLayout android:id="@+id/play_pause"
+    <com.android.systemui.tv.pip.PipControlsButtonView
+        android:id="@+id/play_pause_button"
         android:layout_width="100dp"
         android:layout_height="wrap_content"
         android:layout_marginStart="-50dp"
-        android:orientation="vertical"
-        android:gravity="center" >
-
-        <ImageView android:id="@+id/play_pause_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:src="@drawable/tv_pip_pause_button" />
-
-        <TextView android:id="@+id/play_pause_desc"
-            android:layout_width="100dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="3dp"
-            android:gravity="center"
-            android:visibility="invisible"
-            android:text="@string/pip_pause"
-            android:fontFamily="sans-serif"
-            android:textSize="12sp"
-            android:textColor="#EEEEEE" />
-    </LinearLayout>
+        android:src="@drawable/ic_pause_white_24dp"
+        android:text="@string/pip_pause" />
 </merge>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 2647a99..72a4929 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -29,5 +29,6 @@
     <com.android.systemui.tv.pip.PipControlsView
         android:id="@+id/pip_controls"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:alpha="0" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index 64bf3b5..608680c 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -19,7 +19,8 @@
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="@drawable/tv_pip_overlay_background">
 
     <TextView
         android:id="@+id/guide_overlay"
@@ -33,7 +34,7 @@
         android:textSize="14sp"
         android:textColor="#EEEEEE"
         android:fontFamily="sans-serif"
-        android:background="@drawable/tv_pip_overlay_background"
+        android:background="@drawable/tv_pip_overlay_text_background"
         android:lineSpacingMultiplier="1.465"
         android:gravity="center"
         android:maxLines="2"
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/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index f536f86..e938d09 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -54,8 +54,6 @@
 
     <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
     <dimen name="tv_pip_bounds_space">3dp</dimen>
-    <!-- Extra space around the PIP control button icon to match with the focused circle -->
-    <dimen name="tv_pip_button_icon_padding">5dp</dimen>
 
     <!-- Values for entering Recents and exiting Recents -->
     <dimen name="recents_tv_home_recents_shift">125dip</dimen>
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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f892fd6..1abd073 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1477,12 +1477,14 @@
     private void sendUserPresentBroadcast() {
         synchronized (this) {
             if (mBootCompleted) {
-                final UserHandle currentUser = new UserHandle(KeyguardUpdateMonitor.getCurrentUser());
+                int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
+                final UserHandle currentUser = new UserHandle(currentUserId);
                 final UserManager um = (UserManager) mContext.getSystemService(
                         Context.USER_SERVICE);
                 for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
                     mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
                 }
+                getLockPatternUtils().userPresent(currentUserId);
             } else {
                 mBootSendUserPresent = true;
             }
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/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 6b476ee..df79669 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -66,6 +66,7 @@
 import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
 import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
 import com.android.systemui.recents.events.ui.UserInteractionEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
@@ -752,6 +753,13 @@
         mIgnoreAltTabRelease = true;
     }
 
+    public final void onBusEvent(final DragEndEvent event) {
+        // Handle the case where we drop onto a dock region
+        if (event.dropTarget instanceof TaskStack.DockState) {
+            mScrimViews.animateScrimToCurrentNavBarState(false /* hasStackTasks */);
+        }
+    }
+
     @Override
     public boolean onPreDraw() {
         mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index fda340d..6d32293 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -175,7 +175,11 @@
         ssp.registerTaskStackListener(mTaskStackListener);
 
         // Initialize the static configuration resources
-        reloadHeaderBarLayout();
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        mDummyStackView = new TaskStackView(mContext);
+        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+                null, false);
+        reloadResources();
 
         // When we start, preload the data associated with the previous recent tasks.
         // We can use a new plan since the caches will be the same.
@@ -194,7 +198,9 @@
     }
 
     public void onConfigurationChanged() {
-        reloadHeaderBarLayout();
+        reloadResources();
+        mDummyStackView.reloadOnConfigurationChange();
+        mHeaderBar.onConfigurationChanged();
     }
 
     /**
@@ -542,11 +548,10 @@
     }
 
     /**
-     * Reloads all the layouts for the header bar transition.
+     * Reloads all the resources for the current configuration.
      */
-    private void reloadHeaderBarLayout() {
+    private void reloadResources() {
         Resources res = mContext.getResources();
-        LayoutInflater inflater = LayoutInflater.from(mContext);
 
         mStatusBarHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
@@ -561,9 +566,6 @@
                 R.dimen.recents_task_view_header_height_tablet_land,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land);
-        mDummyStackView = new TaskStackView(mContext);
-        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
-                null, false);
     }
 
     /**
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..68c46a9 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;
     }
 
     /**
@@ -300,6 +315,15 @@
         return key.id != affiliationTaskId;
     }
 
+    /**
+     * Returns the top activity component.
+     */
+    public ComponentName getTopComponent() {
+        return topActivity != null
+                ? topActivity
+                : key.baseIntent.getComponent();
+    }
+
     @Override
     public boolean equals(Object o) {
         // Check that the id matches
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..86d68c2 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.getTopComponent().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..70c4dbd 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,8 @@
         mVisibleDockStates.clear();
         if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
                 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
+            Recents.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
+                    event.task.resizeMode);
             if (!event.task.isDockable) {
                 EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 07a1d4e..dce2353 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -152,7 +152,7 @@
     /**
      * Animates the scrim to match the state of the current nav bar.
      */
-    private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
+    public void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
         boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
         if (mHasNavBarScrim != hasNavBarScrim) {
             AnimationProps animation = hasNavBarScrim
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..63018c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -353,8 +353,8 @@
     public void updateToInitialState(boolean scrollToInitialState) {
         if (scrollToInitialState) {
             mStackScroller.setStackScrollToInitialState();
+            mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
         }
-        mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
     }
 
     /** Updates the list of task views */
@@ -1221,7 +1221,9 @@
         // TaskViews with the stack so that we can lay them out
         if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE) {
             updateToInitialState(mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY);
-            mInitialState = INITIAL_STATE_UPDATE_NONE;
+            if (!mAwaitingFirstLayout) {
+                mInitialState = INITIAL_STATE_UPDATE_NONE;
+            }
         }
 
         // Rebind all the views, including the ignore ones
@@ -1281,6 +1283,7 @@
 
         if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
             mAwaitingFirstLayout = false;
+            mInitialState = INITIAL_STATE_UPDATE_NONE;
             onFirstLayout();
         }
     }
@@ -1618,7 +1621,6 @@
         if (animation != null) {
             relayoutTaskViewsOnNextFrame(animation);
         }
-        mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
 
         if (mEnterAnimationComplete) {
             if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
@@ -1837,9 +1839,17 @@
             // Calculate the new task stack bounds that matches the window size that Recents will
             // have after the drop
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+            Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
+            // When docked, the nav bar insets are consumed and the activity is measured without
+            // insets.  However, the window bounds include the insets, so we need to subtract them
+            // here to make them identical.
+            int height = getMeasuredHeight();
+            height -= systemInsets.bottom;
+            systemInsets.bottom = 0;
             mStackBounds.set(dockState.getDockedTaskStackBounds(getMeasuredWidth(),
-                    getMeasuredHeight(), mDividerSize, mLayoutAlgorithm.mSystemInsets,
+                    height, mDividerSize, mLayoutAlgorithm.mSystemInsets,
                     mLayoutAlgorithm, getResources(), mWindowRect));
+            mLayoutAlgorithm.setSystemInsets(systemInsets);
             mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
             updateLayoutAlgorithm(true /* boundScroll */);
@@ -1850,6 +1860,7 @@
             mWindowRect.set(mStableWindowRect);
             mStackBounds.set(mStableStackBounds);
             removeIgnoreTask(event.task);
+            mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
             mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
             updateLayoutAlgorithm(true /* boundScroll */);
@@ -1988,8 +1999,7 @@
     }
 
     public final void onBusEvent(ConfigurationChangedEvent event) {
-        mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
-        mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+        reloadOnConfigurationChange();
 
         // Notify the task views of the configuration change so they can reload their resources
         if (!event.fromMultiWindow) {
@@ -2003,12 +2013,15 @@
         }
 
         // Trigger a new layout and update to the initial state if necessary
-        if (event.fromMultiWindow) {
-            mInitialState = INITIAL_STATE_UPDATE_ALL;
-        } else if (event.fromOrientationChange) {
+        if (event.fromMultiWindow || event.fromOrientationChange) {
             mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
+            requestLayout();
         }
-        requestLayout();
+    }
+
+    public void reloadOnConfigurationChange() {
+        mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+        mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
     }
 
     /**
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/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 16d8e53..f98d3d5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -280,27 +280,31 @@
     /**
      * Update the header view when the configuration changes.
      */
-    void onConfigurationChanged() {
+    public void onConfigurationChanged() {
         // Update the dimensions of everything in the header. We do this because we need to use
         // resources for the display, and not the current configuration.
         Resources res = getResources();
-        mHeaderBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+        int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land,
                 R.dimen.recents_task_view_header_height,
                 R.dimen.recents_task_view_header_height_tablet_land);
-        mHeaderButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+        int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding_tablet_land,
                 R.dimen.recents_task_view_header_button_padding,
                 R.dimen.recents_task_view_header_button_padding_tablet_land);
-        updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
-        if (mAppOverlayView != null) {
-            updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
+        if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
+            mHeaderBarHeight = headerBarHeight;
+            mHeaderButtonPadding = headerButtonPadding;
+            updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
+            if (mAppOverlayView != null) {
+                updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
+            }
         }
     }
 
@@ -434,7 +438,6 @@
 
     /** Binds the bar view to the task */
     public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
-        SystemServicesProxy ssp = Recents.getSystemServices();
         mTask = t;
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
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/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 86c1fca..c2521b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -65,8 +65,6 @@
 import static android.content.Context.LAYOUT_INFLATER_SERVICE;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 
-import com.google.android.collect.Lists;
-
 /**
  * Contains functionality for handling keyboard shortcuts.
  */
@@ -371,7 +369,7 @@
 
     private KeyboardShortcutGroup getDefaultApplicationShortcuts() {
         final int userId = mContext.getUserId();
-        List<KeyboardShortcutInfo> keyboardShortcutInfoAppItems = Lists.newArrayList();
+        List<KeyboardShortcutInfo> keyboardShortcutInfoAppItems = new ArrayList<>();
 
         // Assist.
         final AssistUtils assistUtils = new AssistUtils(mContext);
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/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index ebefdde..52fb470 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -674,6 +674,13 @@
             } else if (!selfFullscreen && otherFullscreen) {
                 return 1;
             }
+
+            if (remoteInputActive && !o.remoteInputActive) {
+                return -1;
+            } else if (!remoteInputActive && o.remoteInputActive) {
+                return 1;
+            }
+
             return postTime < o.postTime ? 1
                     : postTime == o.postTime ? entry.key.compareTo(o.entry.key)
                             : -1;
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/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
new file mode 100644
index 0000000..c65415e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
@@ -0,0 +1,142 @@
+/*
+ * 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.tv.pip;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View.OnFocusChangeListener;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+/**
+ * A view containing PIP controls including fullscreen, close, and media controls.
+ */
+public class PipControlButtonView extends LinearLayout {
+    private OnFocusChangeListener mFocusChangeListener;
+    private ImageView mButtonImageView;
+    private TextView mDescriptionTextView;
+    private Animator mFocusGainAnimator;
+    private Animator mFocusLoseAnimator;
+
+    private final OnFocusChangeListener mInternalFocusChangeListener =
+            new OnFocusChangeListener() {
+                @Override
+                public void onFocusChange(View v, boolean hasFocus) {
+                    if (hasFocus) {
+                        if (mFocusLoseAnimator.isStarted()) {
+                            mFocusLoseAnimator.cancel();
+                        }
+                        mFocusGainAnimator.start();
+                    } else {
+                        if (mFocusGainAnimator.isStarted()) {
+                            mFocusGainAnimator.cancel();
+                        }
+                        mFocusLoseAnimator.start();
+                    }
+
+                    if (mFocusChangeListener != null) {
+                        mFocusChangeListener.onFocusChange(v, hasFocus);
+                    }
+                }
+            };
+
+    public PipControlButtonView(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public PipControlButtonView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public PipControlButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public PipControlButtonView(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        LayoutInflater inflater = (LayoutInflater) getContext()
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.tv_pip_control_button, this);
+
+        setOrientation(LinearLayout.VERTICAL);
+        setGravity(Gravity.CENTER);
+
+        mButtonImageView = (ImageView) findViewById(R.id.button);
+        mDescriptionTextView = (TextView) findViewById(R.id.desc);
+
+        int[] values = new int[] {android.R.attr.src, android.R.attr.text};
+        TypedArray typedArray =
+            context.obtainStyledAttributes(attrs, values, defStyleAttr, defStyleRes);
+
+        mButtonImageView.setImageDrawable(typedArray.getDrawable(0));
+        mDescriptionTextView.setText(typedArray.getText(1));
+
+        typedArray.recycle();
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mButtonImageView.setOnFocusChangeListener(mInternalFocusChangeListener);
+
+        mFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
+                R.anim.tv_pip_controls_text_focus_gain_animation);
+        mFocusGainAnimator.setTarget(mDescriptionTextView);
+        mFocusLoseAnimator = AnimatorInflater.loadAnimator(getContext(),
+                R.anim.tv_pip_controls_text_focus_lose_animation);
+        mFocusLoseAnimator.setTarget(mDescriptionTextView);
+    }
+
+    @Override
+    public void setOnClickListener(OnClickListener listener) {
+        mButtonImageView.setOnClickListener(listener);
+    }
+
+    @Override
+    public void setOnFocusChangeListener(OnFocusChangeListener listener) {
+        mFocusChangeListener = listener;
+    }
+
+    /**
+     * Sets the drawable for the button with the given resource id.
+     */
+    public void setImageResource(int resId) {
+        mButtonImageView.setImageResource(resId);
+    }
+
+    /**
+     * Sets the text for description the with the given resource id.
+     */
+    public void setText(int resId) {
+        mDescriptionTextView.setText(resId);
+    }
+
+    @Override
+    public boolean isFocused() {
+        return mButtonImageView.isFocused();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
index 3f87611..d15799c 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -57,13 +57,9 @@
     final PipManager mPipManager = PipManager.getInstance();
     Listener mListener;
 
-    View mFullButtonView;
-    View mFullDescriptionView;
-    View mPlayPauseView;
-    ImageView mPlayPauseButtonImageView;
-    TextView mPlayPauseDescriptionTextView;
-    View mCloseButtonView;
-    View mCloseDescriptionView;
+    PipControlButtonView mFullButtonView;
+    PipControlButtonView mCloseButtonView;
+    PipControlButtonView mPlayPauseButtonView;
 
     private boolean mHasFocus;
     private OnFocusChangeListener mOnChildFocusChangeListener;
@@ -75,13 +71,20 @@
         }
     };
 
-    private PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
+    private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
         @Override
         public void onMediaControllerChanged() {
             updateMediaController();
         }
     };
 
+    private final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener() {
+        @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            onChildViewFocusChanged();
+        }
+    };
+
     public PipControlsView(Context context) {
         this(context, null, 0, 0);
     }
@@ -108,26 +111,30 @@
     public void onFinishInflate() {
         super.onFinishInflate();
 
-        mFullButtonView = findViewById(R.id.full_button);
-        mFullDescriptionView = findViewById(R.id.full_desc);
+        mFullButtonView = (PipControlButtonView) findViewById(R.id.full_button);
+        mFullButtonView.setOnFocusChangeListener(mFocusChangeListener);
         mFullButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 mPipManager.movePipToFullscreen();
             }
         });
-        mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+
+        mCloseButtonView = (PipControlButtonView) findViewById(R.id.close_button);
+        mCloseButtonView.setOnFocusChangeListener(mFocusChangeListener);
+        mCloseButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-                onChildViewFocusChanged();
+            public void onClick(View v) {
+                mPipManager.closePip();
+                if (mListener != null) {
+                    mListener.onClosed();
+                }
             }
         });
 
-        mPlayPauseView = findViewById(R.id.play_pause);
-        mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
-        mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
-        mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
+        mPlayPauseButtonView = (PipControlButtonView) findViewById(R.id.play_pause_button);
+        mPlayPauseButtonView.setOnFocusChangeListener(mFocusChangeListener);
+        mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if (mMediaController == null || mMediaController.getPlaybackState() == null) {
@@ -143,33 +150,6 @@
                 // View will be updated later in {@link mMediaControllerCallback}
             }
         });
-        mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mPlayPauseDescriptionTextView.setVisibility(
-                        hasFocus ? View.VISIBLE : View.INVISIBLE);
-                onChildViewFocusChanged();
-            }
-        });
-
-        mCloseButtonView = findViewById(R.id.close_button);
-        mCloseDescriptionView = findViewById(R.id.close_desc);
-        mCloseButtonView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mPipManager.closePip();
-                if (mListener != null) {
-                    mListener.onClosed();
-                }
-            }
-        });
-        mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-                onChildViewFocusChanged();
-            }
-        });
     }
 
     @Override
@@ -206,15 +186,15 @@
     private void updatePlayPauseView() {
         int state = mPipManager.getPlaybackState();
         if (state == PLAYBACK_STATE_UNAVAILABLE) {
-            mPlayPauseView.setVisibility(View.GONE);
+            mPlayPauseButtonView.setVisibility(View.GONE);
         } else {
-            mPlayPauseView.setVisibility(View.VISIBLE);
+            mPlayPauseButtonView.setVisibility(View.VISIBLE);
             if (state == PLAYBACK_STATE_PLAYING) {
-                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
-                mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
+                mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white_24dp);
+                mPlayPauseButtonView.setText(R.string.pip_pause);
             } else {
-                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
-                mPlayPauseDescriptionTextView.setText(R.string.pip_play);
+                mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white_24dp);
+                mPlayPauseButtonView.setText(R.string.pip_play);
             }
         }
     }
@@ -229,7 +209,7 @@
     private void onChildViewFocusChanged() {
         // At this moment, hasFocus() returns true although there's no focused child.
         boolean hasFocus = (mFullButtonView != null && mFullButtonView.isFocused())
-                || (mPlayPauseButtonImageView != null && mPlayPauseButtonImageView.isFocused())
+                || (mPlayPauseButtonView != null && mPlayPauseButtonView.isFocused())
                 || (mCloseButtonView != null && mCloseButtonView.isFocused());
         if (mHasFocus != hasFocus) {
             mHasFocus = hasFocus;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index 854e09d..542a935 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -16,10 +16,14 @@
 
 package com.android.systemui.tv.pip;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
 import android.app.Activity;
 import android.os.Bundle;
+import android.view.View;
 
 import com.android.systemui.R;
+import com.android.systemui.Interpolators;
 
 /**
  * Activity to show the PIP menu to control PIP.
@@ -29,7 +33,9 @@
 
     private final PipManager mPipManager = PipManager.getInstance();
 
-    private PipControlsView mPipControlsView;
+    private Animator mFadeInAnimation;
+    private Animator mFadeOutAnimation;
+    private View mPipControlsView;
     private boolean mRestorePipSizeWhenClose;
 
     @Override
@@ -38,8 +44,14 @@
         setContentView(R.layout.tv_pip_menu);
         mPipManager.addListener(this);
 
-        mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
         mRestorePipSizeWhenClose = true;
+        mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
+        mFadeInAnimation = AnimatorInflater.loadAnimator(
+                this, R.anim.tv_pip_menu_fade_in_animation);
+        mFadeInAnimation.setTarget(mPipControlsView);
+        mFadeOutAnimation = AnimatorInflater.loadAnimator(
+                this, R.anim.tv_pip_menu_fade_out_animation);
+        mFadeOutAnimation.setTarget(mPipControlsView);
     }
 
     private void restorePipAndFinish() {
@@ -51,8 +63,15 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+        mFadeInAnimation.start();
+    }
+
+    @Override
     public void onPause() {
         super.onPause();
+        mFadeOutAnimation.start();
         restorePipAndFinish();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
index 8b8c105..9c806f7 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
@@ -72,28 +72,20 @@
         super.onFinishInflate();
 
         int buttonsFocusGainAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_gain_animation;
-        int textFocusGainAnim = R.anim.tv_pip_controls_text_in_recents_focus_gain_animation;
         mFocusGainAnimatorSet = new AnimatorSet();
         mFocusGainAnimatorSet.playTogether(
                 loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_gain_animation),
                 loadAnimator(mFullButtonView,buttonsFocusGainAnim),
-                loadAnimator(mPlayPauseButtonImageView, buttonsFocusGainAnim),
-                loadAnimator(mCloseButtonView, buttonsFocusGainAnim),
-                loadAnimator(mFullDescriptionView, textFocusGainAnim),
-                loadAnimator(mPlayPauseDescriptionTextView, textFocusGainAnim),
-                loadAnimator(mCloseDescriptionView, textFocusGainAnim));
+                loadAnimator(mPlayPauseButtonView, buttonsFocusGainAnim),
+                loadAnimator(mCloseButtonView, buttonsFocusGainAnim));
 
         int buttonsFocusLoseAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_lose_animation;
-        int textFocusLoseAnim = R.anim.tv_pip_controls_text_in_recents_focus_lose_animation;
         mFocusLoseAnimatorSet = new AnimatorSet();
         mFocusLoseAnimatorSet.playTogether(
                 loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_lose_animation),
                 loadAnimator(mFullButtonView, buttonsFocusLoseAnim),
-                loadAnimator(mPlayPauseButtonImageView, buttonsFocusLoseAnim),
-                loadAnimator(mCloseButtonView, buttonsFocusLoseAnim),
-                loadAnimator(mFullDescriptionView, textFocusLoseAnim),
-                loadAnimator(mPlayPauseDescriptionTextView, textFocusLoseAnim),
-                loadAnimator(mCloseDescriptionView, textFocusLoseAnim));
+                loadAnimator(mPlayPauseButtonView, buttonsFocusLoseAnim),
+                loadAnimator(mCloseButtonView, buttonsFocusLoseAnim));
 
         Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
         int pipControlsMarginTop = getContext().getResources().getDimensionPixelSize(
@@ -142,11 +134,8 @@
         requestFocus();
         setTranslationY(0);
         setScaleXY(mFullButtonView, 1);
-        setScaleXY(mPlayPauseButtonImageView, 1);
+        setScaleXY(mPlayPauseButtonView, 1);
         setScaleXY(mCloseButtonView, 1);
-        mFullDescriptionView.setAlpha(1);
-        mPlayPauseDescriptionTextView.setAlpha(1);
-        mCloseDescriptionView.setAlpha(1);
     }
 
     private void setScaleXY(View view, float scale) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index 47cd8e5..c0e221f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.tv.pip;
 
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.util.Log;
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/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4ac75ca..f2b4e52 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -229,7 +229,6 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_STARTING);
         filter.addAction(Intent.ACTION_USER_REMOVED);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
         mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
 
         mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
@@ -369,8 +368,6 @@
             } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 mStorage.prefetchUser(userHandle);
-            } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
-                mStrongAuth.reportUnlock(getSendingUserId());
             } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 if (userHandle > 0) {
@@ -1347,6 +1344,12 @@
         mStrongAuth.requireStrongAuth(strongAuthReason, userId);
     }
 
+    @Override
+    public void userPresent(int userId) {
+        checkWritePermission(userId);
+        mStrongAuth.reportUnlock(userId);
+    }
+
     private static final String[] VALID_SETTINGS = new String[] {
         LockPatternUtils.LOCKOUT_PERMANENT_KEY,
         LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
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/MountService.java b/services/core/java/com/android/server/MountService.java
index fd9a94d..229a3f4 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1010,7 +1010,7 @@
         Configuration config = new Configuration();
         config.setLocale(locale);
         try {
-            ActivityManagerNative.getDefault().updateConfiguration(config);
+            ActivityManagerNative.getDefault().updatePersistentConfiguration(config);
         } catch (RemoteException e) {
             Slog.e(TAG, "Error setting system locale from mount service", e);
         }
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/accounts/CryptoHelper.java b/services/core/java/com/android/server/accounts/CryptoHelper.java
index 2b59b74..2ade673 100644
--- a/services/core/java/com/android/server/accounts/CryptoHelper.java
+++ b/services/core/java/com/android/server/accounts/CryptoHelper.java
@@ -10,15 +10,12 @@
 
 import java.security.GeneralSecurityException;
 import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Arrays;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
 import javax.crypto.Mac;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
 
 /**
  * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
@@ -30,15 +27,15 @@
     private static final String KEY_CIPHER = "cipher";
     private static final String KEY_MAC = "mac";
     private static final String KEY_ALGORITHM = "AES";
+    private static final String KEY_IV = "iv";
     private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
     private static final String MAC_ALGORITHM = "HMACSHA256";
     private static final int IV_LENGTH = 16;
 
     private static CryptoHelper sInstance;
     // Keys used for encrypting and decrypting data returned in a Bundle.
-    private final SecretKeySpec mCipherKeySpec;
-    private final SecretKeySpec mMacKeySpec;
-    private final IvParameterSpec mIv;
+    private final SecretKey mEncryptionKey;
+    private final SecretKey mMacKey;
 
     /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
         if (sInstance == null) {
@@ -49,18 +46,10 @@
 
     private CryptoHelper() throws NoSuchAlgorithmException {
         KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
-        SecretKey skey = kgen.generateKey();
-        mCipherKeySpec = new SecretKeySpec(skey.getEncoded(), KEY_ALGORITHM);
-
+        mEncryptionKey = kgen.generateKey();
+        // Use a different key for mac-ing than encryption/decryption.
         kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
-        skey = kgen.generateKey();
-        mMacKeySpec = new SecretKeySpec(skey.getEncoded(), MAC_ALGORITHM);
-
-        // Create random iv
-        byte[] iv = new byte[IV_LENGTH];
-        SecureRandom secureRandom = new SecureRandom();
-        secureRandom.nextBytes(iv);
-        mIv = new IvParameterSpec(iv);
+        mMacKey = kgen.generateKey();
     }
 
     @NonNull
@@ -68,16 +57,19 @@
         Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
         Parcel parcel = Parcel.obtain();
         bundle.writeToParcel(parcel, 0);
-        byte[] bytes = parcel.marshall();
+        byte[] clearBytes = parcel.marshall();
         parcel.recycle();
 
+        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        cipher.init(Cipher.ENCRYPT_MODE, mEncryptionKey);
+        byte[] encryptedBytes = cipher.doFinal(clearBytes);
+        byte[] iv = cipher.getIV();
+        byte[] mac = createMac(encryptedBytes, iv);
+
         Bundle encryptedBundle = new Bundle();
-
-        byte[] cipher = encrypt(bytes);
-        byte[] mac = createMac(cipher);
-
-        encryptedBundle.putByteArray(KEY_CIPHER, cipher);
+        encryptedBundle.putByteArray(KEY_CIPHER, encryptedBytes);
         encryptedBundle.putByteArray(KEY_MAC, mac);
+        encryptedBundle.putByteArray(KEY_IV, iv);
 
         return encryptedBundle;
     }
@@ -85,19 +77,18 @@
     @Nullable
     /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
         Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
-        byte[] cipherArray = bundle.getByteArray(KEY_CIPHER);
-        byte[] macArray = bundle.getByteArray(KEY_MAC);
-
-        if (!verifyMac(cipherArray, macArray)) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "Escrow mac mismatched!");
-            }
+        byte[] iv = bundle.getByteArray(KEY_IV);
+        byte[] encryptedBytes = bundle.getByteArray(KEY_CIPHER);
+        byte[] mac = bundle.getByteArray(KEY_MAC);
+        if (!verifyMac(encryptedBytes, iv, mac)) {
+            Log.w(TAG, "Escrow mac mismatched!");
             return null;
         }
 
+        IvParameterSpec ivSpec = new IvParameterSpec(iv);
         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-        cipher.init(Cipher.DECRYPT_MODE, mCipherKeySpec, mIv);
-        byte[] decryptedBytes = cipher.doFinal(cipherArray);
+        cipher.init(Cipher.DECRYPT_MODE, mEncryptionKey, ivSpec);
+        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
 
         Parcel decryptedParcel = Parcel.obtain();
         decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
@@ -108,9 +99,8 @@
         return decryptedBundle;
     }
 
-    private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] macArray)
+    private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] iv, @Nullable byte[] macArray)
             throws GeneralSecurityException {
-
         if (cipherArray == null || cipherArray.length == 0 || macArray == null
                 || macArray.length == 0) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -118,23 +108,29 @@
             }
             return false;
         }
-        Mac mac = Mac.getInstance(MAC_ALGORITHM);
-        mac.init(mMacKeySpec);
-        mac.update(cipherArray);
-        return Arrays.equals(macArray, mac.doFinal());
+        return constantTimeArrayEquals(macArray, createMac(cipherArray, iv));
     }
 
     @NonNull
-    private byte[] encrypt(@NonNull byte[] data) throws GeneralSecurityException {
-        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-        cipher.init(Cipher.ENCRYPT_MODE, mCipherKeySpec, mIv);
-        return cipher.doFinal(data);
+    private byte[] createMac(@NonNull byte[] cipher, @NonNull byte[] iv) throws GeneralSecurityException {
+        Mac mac = Mac.getInstance(MAC_ALGORITHM);
+        mac.init(mMacKey);
+        mac.update(cipher);
+        mac.update(iv);
+        return mac.doFinal();
     }
 
-    @NonNull
-    private byte[] createMac(@NonNull byte[] cipher) throws GeneralSecurityException {
-        Mac mac = Mac.getInstance(MAC_ALGORITHM);
-        mac.init(mMacKeySpec);
-        return mac.doFinal(cipher);
+    private static boolean constantTimeArrayEquals(byte[] a, byte[] b) {
+        if (a == null || b == null) {
+            return a == b;
+        }
+        if (a.length != b.length) {
+            return false;
+        }
+        boolean isEqual = true;
+        for (int i = 0; i < b.length; i++) {
+            isEqual &= (a[i] == b[i]);
+        }
+        return isEqual;
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3ec51e3..04a63a1 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);
         }
@@ -17620,7 +17622,7 @@
             int res = broadcastIntentLocked(callerApp,
                     callerApp != null ? callerApp.info.packageName : null,
                     intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
-                    requiredPermissions, appOp, null, serialized, sticky,
+                    requiredPermissions, appOp, bOptions, serialized, sticky,
                     callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
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..7df8ffd 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.
@@ -249,6 +257,10 @@
         return mLock;
     }
 
+    public JobStore getJobStore() {
+        return mJobs;
+    }
+
     @Override
     public void onStartUser(int userHandle) {
         mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
@@ -386,7 +398,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 +532,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 +618,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 +787,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 +826,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "queuing all ready jobs for execution:");
             }
+            noteJobsNonpending(mPendingJobs);
             mPendingJobs.clear();
             mJobs.forEachJob(mReadyQueueFunctor);
             mReadyQueueFunctor.postProcess();
@@ -832,6 +862,7 @@
 
             public void postProcess() {
                 if (newReadyJobs != null) {
+                    noteJobsPending(newReadyJobs);
                     mPendingJobs.addAll(newReadyJobs);
                 }
                 newReadyJobs = null;
@@ -910,6 +941,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
                     }
+                    noteJobsPending(runnableJobs);
                     mPendingJobs.addAll(runnableJobs);
                 } else {
                     if (DEBUG) {
@@ -935,6 +967,7 @@
         private void maybeQueueReadyJobsForExecutionLockedH() {
             if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
 
+            noteJobsNonpending(mPendingJobs);
             mPendingJobs.clear();
             mJobs.forEachJob(mMaybeQueueFunctor);
             mMaybeQueueFunctor.postProcess();
@@ -998,16 +1031,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 +1074,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 +1179,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 +1491,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/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index d8490d4..02bc36ca 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -22,6 +22,7 @@
 
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobStore;
 import com.android.server.job.StateChangedListener;
 
 import java.io.PrintWriter;
@@ -41,10 +42,52 @@
     // Singleton factory
     private static Object sCreationLock = new Object();
     private static volatile AppIdleController sController;
-    final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
+    private final JobSchedulerService mJobSchedulerService;
     private final UsageStatsManagerInternal mUsageStatsInternal;
     boolean mAppIdleParoleOn;
 
+    final class GlobalUpdateFunc implements JobStore.JobStatusFunctor {
+        boolean mChanged;
+
+        @Override public void process(JobStatus jobStatus) {
+            String packageName = jobStatus.getSourcePackageName();
+            final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
+                    jobStatus.getSourceUid(), jobStatus.getSourceUserId());
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
+            }
+            if (jobStatus.setAppNotIdleConstraintSatisfied(!appIdle)) {
+                mChanged = true;
+            }
+        }
+    };
+
+    final static class PackageUpdateFunc implements JobStore.JobStatusFunctor {
+        final int mUserId;
+        final String mPackage;
+        final boolean mIdle;
+        boolean mChanged;
+
+        PackageUpdateFunc(int userId, String pkg, boolean idle) {
+            mUserId = userId;
+            mPackage = pkg;
+            mIdle = idle;
+        }
+
+        @Override public void process(JobStatus jobStatus) {
+            if (jobStatus.getSourcePackageName().equals(mPackage)
+                    && jobStatus.getSourceUserId() == mUserId) {
+                if (jobStatus.setAppNotIdleConstraintSatisfied(!mIdle)) {
+                    if (DEBUG) {
+                        Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
+                                + mPackage + " to " + mIdle);
+                    }
+                    mChanged = true;
+                }
+            }
+        }
+    };
+
     public static AppIdleController get(JobSchedulerService service) {
         synchronized (sCreationLock) {
             if (sController == null) {
@@ -55,9 +98,9 @@
         }
     }
 
-    private AppIdleController(StateChangedListener stateChangedListener, Context context,
-            Object lock) {
-        super(stateChangedListener, context, lock);
+    private AppIdleController(JobSchedulerService service, Context context, Object lock) {
+        super(service, context, lock);
+        mJobSchedulerService = service;
         mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
         mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
         mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
@@ -65,7 +108,6 @@
 
     @Override
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
-        mTrackedTasks.add(jobStatus);
         String packageName = jobStatus.getSourcePackageName();
         final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
                 jobStatus.getSourceUid(), jobStatus.getSourceUserId());
@@ -78,19 +120,20 @@
 
     @Override
     public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
-        mTrackedTasks.remove(jobStatus);
     }
 
     @Override
-    public void dumpControllerStateLocked(PrintWriter pw) {
+    public void dumpControllerStateLocked(final PrintWriter pw) {
         pw.println("AppIdle");
         pw.println("Parole On: " + mAppIdleParoleOn);
-        for (JobStatus task : mTrackedTasks) {
-            pw.print(task.getSourcePackageName());
-            pw.print(":runnable="
-                    + ((task.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0));
-            pw.print(", ");
-        }
+        mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+            @Override public void process(JobStatus jobStatus) {
+                pw.print("  ");
+                pw.print(jobStatus.getSourcePackageName());
+                pw.print(": runnable=");
+                pw.println((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
+            }
+        });
         pw.println();
     }
 
@@ -102,16 +145,10 @@
                 return;
             }
             mAppIdleParoleOn = isAppIdleParoleOn;
-            for (JobStatus task : mTrackedTasks) {
-                String packageName = task.getSourcePackageName();
-                final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                        task.getSourceUid(), task.getSourceUserId());
-                if (DEBUG) {
-                    Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
-                }
-                if (task.setAppNotIdleConstraintSatisfied(!appIdle)) {
-                    changed = true;
-                }
+            GlobalUpdateFunc update = new GlobalUpdateFunc();
+            mJobSchedulerService.getJobStore().forEachJob(update);
+            if (update.mChanged) {
+                changed = true;
             }
         }
         if (changed) {
@@ -128,17 +165,10 @@
                 if (mAppIdleParoleOn) {
                     return;
                 }
-                for (JobStatus task : mTrackedTasks) {
-                    if (task.getSourcePackageName().equals(packageName)
-                            && task.getSourceUserId() == userId) {
-                        if (task.setAppNotIdleConstraintSatisfied(!idle)) {
-                            if (DEBUG) {
-                                Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
-                                        + packageName + " to " + idle);
-                            }
-                            changed = true;
-                        }
-                    }
+                PackageUpdateFunc update = new PackageUpdateFunc(userId, packageName, idle);
+                mJobSchedulerService.getJobStore().forEachJob(update);
+                if (update.mChanged) {
+                    changed = true;
                 }
             }
             if (changed) {
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index b2f1958..c5b1a3d 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -21,6 +21,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
+import android.util.TimeUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
@@ -46,12 +47,17 @@
      */
     private static final int MAX_URIS_REPORTED = 50;
 
+    /**
+     * At this point we consider it urgent to schedule the job ASAP.
+     */
+    private static final int URIS_URGENT_THRESHOLD = 40;
+
     private static final Object sCreationLock = new Object();
     private static volatile ContentObserverController sController;
 
     final private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
     ArrayMap<Uri, ObserverInstance> mObservers = new ArrayMap<>();
-    final Handler mHandler = new Handler();
+    final Handler mHandler;
 
     public static ContentObserverController get(JobSchedulerService taskManagerService) {
         synchronized (sCreationLock) {
@@ -72,6 +78,7 @@
     private ContentObserverController(StateChangedListener stateChangedListener, Context context,
                 Object lock) {
         super(stateChangedListener, context, lock);
+        mHandler = new Handler(context.getMainLooper());
     }
 
     @Override
@@ -113,6 +120,11 @@
             taskStatus.changedUris = null;
             taskStatus.setContentTriggerConstraintSatisfied(havePendingUris);
         }
+        if (lastJob != null && lastJob.contentObserverJobInstance != null) {
+            // And now we can detach the instance state from the last job.
+            lastJob.contentObserverJobInstance.detachLocked();
+            lastJob.contentObserverJobInstance = null;
+        }
     }
 
     @Override
@@ -133,30 +145,33 @@
             boolean forUpdate) {
         if (taskStatus.hasContentTriggerConstraint()) {
             if (taskStatus.contentObserverJobInstance != null) {
-                if (incomingJob != null && taskStatus.contentObserverJobInstance != null
-                        && taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
-                    // We are stopping this job, but it is going to be replaced by this given
-                    // incoming job.  We want to propagate our state over to it, so we don't
-                    // lose any content changes that had happend since the last one started.
-                    // If there is a previous job associated with the new job, propagate over
-                    // any pending content URI trigger reports.
-                    if (incomingJob.contentObserverJobInstance == null) {
-                        incomingJob.contentObserverJobInstance = new JobInstance(incomingJob);
+                taskStatus.contentObserverJobInstance.unscheduleLocked();
+                if (incomingJob != null) {
+                    if (taskStatus.contentObserverJobInstance != null
+                            && taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
+                        // We are stopping this job, but it is going to be replaced by this given
+                        // incoming job.  We want to propagate our state over to it, so we don't
+                        // lose any content changes that had happend since the last one started.
+                        // If there is a previous job associated with the new job, propagate over
+                        // any pending content URI trigger reports.
+                        if (incomingJob.contentObserverJobInstance == null) {
+                            incomingJob.contentObserverJobInstance = new JobInstance(incomingJob);
+                        }
+                        incomingJob.contentObserverJobInstance.mChangedAuthorities
+                                = taskStatus.contentObserverJobInstance.mChangedAuthorities;
+                        incomingJob.contentObserverJobInstance.mChangedUris
+                                = taskStatus.contentObserverJobInstance.mChangedUris;
+                        taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
+                        taskStatus.contentObserverJobInstance.mChangedUris = null;
                     }
-                    incomingJob.contentObserverJobInstance.mChangedAuthorities
-                            = taskStatus.contentObserverJobInstance.mChangedAuthorities;
-                    incomingJob.contentObserverJobInstance.mChangedUris
-                            = taskStatus.contentObserverJobInstance.mChangedUris;
-                    taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
-                    taskStatus.contentObserverJobInstance.mChangedUris = null;
+                    // We won't detach the content observers here, because we want to
+                    // allow them to continue monitoring so we don't miss anything...  and
+                    // since we are giving an incomingJob here, we know this will be
+                    // immediately followed by a start tracking of that job.
                 } else {
-                    // We won't do this reset if being called for an update, because
-                    // we know it will be immediately followed by maybeStartTrackingJobLocked...
-                    // and we don't want to lose any content changes in-between.
-                    if (taskStatus.contentObserverJobInstance != null) {
-                        taskStatus.contentObserverJobInstance.detach();
-                        taskStatus.contentObserverJobInstance = null;
-                    }
+                    // But here there is no incomingJob, so nothing coming up, so time to detach.
+                    taskStatus.contentObserverJobInstance.detachLocked();
+                    taskStatus.contentObserverJobInstance = null;
                 }
             }
             mTrackedTasks.remove(taskStatus);
@@ -177,9 +192,9 @@
         }
     }
 
-    class ObserverInstance extends ContentObserver {
+    final class ObserverInstance extends ContentObserver {
         final Uri mUri;
-        final ArrayList<JobInstance> mJobs = new ArrayList<>();
+        final ArraySet<JobInstance> mJobs = new ArraySet<>();
 
         public ObserverInstance(Handler handler, Uri uri) {
             super(handler);
@@ -188,11 +203,10 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            boolean reportChange = false;
             synchronized (mLock) {
                 final int N = mJobs.size();
                 for (int i=0; i<N; i++) {
-                    JobInstance inst = mJobs.get(i);
+                    JobInstance inst = mJobs.valueAt(i);
                     if (inst.mChangedUris == null) {
                         inst.mChangedUris = new ArraySet<>();
                     }
@@ -203,26 +217,38 @@
                         inst.mChangedAuthorities = new ArraySet<>();
                     }
                     inst.mChangedAuthorities.add(uri.getAuthority());
-                    if (inst.mJobStatus.setContentTriggerConstraintSatisfied(true)) {
-                        reportChange = true;
-                    }
+                    inst.scheduleLocked();
                 }
             }
-            // Let the scheduler know that state has changed. This may or may not result in an
-            // execution.
-            if (reportChange) {
-                mStateChangedListener.onControllerStateChanged();
-            }
         }
     }
 
-    class JobInstance extends ArrayList<ObserverInstance> {
-        private final JobStatus mJobStatus;
-        private ArraySet<Uri> mChangedUris;
-        private ArraySet<String> mChangedAuthorities;
+    static final class TriggerRunnable implements Runnable {
+        final JobInstance mInstance;
+
+        TriggerRunnable(JobInstance instance) {
+            mInstance = instance;
+        }
+
+        @Override public void run() {
+            mInstance.trigger();
+        }
+    }
+
+    final class JobInstance {
+        final ArrayList<ObserverInstance> mMyObservers = new ArrayList<>();
+        final JobStatus mJobStatus;
+        final Runnable mExecuteRunner;
+        final Runnable mTimeoutRunner;
+        ArraySet<Uri> mChangedUris;
+        ArraySet<String> mChangedAuthorities;
+
+        boolean mTriggerPending;
 
         JobInstance(JobStatus jobStatus) {
             mJobStatus = jobStatus;
+            mExecuteRunner = new TriggerRunnable(this);
+            mTimeoutRunner = new TriggerRunnable(this);
             final JobInfo.TriggerContentUri[] uris = jobStatus.getJob().getTriggerContentUris();
             if (uris != null) {
                 for (JobInfo.TriggerContentUri uri : uris) {
@@ -238,15 +264,54 @@
                                 obs);
                     }
                     obs.mJobs.add(this);
-                    add(obs);
+                    mMyObservers.add(obs);
                 }
             }
         }
 
-        void detach() {
-            final int N = size();
+        void trigger() {
+            boolean reportChange = false;
+            synchronized (mLock) {
+                if (mTriggerPending) {
+                    if (mJobStatus.setContentTriggerConstraintSatisfied(true)) {
+                        reportChange = true;
+                    }
+                    unscheduleLocked();
+                }
+            }
+            // Let the scheduler know that state has changed. This may or may not result in an
+            // execution.
+            if (reportChange) {
+                mStateChangedListener.onControllerStateChanged();
+            }
+        }
+
+        void scheduleLocked() {
+            if (!mTriggerPending) {
+                mTriggerPending = true;
+                mHandler.postDelayed(mTimeoutRunner, mJobStatus.getTriggerContentMaxDelay());
+            }
+            mHandler.removeCallbacks(mExecuteRunner);
+            if (mChangedUris.size() >= URIS_URGENT_THRESHOLD) {
+                // If we start getting near the limit, GO NOW!
+                mHandler.post(mExecuteRunner);
+            } else {
+                mHandler.postDelayed(mExecuteRunner, mJobStatus.getTriggerContentUpdateDelay());
+            }
+        }
+
+        void unscheduleLocked() {
+            if (mTriggerPending) {
+                mHandler.removeCallbacks(mExecuteRunner);
+                mHandler.removeCallbacks(mTimeoutRunner);
+                mTriggerPending = false;
+            }
+        }
+
+        void detachLocked() {
+            final int N = mMyObservers.size();
             for (int i=0; i<N; i++) {
-                final ObserverInstance obs = get(i);
+                final ObserverInstance obs = mMyObservers.get(i);
                 obs.mJobs.remove(this);
                 if (obs.mJobs.size() == 0) {
                     mContext.getContentResolver().unregisterContentObserver(obs);
@@ -259,39 +324,54 @@
     @Override
     public void dumpControllerStateLocked(PrintWriter pw) {
         pw.println("Content.");
+        boolean printed = false;
         Iterator<JobStatus> it = mTrackedTasks.iterator();
-        if (it.hasNext()) {
-            pw.print(String.valueOf(it.next().hashCode()));
-        }
         while (it.hasNext()) {
-            pw.print("," + String.valueOf(it.next().hashCode()));
+            if (!printed) {
+                pw.print("  ");
+                printed = true;
+            } else {
+                pw.print(",");
+            }
+            pw.print(System.identityHashCode(it.next()));
         }
-        pw.println();
+        if (printed) {
+            pw.println();
+        }
         int N = mObservers.size();
         if (N > 0) {
-            pw.println("URIs:");
+            pw.println("  Observers:");
             for (int i = 0; i < N; i++) {
                 ObserverInstance obs = mObservers.valueAt(i);
-                pw.print("  ");
-                pw.print(mObservers.keyAt(i));
-                pw.println(":");
                 pw.print("    ");
-                pw.println(obs);
-                pw.println("    Jobs:");
+                pw.print(mObservers.keyAt(i));
+                pw.print(" (");
+                pw.print(System.identityHashCode(obs));
+                pw.println("):");
+                pw.println("      Jobs:");
                 int M = obs.mJobs.size();
                 for (int j=0; j<M; j++) {
-                    JobInstance inst = obs.mJobs.get(j);
-                    pw.print("      ");
-                    pw.print(inst.hashCode());
+                    JobInstance inst = obs.mJobs.valueAt(j);
+                    pw.print("        ");
+                    pw.print(System.identityHashCode(inst.mJobStatus));
                     if (inst.mChangedAuthorities != null) {
                         pw.println(":");
-                        pw.println("        Changed Authorities:");
+                        if (inst.mTriggerPending) {
+                            pw.print("          Trigger pending: update=");
+                            TimeUtils.formatDuration(
+                                    inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
+                            pw.print(", max=");
+                            TimeUtils.formatDuration(
+                                    inst.mJobStatus.getTriggerContentMaxDelay(), pw);
+                            pw.println();
+                        }
+                        pw.println("          Changed Authorities:");
                         for (int k=0; k<inst.mChangedAuthorities.size(); k++) {
                             pw.print("          ");
                             pw.println(inst.mChangedAuthorities.valueAt(k));
                         }
                         if (inst.mChangedUris != null) {
-                            pw.println("        Changed URIs:");
+                            pw.println("          Changed URIs:");
                             for (int k = 0; k<inst.mChangedUris.size(); k++) {
                                 pw.print("          ");
                                 pw.println(inst.mChangedUris.valueAt(k));
diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 64887e8..fe563d2 100644
--- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -28,6 +28,7 @@
 import com.android.server.DeviceIdleController;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobStore;
 import com.android.server.job.StateChangedListener;
 
 import java.io.PrintWriter;
@@ -45,9 +46,9 @@
 
     // Singleton factory
     private static Object sCreationLock = new Object();
-    final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
     private static DeviceIdleJobsController sController;
 
+    private final JobSchedulerService mJobSchedulerService;
     private final PowerManager mPowerManager;
     private final DeviceIdleController.LocalService mLocalDeviceIdleController;
 
@@ -57,6 +58,12 @@
     private boolean mDeviceIdleMode;
     private int[] mDeviceIdleWhitelistAppIds;
 
+    final JobStore.JobStatusFunctor mUpdateFunctor = new JobStore.JobStatusFunctor() {
+        @Override public void process(JobStatus jobStatus) {
+            updateTaskStateLocked(jobStatus);
+        }
+    };
+
     /**
      * Returns a singleton for the DeviceIdleJobsController
      */
@@ -87,10 +94,11 @@
         }
     };
 
-    private DeviceIdleJobsController(StateChangedListener stateChangedListener, Context context,
+    private DeviceIdleJobsController(JobSchedulerService jobSchedulerService, Context context,
             Object lock) {
-        super(stateChangedListener, context, lock);
+        super(jobSchedulerService, context, lock);
 
+        mJobSchedulerService = jobSchedulerService;
         // Register for device idle mode changes
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mLocalDeviceIdleController =
@@ -115,9 +123,7 @@
             }
             mDeviceIdleMode = enabled;
             if (LOG_DEBUG) Slog.d(LOG_TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
-            for (JobStatus task : mTrackedTasks) {
-                updateTaskStateLocked(task);
-            }
+            mJobSchedulerService.getJobStore().forEachJob(mUpdateFunctor);
         }
         // Inform the job scheduler service about idle mode changes
         if (changed) {
@@ -160,25 +166,26 @@
     @Override
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
         synchronized (mLock) {
-            mTrackedTasks.add(jobStatus);
             updateTaskStateLocked(jobStatus);
         }
     }
 
     @Override
     public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
-        mTrackedTasks.remove(jobStatus);
     }
 
     @Override
-    public void dumpControllerStateLocked(PrintWriter pw) {
+    public void dumpControllerStateLocked(final PrintWriter pw) {
         pw.println("DeviceIdleJobsController");
-        for (JobStatus task : mTrackedTasks) {
-            pw.print(task.getSourcePackageName());
-            pw.print(":runnable="
-                    + ((task.satisfiedConstraints & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0));
-            pw.print(", ");
-        }
+        mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+            @Override public void process(JobStatus jobStatus) {
+                pw.print("  ");
+                pw.print(jobStatus.getSourcePackageName());
+                pw.print(": runnable=");
+                pw.println((jobStatus.satisfiedConstraints
+                        & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
+            }
+        });
         pw.println();
     }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 39905d8..dd70758 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -61,6 +61,18 @@
     // Full override: ignore all constraints including API-affecting like connectivity
     public static final int OVERRIDE_FULL = 2;
 
+    /** If not specified, trigger update delay is 10 seconds. */
+    public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000;
+
+    /** The minimum possible update delay is 1/2 second. */
+    public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
+
+    /** If not specified, trigger maxumum delay is 2 minutes. */
+    public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
+
+    /** The minimum possible update delay is 1 second. */
+    public static final long MIN_TRIGGER_MAX_DELAY = 1000;
+
     final JobInfo job;
     /** Uid of the package requesting this job. */
     final int callingUid;
@@ -320,6 +332,22 @@
         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
     }
 
+    public long getTriggerContentUpdateDelay() {
+        long time = job.getTriggerContentUpdateDelay();
+        if (time < 0) {
+            return DEFAULT_TRIGGER_UPDATE_DELAY;
+        }
+        return Math.max(time, MIN_TRIGGER_UPDATE_DELAY);
+    }
+
+    public long getTriggerContentMaxDelay() {
+        long time = job.getTriggerContentMaxDelay();
+        if (time < 0) {
+            return DEFAULT_TRIGGER_MAX_DELAY;
+        }
+        return Math.max(time, MIN_TRIGGER_MAX_DELAY);
+    }
+
     public boolean isPersisted() {
         return job.isPersisted();
     }
@@ -540,6 +568,16 @@
                     pw.print(Integer.toHexString(trig.getFlags()));
                     pw.print(' '); pw.println(trig.getUri());
                 }
+                if (job.getTriggerContentUpdateDelay() >= 0) {
+                    pw.print(prefix); pw.print("  Trigger update delay: ");
+                    TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
+                    pw.println();
+                }
+                if (job.getTriggerContentMaxDelay() >= 0) {
+                    pw.print(prefix); pw.print("  Trigger max delay: ");
+                    TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
+                    pw.println();
+                }
             }
             if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
                 pw.print(prefix); pw.print("  Network type: "); pw.println(job.getNetworkType());
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/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index c19b51f..62fe70c 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,7 +16,7 @@
 
 package com.android.server.notification;
 
-import android.app.AutomaticZenRule;
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -29,7 +29,6 @@
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
-import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -82,6 +81,7 @@
         c.caption = "condition provider";
         c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
         c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
+        c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
         c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
         c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
         c.clientLabel = R.string.condition_provider_service_binding_label;
@@ -257,7 +257,7 @@
     }
 
     @Override
-    protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+    protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
             int userId) {
         final ContentResolver cr = mContext.getContentResolver();
         String settingValue = Settings.Secure.getStringForUser(
@@ -265,12 +265,17 @@
                 settingName,
                 userId);
         if (TextUtils.isEmpty(settingValue))
-            return null;
+            return new ArraySet<>();
         String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR);
         ArraySet<ComponentName> result = new ArraySet<>(packages.length);
         for (int i = 0; i < packages.length; i++) {
             if (!TextUtils.isEmpty(packages[i])) {
-                result.addAll(queryPackageForServices(packages[i], userId));
+                final ComponentName component = ComponentName.unflattenFromString(packages[i]);
+                if (component != null) {
+                    result.addAll(queryPackageForServices(component.getPackageName(), userId));
+                } else {
+                    result.addAll(queryPackageForServices(packages[i], userId));
+                }
             }
         }
         return result;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 6d9fed4..29e2e44 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<>();
 
@@ -128,7 +126,8 @@
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
                 String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
-                if (Objects.equals(element, mConfig.secureSettingName)) {
+                if (Objects.equals(element, mConfig.secureSettingName)
+                        || Objects.equals(element, mConfig.secondarySettingName)) {
                     String prevValue = intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
                     String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
                     settingRestored(element, prevValue, newValue, getSendingUserId());
@@ -188,8 +187,8 @@
 
     // By convention, restored settings are replicated to another settings
     // entry, named similarly but with a disambiguation suffix.
-    public static String restoredSettingName(Config config) {
-        return config.secureSettingName + ":restored";
+    public static String restoredSettingName(String setting) {
+        return setting + ":restored";
     }
 
     // The OS has done a restore of this service's saved state.  We clone it to the
@@ -199,14 +198,14 @@
     public void settingRestored(String element, String oldValue, String newValue, int userid) {
         if (DEBUG) Slog.d(TAG, "Restored managed service setting: " + element
                 + " ovalue=" + oldValue + " nvalue=" + newValue);
-        if (mConfig.secureSettingName.equals(element)) {
+        if (mConfig.secureSettingName.equals(element) ||
+                mConfig.secondarySettingName.equals(element)) {
             if (element != null) {
-                mRestored = null;
                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        restoredSettingName(mConfig),
+                        restoredSettingName(element),
                         newValue,
                         userid);
-                updateSettingsAccordingToInstalledServices(userid);
+                updateSettingsAccordingToInstalledServices(element, userid);
                 rebuildRestoredPackages();
             }
         }
@@ -342,65 +341,28 @@
         }
     }
 
-    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();
-        String settingName = restoredSettingName(mConfig);
+        String secureSettingName = restoredSettingName(mConfig.secureSettingName);
+        String secondarySettingName = mConfig.secondarySettingName == null
+                ? null : restoredSettingName(mConfig.secondarySettingName);
         int[] userIds = mUserProfiles.getCurrentProfileIds();
         final int N = userIds.length;
         for (int i = 0; i < N; ++i) {
-            ArraySet<ComponentName> names = loadComponentNamesFromSetting(settingName, userIds[i]);
-            if (names == null)
-                continue;
-            for (ComponentName name: names) {
+            ArraySet<ComponentName> names =
+                    loadComponentNamesFromSetting(secureSettingName, userIds[i]);
+            if (secondarySettingName != null) {
+                names.addAll(loadComponentNamesFromSetting(secondarySettingName, userIds[i]));
+            }
+            for (ComponentName name : names) {
                 mRestoredPackages.add(name.getPackageName());
             }
         }
     }
 
 
-    protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+    protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
             int userId) {
         final ContentResolver cr = mContext.getContentResolver();
         String settingValue = Settings.Secure.getStringForUser(
@@ -408,7 +370,7 @@
             settingName,
             userId);
         if (TextUtils.isEmpty(settingValue))
-            return null;
+            return new ArraySet<>();
         String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
         ArraySet<ComponentName> result = new ArraySet<>(restored.length);
         for (int i = 0; i < restored.length; i++) {
@@ -448,26 +410,22 @@
         int[] userIds = mUserProfiles.getCurrentProfileIds();
         final int N = userIds.length;
         for (int i = 0; i < N; ++i) {
-            updateSettingsAccordingToInstalledServices(userIds[i]);
+            updateSettingsAccordingToInstalledServices(mConfig.secureSettingName, userIds[i]);
+            if (mConfig.secondarySettingName != null) {
+                updateSettingsAccordingToInstalledServices(
+                        mConfig.secondarySettingName, userIds[i]);
+            }
         }
         rebuildRestoredPackages();
     }
 
     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,
@@ -493,13 +451,13 @@
         return installed;
     }
 
-    private void updateSettingsAccordingToInstalledServices(int userId) {
+    private void updateSettingsAccordingToInstalledServices(String setting, int userId) {
         boolean restoredChanged = false;
         boolean currentChanged = false;
         Set<ComponentName> restored =
-                loadComponentNamesFromSetting(restoredSettingName(mConfig), userId);
+                loadComponentNamesFromSetting(restoredSettingName(setting), userId);
         Set<ComponentName> current =
-                loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
+                loadComponentNamesFromSetting(setting, userId);
         // Load all services for all packages.
         Set<ComponentName> installed = queryPackageForServices(null, userId);
 
@@ -529,13 +487,13 @@
 
         if (currentChanged) {
             if (DEBUG) Slog.v(TAG, "List of  " + getCaption() + " services was updated " + current);
-            storeComponentsToSetting(retained, mConfig.secureSettingName, userId);
+            storeComponentsToSetting(retained, setting, userId);
         }
 
         if (restoredChanged) {
             if (DEBUG) Slog.v(TAG,
                     "List of  " + getCaption() + " restored services was updated " + restored);
-            storeComponentsToSetting(restored, restoredSettingName(mConfig), userId);
+            storeComponentsToSetting(restored, restoredSettingName(setting), userId);
         }
     }
 
@@ -553,6 +511,10 @@
         for (int i = 0; i < nUserIds; ++i) {
             componentsByUser.put(userIds[i],
                     loadComponentNamesFromSetting(mConfig.secureSettingName, userIds[i]));
+            if (mConfig.secondarySettingName != null) {
+                componentsByUser.get(userIds[i]).addAll(
+                        loadComponentNamesFromSetting(mConfig.secondarySettingName, userIds[i]));
+            }
         }
 
         final ArrayList<ManagedServiceInfo> removableBoundServices = new ArrayList<>();
@@ -573,20 +535,11 @@
                 // decode the list of components
                 final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
                 if (null == userComponents) {
-                    toAdd.put(userIds[i], new HashSet<ComponentName>());
+                    toAdd.put(userIds[i], new ArraySet<ComponentName>());
                     continue;
                 }
 
                 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);
@@ -835,15 +788,25 @@
 
     private class SettingsObserver extends ContentObserver {
         private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName);
+        private final Uri mSecondarySettingsUri;
 
         private SettingsObserver(Handler handler) {
             super(handler);
+            if (mConfig.secondarySettingName != null) {
+                mSecondarySettingsUri = Settings.Secure.getUriFor(mConfig.secondarySettingName);
+            } else {
+                mSecondarySettingsUri = null;
+            }
         }
 
         private void observe() {
             ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(mSecureSettingsUri,
                     false, this, UserHandle.USER_ALL);
+            if (mSecondarySettingsUri != null) {
+                resolver.registerContentObserver(mSecondarySettingsUri,
+                        false, this, UserHandle.USER_ALL);
+            }
             update(null);
         }
 
@@ -853,9 +816,9 @@
         }
 
         private void update(Uri uri) {
-            if (uri == null || mSecureSettingsUri.equals(uri)) {
-                if (DEBUG) Slog.d(TAG, "Setting changed: mSecureSettingsUri=" + mSecureSettingsUri +
-                        " / uri=" + uri);
+            if (uri == null || mSecureSettingsUri.equals(uri)
+                    || uri.equals(mSecondarySettingsUri)) {
+                if (DEBUG) Slog.d(TAG, "Setting changed: uri=" + uri);
                 rebindServices(false);
                 rebuildRestoredPackages();
             }
@@ -977,6 +940,7 @@
         public String caption;
         public String serviceInterface;
         public String secureSettingName;
+        public String secondarySettingName;
         public String bindPermission;
         public String settingsAction;
         public int clientLabel;
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5ce451f..d8c0775 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -318,6 +318,7 @@
     AccessibilityManager mAccessibilityManager;
     BurnInProtectionHelper mBurnInProtectionHelper;
     AppOpsManager mAppOpsManager;
+    private boolean mHasFeatureWatch;
 
     // Vibrator pattern for haptic feedback of a long press.
     long[] mLongPressVibePattern;
@@ -693,6 +694,7 @@
             = new LogDecelerateInterpolator(100, 0);
 
     private final MutableBoolean mTmpBoolean = new MutableBoolean(false);
+
     private static final int MSG_ENABLE_POINTER_LOCATION = 1;
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -1501,6 +1503,7 @@
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
 
         // Init display burn-in protection
         boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -5780,9 +5783,14 @@
     }
 
     private boolean shouldDispatchInputWhenNonInteractive() {
-        // Send events to keyguard while the screen is on.
-        if (isKeyguardShowingAndNotOccluded() && mDisplay != null
-                && mDisplay.getState() != Display.STATE_OFF) {
+        final boolean displayOff = (mDisplay == null || mDisplay.getState() == Display.STATE_OFF);
+
+        if (displayOff && !mHasFeatureWatch) {
+            return false;
+        }
+
+        // Send events to keyguard while the screen is on and it's showing.
+        if (isKeyguardShowingAndNotOccluded() && !displayOff) {
             return true;
         }
 
@@ -6695,7 +6703,7 @@
             @Override public void run() {
                 if (mBootMsgDialog == null) {
                     int theme;
-                    if (mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH)) {
+                    if (mHasFeatureWatch) {
                         theme = com.android.internal.R.style.Theme_Micro_Dialog_Alert;
                     } else if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEVISION)) {
                         theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 984fb76..3c84fc2 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -974,7 +974,6 @@
         public void register(Context context) {
             IntentFilter filter = new IntentFilter();
             filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
-            filter.addAction(Intent.ACTION_USER_PRESENT);
             filter.addAction(Intent.ACTION_USER_ADDED);
             filter.addAction(Intent.ACTION_USER_REMOVED);
             context.registerReceiverAsUser(this,
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/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index ed935ce..a5d68da 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -34,7 +34,6 @@
 import android.provider.Settings;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
-import android.webkit.WebViewFactory.MissingWebViewPackageException;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 
@@ -68,6 +67,7 @@
     @Override
     public WebViewProviderInfo[] getWebViewPackages() {
         int numFallbackPackages = 0;
+        int numAvailableByDefaultPackages = 0;
         XmlResourceParser parser = null;
         List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
         try {
@@ -83,12 +83,12 @@
                 if (element.equals(TAG_WEBVIEW_PROVIDER)) {
                     String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
                     if (packageName == null) {
-                        throw new MissingWebViewPackageException(
+                        throw new AndroidRuntimeException(
                                 "WebView provider in framework resources missing package name");
                     }
                     String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
                     if (description == null) {
-                        throw new MissingWebViewPackageException(
+                        throw new AndroidRuntimeException(
                                 "WebView provider in framework resources missing description");
                     }
                     boolean availableByDefault = "true".equals(
@@ -100,22 +100,33 @@
                             readSignatures(parser));
                     if (currentProvider.isFallback) {
                         numFallbackPackages++;
+                        if (!currentProvider.availableByDefault) {
+                            throw new AndroidRuntimeException(
+                                    "Each WebView fallback package must be available by default.");
+                        }
                         if (numFallbackPackages > 1) {
                             throw new AndroidRuntimeException(
-                                    "There can be at most one webview fallback package.");
+                                    "There can be at most one WebView fallback package.");
                         }
                     }
+                    if (currentProvider.availableByDefault) {
+                        numAvailableByDefaultPackages++;
+                    }
                     webViewProviders.add(currentProvider);
                 }
                 else {
-                    Log.e(TAG, "Found an element that is not a webview provider");
+                    Log.e(TAG, "Found an element that is not a WebView provider");
                 }
             }
         } catch (XmlPullParserException | IOException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+            throw new AndroidRuntimeException("Error when parsing WebView config " + e);
         } finally {
             if (parser != null) parser.close();
         }
+        if (numAvailableByDefaultPackages == 0) {
+            throw new AndroidRuntimeException("There must be at least one WebView package "
+                    + "that is available by default");
+        }
         return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
     }
 
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..058b631 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) {
@@ -1165,7 +1165,7 @@
 
 static jobject translate_gnss_clock(JNIEnv* env, GnssClock* clock) {
     JavaObject object(env, "android/location/GnssClock");
-    GpsClockFlags flags = clock->flags;
+    GnssClockFlags flags = clock->flags;
 
     SET_IF(GNSS_CLOCK_HAS_LEAP_SECOND,
            LeapSecond,
@@ -1237,9 +1237,10 @@
 static jobject translate_gnss_measurement(JNIEnv* env,
                                           GnssMeasurement* measurement) {
     JavaObject object(env, "android/location/GnssMeasurement");
-    GpsMeasurementFlags flags = measurement->flags;
 
-    SET(Svid, measurement->svid);
+    GnssMeasurementFlags flags = measurement->flags;
+
+    SET(Svid, static_cast<int32_t>(measurement->svid));
     SET(ConstellationType, static_cast<int32_t>(measurement->constellation));
     SET(TimeOffsetNanos, measurement->time_offset_ns);
     SET(State, static_cast<int32_t>(measurement->state));
@@ -1379,8 +1380,8 @@
         ALOGE("Invalid data provided to gps_measurement_callback");
         return;
     }
-    if (data->size != sizeof(GpsData)) {
-        ALOGE("Invalid GpsData size found in gps_measurement_callback, "
+    if (data->size != sizeof(GnssData)) {
+        ALOGE("Invalid GnssData size found in gnss_measurement_callback, "
               "size=%zd",
               data->size);
         return;
@@ -1486,26 +1487,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 +1498,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 +1515,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..59ebf1b 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -30,10 +30,12 @@
 import android.net.RouteInfo;
 import android.net.StaticIpConfiguration;
 import android.net.dhcp.DhcpClient;
+import android.net.metrics.IpManagerEvent;
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
@@ -52,6 +54,10 @@
 import java.net.SocketException;
 import java.util.Objects;
 
+import static android.net.metrics.IpConnectivityEvent.IPCE_IPMGR_PROVISIONING_OK;
+import static android.net.metrics.IpConnectivityEvent.IPCE_IPMGR_PROVISIONING_FAIL;
+import static android.net.metrics.IpConnectivityEvent.IPCE_IPMGR_COMPLETE_LIFECYCLE;
+
 
 /**
  * IpManager
@@ -264,6 +270,7 @@
     private ProxyInfo mHttpProxy;
     private ApfFilter mApfFilter;
     private boolean mMulticastFiltering;
+    private long mStartTimeMillis;
 
     /**
      * Member variables accessed both from within the StateMachine thread
@@ -355,6 +362,8 @@
 
     public void startProvisioning(ProvisioningConfiguration req) {
         getNetworkInterface();
+
+        mCallback.setNeighborDiscoveryOffload(true);
         sendMessage(CMD_START, new ProvisioningConfiguration(req));
     }
 
@@ -477,6 +486,12 @@
         }
     }
 
+    private void recordMetric(final int type) {
+        if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); }
+        IpManagerEvent.logEvent(type, mInterfaceName,
+                SystemClock.elapsedRealtime() - mStartTimeMillis);
+    }
+
     // For now: use WifiStateMachine's historical notion of provisioned.
     private static boolean isProvisioned(LinkProperties lp) {
         // For historical reasons, we should connect even if all we have is
@@ -534,6 +549,16 @@
             delta = ProvisioningChange.LOST_PROVISIONING;
         }
 
+        // Additionally:
+        //
+        // If the previous link properties had a global IPv6 address and an
+        // IPv6 default route then also consider the loss of that default route
+        // to be a loss of provisioning. See b/27962810.
+        if (oldLp.hasGlobalIPv6Address() && oldLp.hasIPv6DefaultRoute() &&
+                !newLp.hasIPv6DefaultRoute()) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
         return delta;
     }
 
@@ -542,11 +567,13 @@
         switch (delta) {
             case GAINED_PROVISIONING:
                 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+                recordMetric(IPCE_IPMGR_PROVISIONING_OK);
                 mCallback.onProvisioningSuccess(newLp);
                 break;
 
             case LOST_PROVISIONING:
                 if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                recordMetric(IPCE_IPMGR_PROVISIONING_FAIL);
                 mCallback.onProvisioningFailure(newLp);
                 break;
 
@@ -722,6 +749,10 @@
             }
 
             resetLinkProperties();
+            if (mStartTimeMillis > 0) {
+                recordMetric(IPCE_IPMGR_COMPLETE_LIFECYCLE);
+                mStartTimeMillis = 0;
+            }
         }
 
         @Override
@@ -792,11 +823,16 @@
     class StartedState extends State {
         @Override
         public void enter() {
+            mStartTimeMillis = SystemClock.elapsedRealtime();
+
             mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
                     mCallback, mMulticastFiltering);
             // TODO: investigate the effects of any multicast filtering racing/interfering with the
             // rest of this IP configuration startup.
-            if (mApfFilter == null) mCallback.setFallbackMulticastFilter(mMulticastFiltering);
+            if (mApfFilter == null) {
+                mCallback.setFallbackMulticastFilter(mMulticastFiltering);
+            }
+
             // Set privacy extensions.
             try {
                 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
@@ -827,6 +863,7 @@
                     handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
                 } else {
                     if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                    recordMetric(IPCE_IPMGR_PROVISIONING_FAIL);
                     mCallback.onProvisioningFailure(getLinkProperties());
                     transitionTo(mStoppingState);
                 }
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index af3175a..c8dbe02 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -26,6 +26,7 @@
 import android.net.RouteInfo;
 import android.net.metrics.IpReachabilityMonitorMessageEvent;
 import android.net.metrics.IpReachabilityMonitorProbeEvent;
+import android.net.metrics.IpReachabilityMonitorLostEvent;
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkErrorMessage;
 import android.net.netlink.NetlinkMessage;
@@ -353,6 +354,7 @@
         }
 
         if (delta == ProvisioningChange.LOST_PROVISIONING) {
+            IpReachabilityMonitorLostEvent.logEvent(mInterfaceName);
             final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg;
             Log.w(TAG, logMsg);
             if (mCallback != null) {
@@ -521,7 +523,7 @@
 
             final short msgType = neighMsg.getHeader().nlmsg_type;
             final short nudState = ndMsg.ndm_state;
-            IpReachabilityMonitorMessageEvent.logEvent(maybeGetInterfaceName(mInterfaceIndex),
+            IpReachabilityMonitorMessageEvent.logEvent(mInterfaceName,
                     destination.getHostAddress(), msgType, nudState);
             final String eventMsg = "NeighborEvent{"
                     + "elapsedMs=" + whenMs + ", "
@@ -553,11 +555,4 @@
             }
         }
     }
-
-    private String maybeGetInterfaceName(int index) {
-        if (index == mInterfaceIndex) {
-            return mInterfaceName;
-        }
-        return "ifindex-" + index;
-    }
 }
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..0bca628 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. */
@@ -3629,22 +3644,6 @@
     }
 
     /**
-     * Returns the response of SIM Authentication through RIL for the default subscription.
-     * Returns null if the Authentication hasn't been successful
-     *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
-     *
-     * @param appType ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
-     * @param data authentication challenge data
-     * @return the response of SIM Authentication, or null if not available
-     * @hide
-     */
-    public String getIccSimChallengeResponse(int appType, String data) {
-        return getIccAuthentication(getDefaultSubscription(), appType, AUTHTYPE_EAP_SIM, data);
-    }
-
-    /**
      * Get P-CSCF address from PCO after data connection is established or modified.
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
      * @return array of P-CSCF address
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/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
deleted file mode 100644
index 069fcbf..0000000
--- a/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2015 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.telephony;
-
-import com.android.internal.telephony.ITelephonyDebugSubscriber;
-
-import android.os.Bundle;
-
-/**
- * Interface used to interact with the Telephony debug service.
- *
- * {@hide}
- */
-interface ITelephonyDebug {
-
-    /**
-     * Write telephony event
-     * @param timestamp returned by System.currentTimeMillis()
-     * @param phoneId for which event is written
-     * @param tag constant defined in TelephonyEventLog
-     * @param param1 optional
-     * @param param2 optional
-     * @param data optional
-     */
-    void writeEvent(long timestamp, int phoneId, int tag, int param1, int param2, in Bundle data);
-
-    void subscribe(in ITelephonyDebugSubscriber subscriber);
-    void unsubscribe(in ITelephonyDebugSubscriber subscriber);
-}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl b/telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl
deleted file mode 100644
index 64eb0f1..0000000
--- a/telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.telephony;
-
-import com.android.internal.telephony.TelephonyEvent;
-
-import android.os.Bundle;
-
-/**
- * Interface used to subscribe for events from Telephony debug service.
- *
- * {@hide}
- */
-oneway interface ITelephonyDebugSubscriber {
-
-    /**
-     * Called when Telephony debug service has events.
-     */
-    void onEvents(in TelephonyEvent[] events);
-}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyEvent.java b/telephony/java/com/android/internal/telephony/TelephonyEvent.java
deleted file mode 100644
index 26d466d..0000000
--- a/telephony/java/com/android/internal/telephony/TelephonyEvent.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.telephony;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- *  A parcelable used in ITelephonyDebugSubscriber.aidl
- */
-public class TelephonyEvent implements Parcelable {
-
-    final public long timestamp;
-    final public int phoneId;
-    final public int tag;
-    final public int param1;
-    final public int param2;
-    final public Bundle data;
-
-    public TelephonyEvent(long timestamp, int phoneId, int tag,
-            int param1, int param2, Bundle data) {
-        this.timestamp = timestamp;
-        this.phoneId = phoneId;
-        this.tag = tag;
-        this.param1 = param1;
-        this.param2 = param2;
-        this.data = data;
-    }
-
-    /** Implement the Parcelable interface */
-    public static final Parcelable.Creator<TelephonyEvent> CREATOR
-            = new Parcelable.Creator<TelephonyEvent> (){
-        public TelephonyEvent createFromParcel(Parcel source) {
-            final long timestamp = source.readLong();
-            final int phoneId = source.readInt();
-            final int tag = source.readInt();
-            final int param1 = source.readInt();
-            final int param2 = source.readInt();
-            final Bundle data = source.readBundle();
-            return new TelephonyEvent(timestamp, phoneId, tag, param1, param2, data);
-        }
-
-        public TelephonyEvent[] newArray(int size) {
-            return new TelephonyEvent[size];
-        }
-    };
-
-    /** Implement the Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Implement the Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(timestamp);
-        dest.writeInt(phoneId);
-        dest.writeInt(tag);
-        dest.writeInt(param1);
-        dest.writeInt(param2);
-        dest.writeBundle(data);
-    }
-
-    public String toString() {
-        return String.format("%d,%d,%d,%d,%d,%s",
-                timestamp, phoneId, tag, param1, param2, data);
-    }
-}
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/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index fb2bdd4..b8ef150 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1636,6 +1636,9 @@
             if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
                 keyMgmt = KeyMgmt.strings[KeyMgmt.WPA_EAP];
             }
+            if (allowedKeyManagement.get(KeyMgmt.OSEN)) {
+                keyMgmt = KeyMgmt.strings[KeyMgmt.OSEN];
+            }
             if (allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
                 keyMgmt += KeyMgmt.strings[KeyMgmt.IEEE8021X];
             }
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 394934f..b614a86 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -437,6 +437,7 @@
         switch (eapMethod) {
             /** Valid methods */
             case Eap.TLS:
+            case Eap.UNAUTH_TLS:
                 setPhase2Method(Phase2.NONE);
                 /* fall through */
             case Eap.PEAP:
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;
             }