Merge "Call notifyLockedProfile in TrustManagerService instead of KeyguardViewMediator." 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 fbbb2e3..f924b5b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -891,6 +891,7 @@
     field public static final int notificationTimeout = 16843651; // 0x1010383
     field public static final int numColumns = 16843032; // 0x1010118
     field public static final int numStars = 16843076; // 0x1010144
+    field public static final int numberPickerStyle = 16844071; // 0x1010527
     field public static final int numbersBackgroundColor = 16843938; // 0x10104a2
     field public static final int numbersInnerTextColor = 16844001; // 0x10104e1
     field public static final int numbersSelectorColor = 16843939; // 0x10104a3
@@ -2534,6 +2535,7 @@
     field public static final int Widget_Material_Light_ListView = 16974513; // 0x10302b1
     field public static final int Widget_Material_Light_ListView_DropDown = 16974514; // 0x10302b2
     field public static final int Widget_Material_Light_MediaRouteButton = 16974515; // 0x10302b3
+    field public static final int Widget_Material_Light_NumberPicker = 16974557; // 0x10302dd
     field public static final int Widget_Material_Light_PopupMenu = 16974516; // 0x10302b4
     field public static final int Widget_Material_Light_PopupMenu_Overflow = 16974517; // 0x10302b5
     field public static final int Widget_Material_Light_PopupWindow = 16974518; // 0x10302b6
@@ -2566,6 +2568,7 @@
     field public static final int Widget_Material_ListView = 16974448; // 0x1030270
     field public static final int Widget_Material_ListView_DropDown = 16974449; // 0x1030271
     field public static final int Widget_Material_MediaRouteButton = 16974450; // 0x1030272
+    field public static final int Widget_Material_NumberPicker = 16974556; // 0x10302dc
     field public static final int Widget_Material_PopupMenu = 16974451; // 0x1030273
     field public static final int Widget_Material_PopupMenu_Overflow = 16974452; // 0x1030274
     field public static final int Widget_Material_PopupWindow = 16974453; // 0x1030275
@@ -4480,6 +4483,7 @@
     method public void startActivity(android.content.Intent, android.os.Bundle);
     method public void startActivityForResult(android.content.Intent, int);
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public void unregisterForContextMenu(android.view.View);
   }
 
@@ -4571,6 +4575,7 @@
     method public void onRequestPermissionsFromFragment(android.app.Fragment, java.lang.String[], int);
     method public boolean onShouldSaveFragmentState(android.app.Fragment);
     method public void onStartActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public boolean onUseFragmentManagerInflaterFactory();
   }
 
@@ -5031,13 +5036,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 +5902,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 +5925,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 +5976,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 +6004,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 +6318,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 +6350,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 +8624,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 +8719,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,10 +9508,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, android.os.UserHandle);
-    method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
-    method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, 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 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);
@@ -9512,6 +9521,7 @@
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+    method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
@@ -9533,6 +9543,7 @@
     method public void setChangedSince(long);
     method public void setPackage(java.lang.String);
     method public void setQueryFlags(int);
+    method public void setShortcutIds(java.util.List<java.lang.String>);
     field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
     field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
     field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10039,13 +10050,16 @@
   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();
     method public java.lang.String getPackageName();
     method public java.lang.String getText();
     method public java.lang.String getTitle();
+    method public android.os.UserHandle getUserHandle();
     method public int getWeight();
     method public boolean hasIconFile();
     method public boolean hasIconResource();
@@ -10062,12 +10076,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);
@@ -10078,15 +10094,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>);
   }
@@ -14432,13 +14448,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 {
@@ -19853,8 +19869,8 @@
 
   public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
-    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -19879,8 +19895,8 @@
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
-    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19914,8 +19930,9 @@
     method public abstract void onPeriodicNotification(android.media.AudioRecord);
   }
 
-  public static abstract interface AudioRecord.OnRoutingChangedListener {
+  public static abstract deprecated interface AudioRecord.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
     method public abstract void onRoutingChanged(android.media.AudioRecord);
+    method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
   public final class AudioRecordingConfiguration implements android.os.Parcelable {
@@ -19930,10 +19947,10 @@
   }
 
   public abstract interface AudioRouting {
-    method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
     method public abstract android.media.AudioDeviceInfo getRoutedDevice();
-    method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public abstract void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
   }
 
@@ -19953,8 +19970,8 @@
     ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
-    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method public void flush();
     method public int getAudioFormat();
@@ -19986,8 +20003,8 @@
     method public void play() throws java.lang.IllegalStateException;
     method public void release();
     method public int reloadStaticData();
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
-    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setAuxEffectSendLevel(float);
     method public int setBufferSizeInFrames(int);
     method public int setLoopPoints(int, int, int);
@@ -20041,8 +20058,9 @@
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
   }
 
-  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
-    method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
+  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
+    method public abstract void onRoutingChanged(android.media.AudioTrack);
+    method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
   public class CamcorderProfile {
@@ -20718,10 +20736,12 @@
     field public static final int VP9Level6 = 1024; // 0x400
     field public static final int VP9Level61 = 2048; // 0x800
     field public static final int VP9Level62 = 4096; // 0x1000
-    field public static final int VP9Profile0 = 0; // 0x0
-    field public static final int VP9Profile1 = 1; // 0x1
-    field public static final int VP9Profile2 = 2; // 0x2
-    field public static final int VP9Profile3 = 3; // 0x3
+    field public static final int VP9Profile0 = 1; // 0x1
+    field public static final int VP9Profile1 = 2; // 0x2
+    field public static final int VP9Profile2 = 4; // 0x4
+    field public static final int VP9Profile2HDR = 4096; // 0x1000
+    field public static final int VP9Profile3 = 8; // 0x8
+    field public static final int VP9Profile3HDR = 8192; // 0x2000
     field public int level;
     field public int profile;
   }
@@ -22878,6 +22898,7 @@
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
     field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -22958,6 +22979,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();
@@ -22987,6 +23009,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);
   }
 
@@ -29576,6 +29599,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);
@@ -34937,6 +34961,7 @@
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(java.lang.String[]);
     method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
+    method public void onHandleAssistSecondary(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent, int, int);
     method public void onHandleScreenshot(android.graphics.Bitmap);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -49053,6 +49078,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;
   }
@@ -49923,6 +49949,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);
@@ -57579,6 +57610,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[]);
@@ -58034,6 +58066,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 {
@@ -58077,6 +58110,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();
   }
@@ -58617,6 +58651,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[]);
@@ -59058,6 +59104,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);
@@ -59159,6 +59206,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>);
   }
 
 }
@@ -60132,18 +60180,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);
@@ -61516,6 +61552,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();
@@ -61525,6 +61562,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 d91f5d1..915ceb2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -217,6 +217,7 @@
     field public static final java.lang.String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
     field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
     field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
+    field public static final java.lang.String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final java.lang.String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
     field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
@@ -987,6 +988,7 @@
     field public static final int notificationTimeout = 16843651; // 0x1010383
     field public static final int numColumns = 16843032; // 0x1010118
     field public static final int numStars = 16843076; // 0x1010144
+    field public static final int numberPickerStyle = 16844071; // 0x1010527
     field public static final int numbersBackgroundColor = 16843938; // 0x10104a2
     field public static final int numbersInnerTextColor = 16844001; // 0x10104e1
     field public static final int numbersSelectorColor = 16843939; // 0x10104a3
@@ -2637,6 +2639,7 @@
     field public static final int Widget_Material_Light_ListView = 16974513; // 0x10302b1
     field public static final int Widget_Material_Light_ListView_DropDown = 16974514; // 0x10302b2
     field public static final int Widget_Material_Light_MediaRouteButton = 16974515; // 0x10302b3
+    field public static final int Widget_Material_Light_NumberPicker = 16974557; // 0x10302dd
     field public static final int Widget_Material_Light_PopupMenu = 16974516; // 0x10302b4
     field public static final int Widget_Material_Light_PopupMenu_Overflow = 16974517; // 0x10302b5
     field public static final int Widget_Material_Light_PopupWindow = 16974518; // 0x10302b6
@@ -2669,6 +2672,7 @@
     field public static final int Widget_Material_ListView = 16974448; // 0x1030270
     field public static final int Widget_Material_ListView_DropDown = 16974449; // 0x1030271
     field public static final int Widget_Material_MediaRouteButton = 16974450; // 0x1030272
+    field public static final int Widget_Material_NumberPicker = 16974556; // 0x10302dc
     field public static final int Widget_Material_PopupMenu = 16974451; // 0x1030273
     field public static final int Widget_Material_PopupMenu_Overflow = 16974452; // 0x1030274
     field public static final int Widget_Material_PopupWindow = 16974453; // 0x1030275
@@ -4613,6 +4617,7 @@
     method public void startActivity(android.content.Intent, android.os.Bundle);
     method public void startActivityForResult(android.content.Intent, int);
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public void unregisterForContextMenu(android.view.View);
   }
 
@@ -4704,6 +4709,7 @@
     method public void onRequestPermissionsFromFragment(android.app.Fragment, java.lang.String[], int);
     method public boolean onShouldSaveFragmentState(android.app.Fragment);
     method public void onStartActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public boolean onUseFragmentManagerInflaterFactory();
   }
 
@@ -5165,13 +5171,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 +6046,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 +6073,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 +6127,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 +6155,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 +6587,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 +6619,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 +8944,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,10 +9846,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, android.os.UserHandle);
-    method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
-    method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, 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 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);
@@ -9850,6 +9859,7 @@
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+    method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
@@ -9871,6 +9881,7 @@
     method public void setChangedSince(long);
     method public void setPackage(java.lang.String);
     method public void setQueryFlags(int);
+    method public void setShortcutIds(java.util.List<java.lang.String>);
     field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
     field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
     field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10439,13 +10450,16 @@
   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();
     method public java.lang.String getPackageName();
     method public java.lang.String getText();
     method public java.lang.String getTitle();
+    method public android.os.UserHandle getUserHandle();
     method public int getWeight();
     method public boolean hasIconFile();
     method public boolean hasIconResource();
@@ -10462,12 +10476,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);
@@ -10478,15 +10494,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>);
   }
@@ -14840,20 +14856,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 {
@@ -15491,6 +15507,7 @@
 
   public class NanoApp {
     ctor public NanoApp();
+    ctor public NanoApp(int, byte[]);
     method public int describeContents();
     method public byte[] getAppBinary();
     method public int getAppId();
@@ -21331,8 +21348,8 @@
   public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
-    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -21357,8 +21374,8 @@
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
-    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -21394,8 +21411,9 @@
     method public abstract void onPeriodicNotification(android.media.AudioRecord);
   }
 
-  public static abstract interface AudioRecord.OnRoutingChangedListener {
+  public static abstract deprecated interface AudioRecord.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
     method public abstract void onRoutingChanged(android.media.AudioRecord);
+    method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
   public final class AudioRecordingConfiguration implements android.os.Parcelable {
@@ -21410,10 +21428,10 @@
   }
 
   public abstract interface AudioRouting {
-    method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
     method public abstract android.media.AudioDeviceInfo getRoutedDevice();
-    method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public abstract void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
   }
 
@@ -21433,8 +21451,8 @@
     ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
-    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method public void flush();
     method public int getAudioFormat();
@@ -21466,8 +21484,8 @@
     method public void play() throws java.lang.IllegalStateException;
     method public void release();
     method public int reloadStaticData();
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
-    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setAuxEffectSendLevel(float);
     method public int setBufferSizeInFrames(int);
     method public int setLoopPoints(int, int, int);
@@ -21521,8 +21539,9 @@
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
   }
 
-  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
-    method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
+  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
+    method public abstract void onRoutingChanged(android.media.AudioTrack);
+    method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
   public class CamcorderProfile {
@@ -22198,10 +22217,12 @@
     field public static final int VP9Level6 = 1024; // 0x400
     field public static final int VP9Level61 = 2048; // 0x800
     field public static final int VP9Level62 = 4096; // 0x1000
-    field public static final int VP9Profile0 = 0; // 0x0
-    field public static final int VP9Profile1 = 1; // 0x1
-    field public static final int VP9Profile2 = 2; // 0x2
-    field public static final int VP9Profile3 = 3; // 0x3
+    field public static final int VP9Profile0 = 1; // 0x1
+    field public static final int VP9Profile1 = 2; // 0x2
+    field public static final int VP9Profile2 = 4; // 0x4
+    field public static final int VP9Profile2HDR = 4096; // 0x1000
+    field public static final int VP9Profile3 = 8; // 0x8
+    field public static final int VP9Profile3HDR = 8192; // 0x2000
     field public int level;
     field public int profile;
   }
@@ -24483,6 +24504,7 @@
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
     field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -24614,6 +24636,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();
@@ -24647,6 +24670,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);
@@ -31881,6 +31905,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);
@@ -37510,6 +37535,7 @@
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(java.lang.String[]);
     method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
+    method public void onHandleAssistSecondary(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent, int, int);
     method public void onHandleScreenshot(android.graphics.Bitmap);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -52165,6 +52191,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;
   }
@@ -53035,6 +53062,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);
@@ -60691,6 +60723,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[]);
@@ -61146,6 +61179,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 {
@@ -61189,6 +61223,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();
   }
@@ -61729,6 +61764,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[]);
@@ -62170,6 +62217,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);
@@ -62271,6 +62319,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>);
   }
 
 }
@@ -63244,18 +63293,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);
@@ -64628,6 +64665,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();
@@ -64637,6 +64675,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 bad9733..0c86fd1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -891,6 +891,7 @@
     field public static final int notificationTimeout = 16843651; // 0x1010383
     field public static final int numColumns = 16843032; // 0x1010118
     field public static final int numStars = 16843076; // 0x1010144
+    field public static final int numberPickerStyle = 16844071; // 0x1010527
     field public static final int numbersBackgroundColor = 16843938; // 0x10104a2
     field public static final int numbersInnerTextColor = 16844001; // 0x10104e1
     field public static final int numbersSelectorColor = 16843939; // 0x10104a3
@@ -2534,6 +2535,7 @@
     field public static final int Widget_Material_Light_ListView = 16974513; // 0x10302b1
     field public static final int Widget_Material_Light_ListView_DropDown = 16974514; // 0x10302b2
     field public static final int Widget_Material_Light_MediaRouteButton = 16974515; // 0x10302b3
+    field public static final int Widget_Material_Light_NumberPicker = 16974557; // 0x10302dd
     field public static final int Widget_Material_Light_PopupMenu = 16974516; // 0x10302b4
     field public static final int Widget_Material_Light_PopupMenu_Overflow = 16974517; // 0x10302b5
     field public static final int Widget_Material_Light_PopupWindow = 16974518; // 0x10302b6
@@ -2566,6 +2568,7 @@
     field public static final int Widget_Material_ListView = 16974448; // 0x1030270
     field public static final int Widget_Material_ListView_DropDown = 16974449; // 0x1030271
     field public static final int Widget_Material_MediaRouteButton = 16974450; // 0x1030272
+    field public static final int Widget_Material_NumberPicker = 16974556; // 0x10302dc
     field public static final int Widget_Material_PopupMenu = 16974451; // 0x1030273
     field public static final int Widget_Material_PopupMenu_Overflow = 16974452; // 0x1030274
     field public static final int Widget_Material_PopupWindow = 16974453; // 0x1030275
@@ -4480,6 +4483,7 @@
     method public void startActivity(android.content.Intent, android.os.Bundle);
     method public void startActivityForResult(android.content.Intent, int);
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
+    method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public void unregisterForContextMenu(android.view.View);
   }
 
@@ -4571,6 +4575,7 @@
     method public void onRequestPermissionsFromFragment(android.app.Fragment, java.lang.String[], int);
     method public boolean onShouldSaveFragmentState(android.app.Fragment);
     method public void onStartActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public void onStartIntentSenderFromFragment(android.app.Fragment, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public boolean onUseFragmentManagerInflaterFactory();
   }
 
@@ -5031,13 +5036,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 +5906,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 +5929,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 +5980,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 +6008,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 +6322,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 +6354,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 +8631,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 +8726,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";
@@ -9505,11 +9516,10 @@
   }
 
   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, android.os.UserHandle);
-    method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
-    method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, 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 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);
@@ -9521,6 +9531,7 @@
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
+    method public boolean startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
@@ -9542,6 +9553,7 @@
     method public void setChangedSince(long);
     method public void setPackage(java.lang.String);
     method public void setQueryFlags(int);
+    method public void setShortcutIds(java.util.List<java.lang.String>);
     field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
     field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
     field public static final int FLAG_GET_PINNED = 2; // 0x2
@@ -10049,13 +10061,16 @@
   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();
     method public java.lang.String getPackageName();
     method public java.lang.String getText();
     method public java.lang.String getTitle();
+    method public android.os.UserHandle getUserHandle();
     method public int getWeight();
     method public boolean hasIconFile();
     method public boolean hasIconResource();
@@ -10072,12 +10087,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);
@@ -10088,15 +10105,16 @@
   }
 
   public class ShortcutManager {
-    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
-    method public void deleteAllDynamicShortcuts();
-    method public void deleteDynamicShortcut(java.lang.String);
+    ctor public ShortcutManager(android.content.Context);
+    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>);
   }
@@ -14442,13 +14460,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 {
@@ -19919,8 +19937,8 @@
 
   public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
-    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -19945,8 +19963,8 @@
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
-    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19980,8 +19998,9 @@
     method public abstract void onPeriodicNotification(android.media.AudioRecord);
   }
 
-  public static abstract interface AudioRecord.OnRoutingChangedListener {
+  public static abstract deprecated interface AudioRecord.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
     method public abstract void onRoutingChanged(android.media.AudioRecord);
+    method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
   public final class AudioRecordingConfiguration implements android.os.Parcelable {
@@ -19996,10 +20015,10 @@
   }
 
   public abstract interface AudioRouting {
-    method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public abstract android.media.AudioDeviceInfo getPreferredDevice();
     method public abstract android.media.AudioDeviceInfo getRoutedDevice();
-    method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public abstract void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
   }
 
@@ -20019,8 +20038,8 @@
     ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
-    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method public void flush();
     method public int getAudioFormat();
@@ -20052,8 +20071,8 @@
     method public void play() throws java.lang.IllegalStateException;
     method public void release();
     method public int reloadStaticData();
+    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
-    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setAuxEffectSendLevel(float);
     method public int setBufferSizeInFrames(int);
     method public int setLoopPoints(int, int, int);
@@ -20107,8 +20126,9 @@
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
   }
 
-  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
-    method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
+  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener implements android.media.AudioRouting.OnRoutingChangedListener {
+    method public abstract void onRoutingChanged(android.media.AudioTrack);
+    method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
   public class CamcorderProfile {
@@ -20784,10 +20804,12 @@
     field public static final int VP9Level6 = 1024; // 0x400
     field public static final int VP9Level61 = 2048; // 0x800
     field public static final int VP9Level62 = 4096; // 0x1000
-    field public static final int VP9Profile0 = 0; // 0x0
-    field public static final int VP9Profile1 = 1; // 0x1
-    field public static final int VP9Profile2 = 2; // 0x2
-    field public static final int VP9Profile3 = 3; // 0x3
+    field public static final int VP9Profile0 = 1; // 0x1
+    field public static final int VP9Profile1 = 2; // 0x2
+    field public static final int VP9Profile2 = 4; // 0x4
+    field public static final int VP9Profile2HDR = 4096; // 0x1000
+    field public static final int VP9Profile3 = 8; // 0x8
+    field public static final int VP9Profile3HDR = 8192; // 0x2000
     field public int level;
     field public int profile;
   }
@@ -22944,6 +22966,7 @@
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
     field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -23024,6 +23047,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();
@@ -23053,6 +23077,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);
   }
 
@@ -29643,6 +29668,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);
@@ -35010,6 +35036,7 @@
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(java.lang.String[]);
     method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
+    method public void onHandleAssistSecondary(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent, int, int);
     method public void onHandleScreenshot(android.graphics.Bitmap);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -49129,6 +49156,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;
   }
@@ -49999,6 +50027,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);
@@ -57655,6 +57688,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[]);
@@ -58110,6 +58144,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 {
@@ -58153,6 +58188,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();
   }
@@ -58693,6 +58729,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[]);
@@ -59134,6 +59182,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);
@@ -59235,6 +59284,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>);
   }
 
 }
@@ -60208,18 +60258,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);
@@ -61592,6 +61630,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();
@@ -61601,6 +61640,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/Activity.java b/core/java/android/app/Activity.java
index 7652766..f7884a7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4405,8 +4405,8 @@
             @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
             Bundle options) throws IntentSender.SendIntentException {
         if (mParent == null) {
-            startIntentSenderForResultInner(intent, requestCode, fillInIntent,
-                    flagsMask, flagsValues, this, options);
+            startIntentSenderForResultInner(intent, mEmbeddedID, requestCode, fillInIntent,
+                    flagsMask, flagsValues, options);
         } else if (options != null) {
             mParent.startIntentSenderFromChild(this, intent, requestCode,
                     fillInIntent, flagsMask, flagsValues, extraFlags, options);
@@ -4418,8 +4418,8 @@
         }
     }
 
-    private void startIntentSenderForResultInner(IntentSender intent, int requestCode,
-            Intent fillInIntent, int flagsMask, int flagsValues, Activity activity,
+    private void startIntentSenderForResultInner(IntentSender intent, String who, int requestCode,
+            Intent fillInIntent, int flagsMask, int flagsValues,
             Bundle options)
             throws IntentSender.SendIntentException {
         try {
@@ -4431,7 +4431,7 @@
             }
             int result = ActivityManagerNative.getDefault()
                 .startActivityIntentSender(mMainThread.getApplicationThread(), intent,
-                        fillInIntent, resolvedType, mToken, activity.mEmbeddedID,
+                        fillInIntent, resolvedType, mToken, who,
                         requestCode, flagsMask, flagsValues, options);
             if (result == ActivityManager.START_CANCELED) {
                 throw new IntentSender.SendIntentException();
@@ -4888,8 +4888,23 @@
             int requestCode, Intent fillInIntent, int flagsMask, int flagsValues,
             int extraFlags, @Nullable Bundle options)
             throws IntentSender.SendIntentException {
-        startIntentSenderForResultInner(intent, requestCode, fillInIntent,
-                flagsMask, flagsValues, child, options);
+        startIntentSenderForResultInner(intent, child.mEmbeddedID, requestCode, fillInIntent,
+                flagsMask, flagsValues, options);
+    }
+
+    /**
+     * Like {@link #startIntentSenderFromChild}, but taking a Fragment; see
+     * {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
+     * for more information.
+     *
+     * @hide
+     */
+    public void startIntentSenderFromChildFragment(Fragment child, IntentSender intent,
+            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues,
+            int extraFlags, @Nullable Bundle options)
+            throws IntentSender.SendIntentException {
+        startIntentSenderForResultInner(intent, child.mWho, requestCode, fillInIntent,
+                flagsMask, flagsValues, options);
     }
 
     /**
@@ -7035,6 +7050,19 @@
         }
 
         @Override
+        public void onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent,
+                int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
+                int extraFlags, Bundle options) throws IntentSender.SendIntentException {
+            if (mParent == null) {
+                startIntentSenderForResultInner(intent, fragment.mWho, requestCode, fillInIntent,
+                        flagsMask, flagsValues, options);
+            } else if (options != null) {
+                mParent.startIntentSenderFromChildFragment(fragment, intent, requestCode,
+                        fillInIntent, flagsMask, flagsValues, extraFlags, options);
+            }
+        }
+
+        @Override
         public void onRequestPermissionsFromFragment(Fragment fragment, String[] permissions,
                 int requestCode) {
             String who = REQUEST_PERMISSIONS_WHO_PREFIX + fragment.mWho;
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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5116634..374c4f6 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,6 +23,8 @@
 
 import com.android.internal.app.IVoiceInteractor;
 
+import java.util.List;
+
 /**
  * Activity manager local system service interface.
  *
@@ -125,4 +127,10 @@
      * Callback for window manager to let activity manager know that the app transition is finished.
      */
     public abstract void notifyAppTransitionFinished();
+
+    /**
+     * Returns the top activity from each of the currently visible stacks. The first entry will be
+     * the focused activity.
+     */
+    public abstract List<IBinder> getTopVisibleActivities();
 }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 65d48e6..a82b950 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2411,8 +2411,11 @@
             data.enforceInterface(IActivityManager.descriptor);
             int requestType = data.readInt();
             IResultReceiver receiver = IResultReceiver.Stub.asInterface(data.readStrongBinder());
+            Bundle receiverExtras = data.readBundle();
             IBinder activityToken = data.readStrongBinder();
-            boolean res = requestAssistContextExtras(requestType, receiver, activityToken);
+            boolean focused = data.readInt() == 1;
+            boolean res = requestAssistContextExtras(requestType, receiver, receiverExtras,
+                    activityToken, focused);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -2575,9 +2578,10 @@
             return true;
         }
 
-        case START_LOCK_TASK_BY_CURRENT_TRANSACTION: {
+        case START_SYSTEM_LOCK_TASK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            startLockTaskModeOnCurrent();
+            int taskId = data.readInt();
+            startSystemLockTaskMode(taskId);
             reply.writeNoException();
             return true;
         }
@@ -2589,9 +2593,9 @@
             return true;
         }
 
-        case STOP_LOCK_TASK_BY_CURRENT_TRANSACTION: {
+        case STOP_SYSTEM_LOCK_TASK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            stopLockTaskModeOnCurrent();
+            stopSystemLockTaskMode();
             reply.writeNoException();
             return true;
         }
@@ -6080,13 +6084,16 @@
     }
 
     public boolean requestAssistContextExtras(int requestType, IResultReceiver receiver,
-            IBinder activityToken) throws RemoteException {
+            Bundle receiverExtras,
+            IBinder activityToken, boolean focused) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(requestType);
         data.writeStrongBinder(receiver.asBinder());
+        data.writeBundle(receiverExtras);
         data.writeStrongBinder(activityToken);
+        data.writeInt(focused ? 1 : 0);
         mRemote.transact(REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean res = reply.readInt() != 0;
@@ -6380,11 +6387,12 @@
     }
 
     @Override
-    public void startLockTaskModeOnCurrent() throws RemoteException {
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(START_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
+        data.writeInt(taskId);
+        mRemote.transact(START_SYSTEM_LOCK_TASK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -6402,11 +6410,11 @@
     }
 
     @Override
-    public void stopLockTaskModeOnCurrent() throws RemoteException {
+    public void stopSystemLockTaskMode() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(STOP_LOCK_TASK_BY_CURRENT_TRANSACTION, data, reply, 0);
+        mRemote.transact(STOP_SYSTEM_LOCK_TASK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
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/Fragment.java b/core/java/android/app/Fragment.java
index 2a04c39..29f594f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -23,6 +23,7 @@
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -1116,6 +1117,20 @@
     }
 
     /**
+     * Call {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int,
+     * Bundle)} from the fragment's containing Activity.
+     */
+    public void startIntentSenderForResult(IntentSender intent, int requestCode,
+            @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+            Bundle options) throws IntentSender.SendIntentException {
+        if (mHost == null) {
+            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
+        }
+        mHost.onStartIntentSenderFromFragment(this, intent, requestCode, fillInIntent, flagsMask,
+                flagsValues, extraFlags, options);
+    }
+
+    /**
      * Receive the result from a previous call to
      * {@link #startActivityForResult(Intent, int)}.  This follows the
      * related Activity API as described there in
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 13517e6..cf6b114 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.ArrayMap;
@@ -131,6 +132,21 @@
     }
 
     /**
+     * Starts a new {@link IntentSender} from the given fragment.
+     * See {@link Activity#startIntentSender(IntentSender, Intent, int, int, int, Bundle)}.
+     */
+    public void onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent,
+            int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
+            int extraFlags, Bundle options) throws IntentSender.SendIntentException {
+        if (requestCode != -1) {
+            throw new IllegalStateException(
+                    "Starting intent sender with a requestCode requires a FragmentActivity host");
+        }
+        mContext.startIntentSender(intent, fillInIntent, flagsMask, flagsValues, extraFlags,
+                options);
+    }
+
+    /**
      * Requests permissions from the given fragment.
      * See {@link Activity#requestPermissions(String[], int)}
      */
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 8ee6fd0..1a4e98c 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -523,7 +523,8 @@
     public Bundle getAssistContextExtras(int requestType) throws RemoteException;
 
     public boolean requestAssistContextExtras(int requestType, IResultReceiver receiver,
-            IBinder activityToken) throws RemoteException;
+            Bundle receiverExtras,
+            IBinder activityToken, boolean focused) throws RemoteException;
 
     public void reportAssistContextExtras(IBinder token, Bundle extras,
             AssistStructure structure, AssistContent content, Uri referrer) throws RemoteException;
@@ -554,7 +555,7 @@
 
     public int getActivityDisplayId(IBinder activityToken) throws RemoteException;
 
-    public void startLockTaskModeOnCurrent() throws RemoteException;
+    public void startSystemLockTaskMode(int taskId) throws RemoteException;
 
     public void startLockTaskMode(int taskId) throws RemoteException;
 
@@ -562,7 +563,7 @@
 
     public void stopLockTaskMode() throws RemoteException;
 
-    public void stopLockTaskModeOnCurrent() throws RemoteException;
+    public void stopSystemLockTaskMode() throws RemoteException;
 
     public boolean isInLockTaskMode() throws RemoteException;
 
@@ -948,8 +949,8 @@
     int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
     int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
     int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220;
-    int START_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+221;
-    int STOP_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
+    int START_SYSTEM_LOCK_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+221;
+    int STOP_SYSTEM_LOCK_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
     int FINISH_VOICE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+223;
     int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
     int REQUEST_VISIBLE_BEHIND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+225;
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index f0e35c9..f203f46 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -318,7 +318,7 @@
             if (mStarted) {
                 if (mReportNextStart) {
                     mReportNextStart = false;
-                    if (mHaveData) {
+                    if (mHaveData && !mRetaining) {
                         callOnLoadFinished(mLoader, mData);
                     }
                 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4bf1aa3..400a313 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -25,10 +25,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 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 +46,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 +592,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
@@ -777,6 +781,16 @@
     /**
      * {@link #extras} key: this is the remote input history, as supplied to
      * {@link Builder#setRemoteInputHistory(CharSequence[])}.
+     *
+     * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
+     * with the most recent inputs that have been sent through a {@link RemoteInput} of this
+     * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
+     * notifications once the other party has responded).
+     *
+     * The extra with this key is of type CharSequence[] and contains the most recent entry at
+     * the 0 index, the second most recent at the 1 index, etc.
+     *
+     * @see Builder#setRemoteInputHistory(CharSequence[])
      */
     public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
 
@@ -958,6 +972,12 @@
      */
     public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
 
+    /**
+     * @SystemApi
+     * @hide
+     */
+    public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
+
     private Icon mSmallIcon;
     private Icon mLargeIcon;
 
@@ -1419,7 +1439,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 +1452,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;
             }
         }
@@ -1955,9 +1975,16 @@
      * @hide
      */
     public static void addFieldsFromContext(Context context, Notification notification) {
-        notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
-                context.getApplicationInfo());
-        notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
+        addFieldsFromContext(context.getApplicationInfo(), context.getUserId(), notification);
+    }
+
+    /**
+     * @hide
+     */
+    public static void addFieldsFromContext(ApplicationInfo ai, int userId,
+            Notification notification) {
+        notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
+        notification.extras.putInt(EXTRA_ORIGINATING_USERID, userId);
     }
 
     @Override
@@ -2220,7 +2247,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);
@@ -3119,6 +3147,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);
@@ -3127,17 +3167,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);
             }
 
@@ -3249,14 +3287,38 @@
             }
         }
 
-        private void bindHeaderAppName(RemoteViews contentView) {
-            CharSequence appName = mContext.getPackageManager()
-                    .getApplicationLabel(mContext.getApplicationInfo());
-
-            if (TextUtils.isEmpty(appName)) {
-                return;
+        private String loadHeaderAppName() {
+            CharSequence name = null;
+            final PackageManager pm = mContext.getPackageManager();
+            if (mN.extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
+                // only system packages which lump together a bunch of unrelated stuff
+                // may substitute a different name to make the purpose of the
+                // notification more clear. the correct package label should always
+                // be accessible via SystemUI.
+                final String pkg = mContext.getPackageName();
+                final String subName = mN.extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
+                if (PackageManager.PERMISSION_GRANTED == pm.checkPermission(
+                        android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg)) {
+                    name = subName;
+                } else {
+                    Log.w(TAG, "warning: pkg "
+                            + pkg + " attempting to substitute app name '" + subName
+                            + "' without holding perm "
+                            + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
+                }
             }
-            contentView.setTextViewText(R.id.app_name_text, appName);
+            if (TextUtils.isEmpty(name)) {
+                name = pm.getApplicationLabel(mContext.getApplicationInfo());
+            }
+            if (TextUtils.isEmpty(name)) {
+                // still nothing?
+                return null;
+            }
+
+            return String.valueOf(name);
+        }
+        private void bindHeaderAppName(RemoteViews contentView) {
+            contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
             contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
         }
 
@@ -3288,7 +3350,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);
 
@@ -3742,6 +3813,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;
         }
@@ -4368,13 +4443,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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bdc4404..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,
@@ -589,9 +590,7 @@
                 new CachedServiceFetcher<LauncherApps>() {
             @Override
             public LauncherApps createService(ContextImpl ctx) {
-                IBinder b = ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE);
-                ILauncherApps service = ILauncherApps.Stub.asInterface(b);
-                return new LauncherApps(ctx, service);
+                return new LauncherApps(ctx);
             }});
 
         registerService(Context.RESTRICTIONS_SERVICE, RestrictionsManager.class,
@@ -758,8 +757,7 @@
                 new CachedServiceFetcher<ShortcutManager>() {
             @Override
             public ShortcutManager createService(ContextImpl ctx) {
-                IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
-                return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
+                return new ShortcutManager(ctx);
             }});
 
         registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3f9629c..269089e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2216,7 +2216,9 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
      */
     public boolean resetPassword(String password, int flags) {
-        throwIfParentInstance("resetPassword");
+        if (mParentInstance) {
+            throw new SecurityException("Reset password does not work across profiles.");
+        }
         if (mService != null) {
             try {
                 return mService.resetPassword(password, flags);
@@ -2353,7 +2355,6 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
      */
     public void wipeData(int flags) {
-        throwIfParentInstance("wipeData");
         if (mService != null) {
             try {
                 mService.wipeData(flags);
@@ -2387,7 +2388,6 @@
      */
     public ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
             List<String> exclusionList ) {
-        throwIfParentInstance("setGlobalProxy");
         if (proxySpec == null) {
             throw new NullPointerException();
         }
@@ -2453,7 +2453,6 @@
      */
     public void setRecommendedGlobalProxy(@NonNull ComponentName admin, @Nullable ProxyInfo
             proxyInfo) {
-        throwIfParentInstance("setRecommendedGlobalProxy");
         if (mService != null) {
             try {
                 mService.setRecommendedGlobalProxy(admin, proxyInfo);
@@ -2604,7 +2603,6 @@
      *             {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE}
      */
     public int setStorageEncryption(@NonNull ComponentName admin, boolean encrypt) {
-        throwIfParentInstance("setStorageEncryption");
         if (mService != null) {
             try {
                 return mService.setStorageEncryption(admin, encrypt);
@@ -2625,7 +2623,6 @@
      * @return true if the admin(s) are requesting encryption, false if not.
      */
     public boolean getStorageEncryption(@Nullable ComponentName admin) {
-        throwIfParentInstance("getStorageEncryption");
         if (mService != null) {
             try {
                 return mService.getStorageEncryption(admin, myUserId());
@@ -2721,7 +2718,6 @@
      *         owner.
      */
     public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
-        throwIfParentInstance("installCaCert");
         if (mService != null) {
             try {
                 return mService.installCaCert(admin, certBuffer);
@@ -2742,7 +2738,6 @@
      *         owner.
      */
     public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
-        throwIfParentInstance("uninstallCaCert");
         if (mService != null) {
             try {
                 final String alias = getCaCertAlias(certBuffer);
@@ -2768,7 +2763,6 @@
      */
     public List<byte[]> getInstalledCaCerts(@Nullable ComponentName admin) {
         List<byte[]> certs = new ArrayList<byte[]>();
-        throwIfParentInstance("getInstalledCaCerts");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2797,7 +2791,6 @@
      *         owner.
      */
     public void uninstallAllUserCaCerts(@Nullable ComponentName admin) {
-        throwIfParentInstance("uninstallAllUserCaCerts");
         if (mService != null) {
             try {
                 mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
@@ -2818,7 +2811,6 @@
      *         owner.
      */
     public boolean hasCaCertInstalled(@Nullable ComponentName admin, byte[] certBuffer) {
-        throwIfParentInstance("hasCaCertInstalled");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2887,7 +2879,6 @@
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
-        throwIfParentInstance("installKeyPair");
         try {
             final byte[] pemCert = Credentials.convertToPem(certs[0]);
             byte[] pemChain = null;
@@ -2920,7 +2911,6 @@
      *         owner.
      */
     public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
-        throwIfParentInstance("removeKeyPair");
         try {
             return mService.removeKeyPair(admin, alias);
         } catch (RemoteException e) {
@@ -2961,7 +2951,6 @@
      */
     public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
             installerPackage) throws SecurityException {
-        throwIfParentInstance("setCertInstallerPackage");
         if (mService != null) {
             try {
                 mService.setCertInstallerPackage(admin, installerPackage);
@@ -2981,7 +2970,6 @@
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
     public String getCertInstallerPackage(@NonNull ComponentName admin) throws SecurityException {
-        throwIfParentInstance("getCertInstallerPackage");
         if (mService != null) {
             try {
                 return mService.getCertInstallerPackage(admin);
@@ -3012,7 +3000,6 @@
      */
     public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
             throws NameNotFoundException, UnsupportedOperationException {
-        throwIfParentInstance("setAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
@@ -3034,7 +3021,6 @@
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
     public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
-        throwIfParentInstance("getAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 return mService.getAlwaysOnVpnPackage(admin);
@@ -3062,7 +3048,6 @@
      *             {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
      */
     public void setCameraDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setCameraDisabled");
         if (mService != null) {
             try {
                 mService.setCameraDisabled(admin, disabled);
@@ -3079,7 +3064,6 @@
      * have disabled the camera
      */
     public boolean getCameraDisabled(@Nullable ComponentName admin) {
-        throwIfParentInstance("getCameraDisabled");
         return getCameraDisabled(admin, myUserId());
     }
 
@@ -3109,7 +3093,6 @@
      *             than the one managed by the device owner.
      */
     public boolean requestBugreport(@NonNull ComponentName admin) {
-        throwIfParentInstance("requestBugreport");
         if (mService != null) {
             try {
                 return mService.requestBugreport(admin);
@@ -3148,7 +3131,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setScreenCaptureDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setScreenCaptureDisabled");
         if (mService != null) {
             try {
                 mService.setScreenCaptureDisabled(admin, disabled);
@@ -3165,7 +3147,6 @@
      * have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
-        throwIfParentInstance("getScreenCaptureDisabled");
         return getScreenCaptureDisabled(admin, myUserId());
     }
 
@@ -3195,7 +3176,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setAutoTimeRequired(@NonNull ComponentName admin, boolean required) {
-        throwIfParentInstance("setAutoTimeRequired");
         if (mService != null) {
             try {
                 mService.setAutoTimeRequired(admin, required);
@@ -3209,7 +3189,6 @@
      * @return true if auto time is required.
      */
     public boolean getAutoTimeRequired() {
-        throwIfParentInstance("getAutoTimeRequired");
         if (mService != null) {
             try {
                 return mService.getAutoTimeRequired();
@@ -3236,7 +3215,6 @@
      */
     public void setForceEphemeralUsers(
             @NonNull ComponentName admin, boolean forceEphemeralUsers) {
-        throwIfParentInstance("setForceEphemeralUsers");
         if (mService != null) {
             try {
                 mService.setForceEphemeralUsers(admin, forceEphemeralUsers);
@@ -3252,7 +3230,6 @@
      * @hide
      */
     public boolean getForceEphemeralUsers(@NonNull ComponentName admin) {
-        throwIfParentInstance("getForceEphemeralUsers");
         if (mService != null) {
             try {
                 return mService.getForceEphemeralUsers(admin);
@@ -3540,7 +3517,6 @@
      * @return whether or not the package is registered as the device owner app.
      */
     public boolean isDeviceOwnerApp(String packageName) {
-        throwIfParentInstance("isDeviceOwnerApp");
         return isDeviceOwnerAppOnCallingUser(packageName);
     }
 
@@ -3638,7 +3614,6 @@
      *             does not own the current device owner component.
      */
     public void clearDeviceOwnerApp(String packageName) {
-        throwIfParentInstance("clearDeviceOwnerApp");
         if (mService != null) {
             try {
                 mService.clearDeviceOwner(packageName);
@@ -3756,7 +3731,6 @@
      * @throws SecurityException if {@code admin} is not an active profile owner.
      */
     public void clearProfileOwner(@NonNull ComponentName admin) {
-        throwIfParentInstance("clearProfileOwner");
         if (mService != null) {
             try {
                 mService.clearProfileOwner(admin);
@@ -3830,7 +3804,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, CharSequence info) {
-        throwIfParentInstance("setDeviceOwnerLockScreenInfo");
         if (mService != null) {
             try {
                 mService.setDeviceOwnerLockScreenInfo(admin, info);
@@ -3844,7 +3817,6 @@
      * @return The device owner information. If it is not set returns {@code null}.
      */
     public CharSequence getDeviceOwnerLockScreenInfo() {
-        throwIfParentInstance("getDeviceOwnerLockScreenInfo");
         if (mService != null) {
             try {
                 return mService.getDeviceOwnerLockScreenInfo();
@@ -3876,7 +3848,6 @@
      */
     public String[] setPackagesSuspended(@NonNull ComponentName admin, String[] packageNames,
             boolean suspended) {
-        throwIfParentInstance("setPackagesSuspended");
         if (mService != null) {
             try {
                 return mService.setPackagesSuspended(admin, packageNames, suspended);
@@ -3899,7 +3870,6 @@
      */
     public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
             throws NameNotFoundException {
-        throwIfParentInstance("isPackageSuspended");
         if (mService != null) {
             try {
                 return mService.isPackageSuspended(admin, packageName);
@@ -3921,7 +3891,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setProfileEnabled(@NonNull ComponentName admin) {
-        throwIfParentInstance("setProfileEnabled");
         if (mService != null) {
             try {
                 mService.setProfileEnabled(admin);
@@ -3943,7 +3912,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setProfileName(@NonNull ComponentName admin, String profileName) {
-        throwIfParentInstance("setProfileName");
         if (mService != null) {
             try {
                 mService.setProfileName(admin, profileName);
@@ -3962,7 +3930,6 @@
      * @return Whether or not the package is registered as the profile owner.
      */
     public boolean isProfileOwnerApp(String packageName) {
-        throwIfParentInstance("isProfileOwnerApp");
         if (mService != null) {
             try {
                 ComponentName profileOwner = mService.getProfileOwner(myUserId());
@@ -4057,7 +4024,6 @@
      */
     public void addPersistentPreferredActivity(@NonNull ComponentName admin, IntentFilter filter,
             @NonNull ComponentName activity) {
-        throwIfParentInstance("addPersistentPreferredActivity");
         if (mService != null) {
             try {
                 mService.addPersistentPreferredActivity(admin, filter, activity);
@@ -4080,7 +4046,6 @@
      */
     public void clearPackagePersistentPreferredActivities(@NonNull ComponentName admin,
             String packageName) {
-        throwIfParentInstance("clearPackagePersistentPreferredActivities");
         if (mService != null) {
             try {
                 mService.clearPackagePersistentPreferredActivities(admin, packageName);
@@ -4109,7 +4074,6 @@
      */
     public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
             @Nullable String packageName) throws NameNotFoundException {
-        throwIfParentInstance("setApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 if (!mService.setApplicationRestrictionsManagingPackage(admin, packageName)) {
@@ -4131,7 +4095,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
-        throwIfParentInstance("getApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictionsManagingPackage(admin);
@@ -4151,7 +4114,6 @@
      * that method.
      */
     public boolean isCallerApplicationRestrictionsManagingPackage() {
-        throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.isCallerApplicationRestrictionsManagingPackage();
@@ -4197,7 +4159,6 @@
      */
     public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
-        throwIfParentInstance("setApplicationRestrictions");
         if (mService != null) {
             try {
                 mService.setApplicationRestrictions(admin, packageName, settings);
@@ -4296,7 +4257,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setCrossProfileCallerIdDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileCallerIdDisabled(admin, disabled);
@@ -4317,7 +4277,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) {
-        throwIfParentInstance("getCrossProfileCallerIdDisabled");
         if (mService != null) {
             try {
                 return mService.getCrossProfileCallerIdDisabled(admin);
@@ -4358,7 +4317,6 @@
      */
     public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
             boolean disabled) {
-        throwIfParentInstance("setCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileContactsSearchDisabled(admin, disabled);
@@ -4379,7 +4337,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
-        throwIfParentInstance("getCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 return mService.getCrossProfileContactsSearchDisabled(admin);
@@ -4450,7 +4407,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setBluetoothContactSharingDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setBluetoothContactSharingDisabled");
         if (mService != null) {
             try {
                 mService.setBluetoothContactSharingDisabled(admin, disabled);
@@ -4473,7 +4429,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getBluetoothContactSharingDisabled(@NonNull ComponentName admin) {
-        throwIfParentInstance("getBluetoothContactSharingDisabled");
         if (mService != null) {
             try {
                 return mService.getBluetoothContactSharingDisabled(admin);
@@ -4517,7 +4472,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addCrossProfileIntentFilter(@NonNull ComponentName admin, IntentFilter filter, int flags) {
-        throwIfParentInstance("addCrossProfileIntentFilter");
         if (mService != null) {
             try {
                 mService.addCrossProfileIntentFilter(admin, filter, flags);
@@ -4536,7 +4490,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearCrossProfileIntentFilters(@NonNull ComponentName admin) {
-        throwIfParentInstance("clearCrossProfileIntentFilters");
         if (mService != null) {
             try {
                 mService.clearCrossProfileIntentFilters(admin);
@@ -4566,7 +4519,6 @@
      */
     public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin,
             List<String> packageNames) {
-        throwIfParentInstance("setPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.setPermittedAccessibilityServices(admin, packageNames);
@@ -4588,7 +4540,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public List<String> getPermittedAccessibilityServices(@NonNull ComponentName admin) {
-        throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.getPermittedAccessibilityServices(admin);
@@ -4666,7 +4617,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean setPermittedInputMethods(@NonNull ComponentName admin, List<String> packageNames) {
-        throwIfParentInstance("setPermittedInputMethods");
         if (mService != null) {
             try {
                 return mService.setPermittedInputMethods(admin, packageNames);
@@ -4689,7 +4639,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
-        throwIfParentInstance("getPermittedInputMethods");
         if (mService != null) {
             try {
                 return mService.getPermittedInputMethods(admin);
@@ -4885,7 +4834,6 @@
     public UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name,
             @NonNull ComponentName profileOwner, @Nullable PersistableBundle adminExtras,
             int flags) {
-        throwIfParentInstance("createAndManageUser");
         try {
             return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
         } catch (RemoteException re) {
@@ -4903,7 +4851,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) {
-        throwIfParentInstance("removeUser");
         try {
             return mService.removeUser(admin, userHandle);
         } catch (RemoteException re) {
@@ -4921,7 +4868,6 @@
      * @see Intent#ACTION_USER_FOREGROUND
      */
     public boolean switchUser(@NonNull ComponentName admin, @Nullable UserHandle userHandle) {
-        throwIfParentInstance("switchUser");
         try {
             return mService.switchUser(admin, userHandle);
         } catch (RemoteException re) {
@@ -4947,7 +4893,6 @@
      * @see {@link #setApplicationRestrictionsManagingPackage}
      */
     public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
-        throwIfParentInstance("getApplicationRestrictions");
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictions(admin, packageName);
@@ -4970,7 +4915,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addUserRestriction(@NonNull ComponentName admin, String key) {
-        throwIfParentInstance("addUserRestriction");
         if (mService != null) {
             try {
                 mService.setUserRestriction(admin, key, true);
@@ -4992,7 +4936,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearUserRestriction(@NonNull ComponentName admin, String key) {
-        throwIfParentInstance("clearUserRestriction");
         if (mService != null) {
             try {
                 mService.setUserRestriction(admin, key, false);
@@ -5014,7 +4957,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public Bundle getUserRestrictions(@NonNull ComponentName admin) {
-        throwIfParentInstance("getUserRestrictions");
         Bundle ret = null;
         if (mService != null) {
             try {
@@ -5059,7 +5001,6 @@
      */
     public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
             boolean hidden) {
-        throwIfParentInstance("setApplicationHidden");
         if (mService != null) {
             try {
                 return mService.setApplicationHidden(admin, packageName, hidden);
@@ -5079,7 +5020,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
-        throwIfParentInstance("isApplicationHidden");
         if (mService != null) {
             try {
                 return mService.isApplicationHidden(admin, packageName);
@@ -5099,7 +5039,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void enableSystemApp(@NonNull ComponentName admin, String packageName) {
-        throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
                 mService.enableSystemApp(admin, packageName);
@@ -5120,7 +5059,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public int enableSystemApp(@NonNull ComponentName admin, Intent intent) {
-        throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
                 return mService.enableSystemAppWithIntent(admin, intent);
@@ -5153,7 +5091,6 @@
      */
     public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
             boolean disabled) {
-        throwIfParentInstance("setAccountManagementDisabled");
         if (mService != null) {
             try {
                 mService.setAccountManagementDisabled(admin, accountType, disabled);
@@ -5174,7 +5111,6 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
-        throwIfParentInstance("getAccountTypesWithManagementDisabled");
         return getAccountTypesWithManagementDisabledAsUser(myUserId());
     }
 
@@ -5212,7 +5148,6 @@
      */
     public void setLockTaskPackages(@NonNull ComponentName admin, String[] packages)
             throws SecurityException {
-        throwIfParentInstance("setLockTaskPackages");
         if (mService != null) {
             try {
                 mService.setLockTaskPackages(admin, packages);
@@ -5229,7 +5164,6 @@
      * @hide
      */
     public String[] getLockTaskPackages(@NonNull ComponentName admin) {
-        throwIfParentInstance("getLockTaskPackages");
         if (mService != null) {
             try {
                 return mService.getLockTaskPackages(admin);
@@ -5246,7 +5180,6 @@
      * @param pkg The package to check
      */
     public boolean isLockTaskPermitted(String pkg) {
-        throwIfParentInstance("isLockTaskPermitted");
         if (mService != null) {
             try {
                 return mService.isLockTaskPermitted(pkg);
@@ -5295,7 +5228,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setGlobalSetting(@NonNull ComponentName admin, String setting, String value) {
-        throwIfParentInstance("setGlobalSetting");
         if (mService != null) {
             try {
                 mService.setGlobalSetting(admin, setting, value);
@@ -5328,7 +5260,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
-        throwIfParentInstance("setSecureSetting");
         if (mService != null) {
             try {
                 mService.setSecureSetting(admin, setting, value);
@@ -5352,7 +5283,6 @@
      */
     public void setRestrictionsProvider(@NonNull ComponentName admin,
             @Nullable ComponentName provider) {
-        throwIfParentInstance("setRestrictionsProvider");
         if (mService != null) {
             try {
                 mService.setRestrictionsProvider(admin, provider);
@@ -5370,7 +5300,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setMasterVolumeMuted(@NonNull ComponentName admin, boolean on) {
-        throwIfParentInstance("setMasterVolumeMuted");
         if (mService != null) {
             try {
                 mService.setMasterVolumeMuted(admin, on);
@@ -5388,7 +5317,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isMasterVolumeMuted(@NonNull ComponentName admin) {
-        throwIfParentInstance("isMasterVolumeMuted");
         if (mService != null) {
             try {
                 return mService.isMasterVolumeMuted(admin);
@@ -5409,7 +5337,6 @@
      */
     public void setUninstallBlocked(@NonNull ComponentName admin, String packageName,
             boolean uninstallBlocked) {
-        throwIfParentInstance("setUninstallBlocked");
         if (mService != null) {
             try {
                 mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
@@ -5435,7 +5362,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isUninstallBlocked(@Nullable ComponentName admin, String packageName) {
-        throwIfParentInstance("isUninstallBlocked");
         if (mService != null) {
             try {
                 return mService.isUninstallBlocked(admin, packageName);
@@ -5463,7 +5389,6 @@
      * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
      */
     public boolean addCrossProfileWidgetProvider(@NonNull ComponentName admin, String packageName) {
-        throwIfParentInstance("addCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.addCrossProfileWidgetProvider(admin, packageName);
@@ -5491,7 +5416,6 @@
      */
     public boolean removeCrossProfileWidgetProvider(
             @NonNull ComponentName admin, String packageName) {
-        throwIfParentInstance("removeCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.removeCrossProfileWidgetProvider(admin, packageName);
@@ -5513,7 +5437,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public List<String> getCrossProfileWidgetProviders(@NonNull ComponentName admin) {
-        throwIfParentInstance("getCrossProfileWidgetProviders");
         if (mService != null) {
             try {
                 List<String> providers = mService.getCrossProfileWidgetProviders(admin);
@@ -5535,7 +5458,6 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setUserIcon(@NonNull ComponentName admin, Bitmap icon) {
-        throwIfParentInstance("setUserIcon");
         try {
             mService.setUserIcon(admin, icon);
         } catch (RemoteException re) {
@@ -5555,7 +5477,6 @@
      * @see SystemUpdatePolicy
      */
     public void setSystemUpdatePolicy(@NonNull ComponentName admin, SystemUpdatePolicy policy) {
-        throwIfParentInstance("setSystemUpdatePolicy");
         if (mService != null) {
             try {
                 mService.setSystemUpdatePolicy(admin, policy);
@@ -5571,7 +5492,6 @@
      * @return The current policy object, or {@code null} if no policy is set.
      */
     public SystemUpdatePolicy getSystemUpdatePolicy() {
-        throwIfParentInstance("getSystemUpdatePolicy");
         if (mService != null) {
             try {
                 return mService.getSystemUpdatePolicy();
@@ -5597,7 +5517,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean setKeyguardDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setKeyguardDisabled");
         try {
             return mService.setKeyguardDisabled(admin, disabled);
         } catch (RemoteException re) {
@@ -5616,7 +5535,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean setStatusBarDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setStatusBarDisabled");
         try {
             return mService.setStatusBarDisabled(admin, disabled);
         } catch (RemoteException re) {
@@ -5662,7 +5580,6 @@
      * @see #setPermissionGrantState
      */
     public void setPermissionPolicy(@NonNull ComponentName admin, int policy) {
-        throwIfParentInstance("setPermissionPolicy");
         try {
             mService.setPermissionPolicy(admin, policy);
         } catch (RemoteException re) {
@@ -5677,7 +5594,6 @@
      * @return the current policy for future permission requests.
      */
     public int getPermissionPolicy(ComponentName admin) {
-        throwIfParentInstance("getPermissionPolicy");
         try {
             return mService.getPermissionPolicy(admin);
         } catch (RemoteException re) {
@@ -5714,7 +5630,6 @@
      */
     public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission, int grantState) {
-        throwIfParentInstance("setPermissionGrantState");
         try {
             return mService.setPermissionGrantState(admin, packageName, permission, grantState);
         } catch (RemoteException re) {
@@ -5743,7 +5658,6 @@
      */
     public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission) {
-        throwIfParentInstance("getPermissionGrantState");
         try {
             return mService.getPermissionGrantState(admin, packageName, permission);
         } catch (RemoteException re) {
@@ -5759,7 +5673,6 @@
      * @throws IllegalArgumentException if the supplied action is not valid.
      */
     public boolean isProvisioningAllowed(String action) {
-        throwIfParentInstance("isProvisioningAllowed");
         try {
             return mService.isProvisioningAllowed(action);
         } catch (RemoteException re) {
@@ -5775,7 +5688,6 @@
      * @return if this user is a managed profile of another user.
      */
     public boolean isManagedProfile(@NonNull ComponentName admin) {
-        throwIfParentInstance("isManagedProfile");
         try {
             return mService.isManagedProfile(admin);
         } catch (RemoteException re) {
@@ -5809,7 +5721,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public String getWifiMacAddress(@NonNull ComponentName admin) {
-        throwIfParentInstance("getWifiMacAddress");
         try {
             return mService.getWifiMacAddress(admin);
         } catch (RemoteException re) {
@@ -5826,7 +5737,6 @@
      * @see TelephonyManager#CALL_STATE_IDLE
      */
     public void reboot(@NonNull ComponentName admin) {
-        throwIfParentInstance("reboot");
         try {
             mService.reboot(admin);
         } catch (RemoteException re) {
@@ -5852,8 +5762,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public void setShortSupportMessage(@NonNull ComponentName admin,
-            @Nullable String message) {
-        throwIfParentInstance("setShortSupportMessage");
+            @Nullable CharSequence message) {
         if (mService != null) {
             try {
                 mService.setShortSupportMessage(admin, message);
@@ -5867,12 +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) {
-        throwIfParentInstance("getShortSupportMessage");
+    public CharSequence getShortSupportMessage(@NonNull ComponentName admin) {
         if (mService != null) {
             try {
                 return mService.getShortSupportMessage(admin);
@@ -5898,8 +5806,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public void setLongSupportMessage(@NonNull ComponentName admin,
-            @Nullable String message) {
-        throwIfParentInstance("setLongSupportMessage");
+            @Nullable CharSequence message) {
         if (mService != null) {
             try {
                 mService.setLongSupportMessage(admin, message);
@@ -5913,12 +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) {
-        throwIfParentInstance("getLongSupportMessage");
+    public CharSequence getLongSupportMessage(@NonNull ComponentName admin) {
         if (mService != null) {
             try {
                 return mService.getLongSupportMessage(admin);
@@ -5934,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);
@@ -5955,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);
@@ -6015,7 +5922,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
-        throwIfParentInstance("getParentProfileInstance");
         try {
             if (!mService.isManagedProfile(admin)) {
                 throw new SecurityException("The current user does not have a parent profile.");
@@ -6042,7 +5948,6 @@
      * @see #retrieveSecurityLogs
      */
     public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
-        throwIfParentInstance("setSecurityLoggingEnabled");
         try {
             mService.setSecurityLoggingEnabled(admin, enabled);
         } catch (RemoteException re) {
@@ -6061,7 +5966,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean isSecurityLoggingEnabled(@NonNull ComponentName admin) {
-        throwIfParentInstance("isSecurityLoggingEnabled");
         try {
             return mService.isSecurityLoggingEnabled(admin);
         } catch (RemoteException re) {
@@ -6085,7 +5989,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
-        throwIfParentInstance("retrieveSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrieveSecurityLogs(admin);
             if (list != null) {
@@ -6131,7 +6034,6 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public List<SecurityEvent> retrievePreRebootSecurityLogs(@NonNull ComponentName admin) {
-        throwIfParentInstance("retrievePreRebootSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrievePreRebootSecurityLogs(admin);
             return list.getList();
@@ -6149,12 +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) {
-        throwIfParentInstance("setOrganizationColor");
         try {
+            // always enforce alpha channel to have 100% opacity
+            color |= 0xFF000000;
             mService.setOrganizationColor(admin, color);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -6166,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,
@@ -6174,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();
@@ -6185,11 +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) {
-        throwIfParentInstance("getOrganizationColor");
+    public @ColorInt int getOrganizationColor(@NonNull ComponentName admin) {
         try {
             return mService.getOrganizationColor(admin);
         } catch (RemoteException re) {
@@ -6202,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) {
@@ -6224,8 +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) {
-        throwIfParentInstance("setOrganizationName");
+    public void setOrganizationName(@NonNull ComponentName admin, @Nullable CharSequence title) {
         try {
             mService.setOrganizationName(admin, title);
         } catch (RemoteException re) {
@@ -6241,8 +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) {
-        throwIfParentInstance("getOrganizationName");
+    public CharSequence getOrganizationName(@NonNull ComponentName admin) {
         try {
             return mService.getOrganizationName(admin);
         } catch (RemoteException re) {
@@ -6258,7 +6160,7 @@
      *
      * @hide
      */
-    public String getOrganizationNameForUser(int userHandle) {
+    public CharSequence getOrganizationNameForUser(int userHandle) {
         try {
             return mService.getOrganizationNameForUser(userHandle);
         } catch (RemoteException re) {
@@ -6368,10 +6270,4 @@
             throw re.rethrowFromSystemServer();
         }
     }
-
-    private void throwIfParentInstance(String functionName) {
-        if (mParentInstance) {
-            throw new SecurityException(functionName + " cannot be called on the parent instance");
-        }
-    }
 }
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 6b3d4f1..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,
@@ -47,17 +48,16 @@
     ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user);
 
     ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
-            in ComponentName componentName, int flags, in UserHandle user);
-    ParceledListSlice getShortcutInfo(String callingPackage, String packageName, in List<String> ids,
-            in UserHandle user);
+            in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
     void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
             in UserHandle user);
     boolean startShortcut(String callingPackage, String packageName, String id,
-            in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+            in Rect sourceBounds, in Bundle startActivityOptions, int userId);
 
-    int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user);
-    ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut,
-            in UserHandle user);
+    int getShortcutIconResId(String callingPackage, String packageName, String id,
+            int userId);
+    ParcelFileDescriptor getShortcutIconFd(String callingPackage, String packageName, String id,
+            int userId);
 
     boolean hasShortcutHostPermission(String callingPackage);
 }
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 d865f34..824722d 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +31,7 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
@@ -37,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;
 
@@ -205,6 +208,9 @@
         String mPackage;
 
         @Nullable
+        List<String> mShortcutIds;
+
+        @Nullable
         ComponentName mActivity;
 
         @QueryFlags
@@ -229,6 +235,14 @@
         }
 
         /**
+         * If non-null, return only the specified shortcuts by ID.  When setting this field,
+         * a packange name must also be set with {@link #setPackage}.
+         */
+        public void setShortcutIds(@Nullable List<String> shortcutIds) {
+            mShortcutIds = shortcutIds;
+        }
+
+        /**
          * If non-null, returns only shortcuts associated with the activity.
          */
         public void setActivity(@Nullable ComponentName activity) {
@@ -250,6 +264,13 @@
         mPm = context.getPackageManager();
     }
 
+    /** @hide */
+    @TestApi
+    public LauncherApps(Context context) {
+        this(context, ILauncherApps.Stub.asInterface(
+                ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
+    }
+
     /**
      * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
      * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
@@ -271,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());
@@ -281,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.
@@ -295,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) {
@@ -369,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) {
@@ -429,7 +447,8 @@
             @NonNull UserHandle user) {
         try {
             return mService.getShortcuts(mContext.getPackageName(),
-                    query.mChangedSince, query.mPackage, query.mActivity, query.mQueryFlags, user)
+                    query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
+                    query.mQueryFlags, user)
                     .getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -437,29 +456,18 @@
     }
 
     /**
-     * Returns {@link ShortcutInfo}s with the given IDs from a package.
-     *
-     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
-     * #hasShortcutHostPermission()}.
-     *
-     * @param packageName The target package.
-     * @param ids IDs of the shortcuts to retrieve.
-     * @param user The UserHandle of the profile.
-     *
-     * @return list of {@link ShortcutInfo} associated with the package.
+     * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
      */
     @Nullable
     public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
             @NonNull List<String> ids, @NonNull UserHandle user) {
-        try {
-            return mService.getShortcutInfo(mContext.getPackageName(), packageName, ids, user)
-                    .getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setPackage(packageName);
+        q.setShortcutIds(ids);
+        q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+        return getShortcuts(q, user);
     }
 
-
     /**
      * Pin shortcuts on a package.
      *
@@ -483,21 +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.
-     * @param user The UserHandle of the profile.
+     * @hide kept for testing.
      */
-    public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
-        try {
-            return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+    public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
+        return shortcut.getIconResourceId();
+    }
+
+    /**
+     * @hide kept for testing.
+     */
+    public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
+            @NonNull UserHandle user) {
+        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);
+
+        return shortcuts.size() > 0 ? shortcuts.get(0).getIconResourceId() : 0;
     }
 
     /**
@@ -508,12 +519,34 @@
      * #hasShortcutHostPermission()}.
      *
      * @param shortcut The target shortcut.
+     */
+    public ParcelFileDescriptor getShortcutIconFd(
+            @NonNull ShortcutInfo shortcut) {
+        return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(),
+                shortcut.getUserId());
+    }
+
+    /**
+     * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
+     * (i.e. when {@link ShortcutInfo#hasIconFile()} 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.
      */
     public ParcelFileDescriptor getShortcutIconFd(
-            @NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+            @NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
+        return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
+    }
+
+    private ParcelFileDescriptor getShortcutIconFd(
+            @NonNull String packageName, @NonNull String shortcutId, int userId) {
         try {
-            return mService.getShortcutIconFd(mContext.getPackageName(), shortcut, user);
+            return mService.getShortcutIconFd(mContext.getPackageName(),
+                    packageName, shortcutId, userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -536,9 +569,35 @@
     public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
             @NonNull UserHandle user) {
+        return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
+                user.getIdentifier());
+    }
+
+    /**
+     * Launches a shortcut.
+     *
+     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+     * #hasShortcutHostPermission()}.
+     *
+     * @param shortcut The target shortcut.
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon.
+     * @param startActivityOptions Options to pass to startActivity.
+     * @return {@code false} when the shortcut is no longer valid (e.g. the creator application
+     *   has been uninstalled). {@code true} when the shortcut is still valid.
+     */
+    public boolean startShortcut(@NonNull ShortcutInfo shortcut,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+        return startShortcut(shortcut.getPackageName(), shortcut.getId(),
+                sourceBounds, startActivityOptions,
+                shortcut.getUserId());
+    }
+
+    private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
+            int userId) {
         try {
             return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
-                    sourceBounds, startActivityOptions, user);
+                    sourceBounds, startActivityOptions, userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
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/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 8236f55..995d2cc 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -87,6 +87,11 @@
     /**
      * Test if the given component is considered installed, enabled and a match
      * for the given flags.
+     *
+     * <p>
+     * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and
+     * {@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
+     * </p>
      */
     public boolean isMatch(ComponentInfo componentInfo, int flags) {
         if (!isInstalled(flags)) return false;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 1812575a..a900015 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -18,6 +18,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -33,6 +34,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 // TODO Enhance javadoc
 /**
@@ -106,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
@@ -123,6 +131,9 @@
     @Nullable
     private String mText;
 
+    @NonNull
+    private List<String> mCategories;
+
     /**
      * Intent *with extras removed*.
      */
@@ -153,7 +164,11 @@
     @Nullable
     private String mBitmapPath;
 
+    private final int mUserId;
+
     private ShortcutInfo(Builder b) {
+        mUserId = b.mContext.getUserId();
+
         mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
 
         // Note we can't do other null checks here because SM.updateShortcuts() takes partial
@@ -163,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();
@@ -176,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.
      *
@@ -191,22 +211,26 @@
      * Copy constructor.
      */
     private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
+        mUserId = source.mUserId;
         mId = source.mId;
         mPackageName = source.mPackageName;
         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;
@@ -238,6 +262,7 @@
      * @hide
      */
     public void copyNonNullFieldsFrom(ShortcutInfo source) {
+        Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
         Preconditions.checkState(mId.equals(source.mId), "ID must match");
         Preconditions.checkState(mPackageName.equals(source.mPackageName),
                 "Package name must match");
@@ -255,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;
@@ -318,6 +346,8 @@
 
         private String mText;
 
+        private List<String> mCategories;
+
         private Intent mIntent;
 
         private int mWeight;
@@ -362,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, UserHandle)}
-         * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)}.
+         * 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) {
@@ -396,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}).
          */
@@ -493,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
@@ -544,6 +595,18 @@
         return mExtras;
     }
 
+    /** @hide */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * {@link UserHandle} on which the publisher created shortcuts.
+     */
+    public UserHandle getUserHandle() {
+        return UserHandle.of(mUserId);
+    }
+
     /**
      * Last time when any of the fields was updated.
      */
@@ -590,7 +653,7 @@
     /**
      * Return whether a shortcut's icon is a resource in the owning package.
      *
-     * @see LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)
+     * @see LauncherApps#getShortcutIconResId(ShortcutInfo)
      */
     public boolean hasIconResource() {
         return hasFlags(FLAG_HAS_ICON_RES);
@@ -599,7 +662,7 @@
     /**
      * Return whether a shortcut's icon is stored as a file.
      *
-     * @see LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)
+     * @see LauncherApps#getShortcutIconFd(ShortcutInfo)
      */
     public boolean hasIconFile() {
         return hasFlags(FLAG_HAS_ICON_FILE);
@@ -643,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;
     }
@@ -661,12 +726,15 @@
     private ShortcutInfo(Parcel source) {
         final ClassLoader cl = getClass().getClassLoader();
 
+        mUserId = source.readInt();
         mId = source.readString();
         mPackageName = source.readString();
         mActivityComponent = source.readParcelable(cl);
         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();
@@ -679,12 +747,14 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUserId);
         dest.writeString(mId);
         dest.writeString(mPackageName);
         dest.writeParcelable(mActivityComponent, flags);
         dest.writeParcelable(mIcon, flags);
         dest.writeString(mTitle);
         dest.writeString(mText);
+        dest.writeStringList(mCategories);
         dest.writeParcelable(mIntent, flags);
         dest.writeParcelable(mIntentPersistableExtras, flags);
         dest.writeInt(mWeight);
@@ -749,6 +819,9 @@
         sb.append(", text=");
         sb.append(secure ? "***" : mText);
 
+        sb.append(", categories=");
+        sb.append(mCategories);
+
         sb.append(", icon=");
         sb.append(mIcon);
 
@@ -784,17 +857,20 @@
     }
 
     /** @hide */
-    public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
-            Icon icon, String title, String text, Intent intent,
+    public ShortcutInfo(
+            @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
+            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) {
+        mUserId = userId;
         mId = id;
         mPackageName = packageName;
         mActivityComponent = activityComponent;
         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 e4a98b5..75803d3 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -16,8 +16,11 @@
 package android.content.pm;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.content.Context;
+import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -31,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
@@ -58,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.
  *
@@ -97,6 +100,15 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public ShortcutManager(Context context) {
+        this(context, IShortcutService.Stub.asInterface(
+                ServiceManager.getService(Context.SHORTCUT_SERVICE)));
+    }
+
+    /**
      * Publish a list of shortcuts.  All existing dynamic shortcuts from the caller application
      * will be replaced.
      *
@@ -141,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();
         }
@@ -153,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();
         }
@@ -164,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/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index d57f2e6e..dc3d317 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -42,15 +42,10 @@
     public abstract List<ShortcutInfo>
             getShortcuts(int launcherUserId,
             @NonNull String callingPackage, long changedSince,
-            @Nullable String packageName, @Nullable ComponentName componentName,
-            @ShortcutQuery.QueryFlags int flags,
+            @Nullable String packageName, @Nullable List<String> shortcutIds,
+            @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
             int userId);
 
-    public abstract List<ShortcutInfo>
-            getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
-            @NonNull String packageName, @Nullable List<String> ids, int userId);
-
-
     public abstract boolean
             isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
             @NonNull String packageName, @NonNull String id, int userId);
@@ -65,11 +60,11 @@
     public abstract void addListener(@NonNull ShortcutChangeListener listener);
 
     public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
-            @NonNull ShortcutInfo shortcut, int userId);
+            @NonNull String packageName, @NonNull String shortcutId, int userId);
 
     public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
             @NonNull String callingPackage,
-            @NonNull ShortcutInfo shortcut, int userId);
+            @NonNull String packageName, @NonNull String shortcutId, int userId);
 
     public abstract boolean hasShortcutHostPermission(int launcherUserId,
             @NonNull String callingPackage);
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..8c73e6a 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>
@@ -3599,10 +3599,20 @@
 
     /**
      * <p>The shading map is a low-resolution floating-point map
-     * that lists the coefficients used to correct for vignetting, for each
-     * Bayer color channel of RAW image data.</p>
-     * <p>The least shaded section of the image should have a gain factor
-     * of 1; all other sections should have gains above 1.</p>
+     * that lists the coefficients used to correct for vignetting and color shading,
+     * for each Bayer color channel of RAW image data.</p>
+     * <p>The lens shading correction is defined as a full shading correction that
+     * corrects both color shading for the output non-RAW images. After the
+     * shading map is applied, the output non-RAW images will be flat-field images
+     * for flat scenes under uniform illumination.</p>
+     * <p>When there is no lens shading correction applied to RAW output images
+     * ({@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied} <code>==</code> false), this map is a full lens
+     * shading correction map; when there is some lens shading correction applied
+     * to the RAW output image ({@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied} <code>==</code> true),
+     * this map reports the remaining lens shading correction map that needs to be
+     * applied to get fully shading corrected images.</p>
+     * <p>For a full shading correction map, the least shaded section of the image
+     * should have a gain factor of 1; all other sections should have gains above 1.</p>
      * <p>When {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} = TRANSFORM_MATRIX, the map
      * must take into account the colorCorrection settings.</p>
      * <p>The shading map is for the entire active pixel array, and is not
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/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 4756b372..271ec79 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -517,7 +517,8 @@
 
         if (mService != null) try {
             mEnrollmentCallback = callback;
-            mService.enroll(mToken, token, userId, mServiceReceiver, flags);
+            mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+                    mContext.getOpPackageName());
         } catch (RemoteException e) {
             Log.w(TAG, "Remote exception in enroll: ", e);
             if (callback != null) {
diff --git a/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl b/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
index 9c13523..f40f8a3 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintDaemon.aidl
@@ -35,4 +35,6 @@
     int closeHal();
     void init(IFingerprintDaemonCallback callback);
     int postEnroll();
+    int enumerate();
+    int cancelEnumeration();
 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 43d5577..d7915e3 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -35,7 +35,7 @@
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
-            int flags);
+            int flags, String opPackageName);
 
     // Cancel enrollment in progress
     void cancelEnrollment(IBinder token);
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index c8f3439..03ac4a2 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -18,6 +18,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 /** A class describing nano apps.
  * A nano app is a piece of executable code that can be
@@ -31,10 +32,15 @@
  */
 @SystemApi
 public class NanoApp {
+    private final String TAG = "NanoApp";
+
+    private final String UNKNOWN = "Unknown";
+
     private String mPublisher;
     private String mName;
 
     private int mAppId;
+    private boolean mAppIdSet;
     private int mAppVersion;
 
     private int mNeededReadMemBytes;
@@ -45,7 +51,48 @@
     private int[] mOutputEvents;
     private byte[] mAppBinary;
 
+    /**
+     * If this version of the constructor is used, the methods
+     * {@link #setAppBinary(byte[])} and {@link #setAppId(int)} must be called
+     * prior to passing this object to any managers.
+     *
+     * @see #NanoApp(int, byte[])
+     */
     public NanoApp() {
+        this(0, null);
+        mAppIdSet = false;
+    }
+
+    /**
+     * Initialize a NanoApp with the given id and binary.
+     *
+     * While this sets defaults for other fields, users will want to provide
+     * other values for those fields in most cases.
+     *
+     * @see #setPublisher(String)
+     * @see #setName(String)
+     * @see #setAppVersion(int)
+     * @see #setNeededReadMemBytes(int)
+     * @see #setNeededWriteMemBytes(int)
+     * @see #setNeededExecMemBytes(int)
+     * @see #setNeededSensors(int[])
+     * @see #setOutputEvents(int[])
+     */
+    public NanoApp(int appId, byte[] appBinary) {
+        mPublisher = UNKNOWN;
+        mName = UNKNOWN;
+
+        mAppId = appId;
+        mAppIdSet = true;
+        mAppVersion = 0;
+
+        mNeededReadMemBytes = 0;
+        mNeededWriteMemBytes = 0;
+        mNeededExecMemBytes = 0;
+
+        mNeededSensors = new int[0];
+        mOutputEvents = new int[0];
+        mAppBinary = appBinary;
     }
 
     /**
@@ -73,6 +120,7 @@
      */
     public void setAppId(int appId) {
         mAppId = appId;
+        mAppIdSet = true;
     }
 
     /**
@@ -144,7 +192,7 @@
      * @return publisher name
      */
     public String getPublisher() {
-      return mPublisher;
+        return mPublisher;
     }
 
     /**
@@ -153,7 +201,7 @@
      * @return app name
      */
     public String getName() {
-    return mName;
+        return mName;
     }
 
     /**
@@ -162,7 +210,7 @@
      * @return identifier for this app
      */
     public int getAppId() {
-      return mAppId;
+        return mAppId;
     }
 
     /**
@@ -171,7 +219,7 @@
      * @return app version
      */
     public int getAppVersion() {
-      return mAppVersion;
+        return mAppVersion;
     }
 
     /**
@@ -180,7 +228,7 @@
      * @return readable memory needed in bytes
      */
     public int getNeededReadMemBytes() {
-      return mNeededReadMemBytes;
+        return mNeededReadMemBytes;
     }
 
     /**
@@ -189,7 +237,7 @@
      * @return writable memory needed in bytes
      */
     public int getNeededWriteMemBytes() {
-      return mNeededWriteMemBytes;
+        return mNeededWriteMemBytes;
     }
 
     /**
@@ -198,7 +246,7 @@
      * @return executable memory needed in bytes
      */
     public int getNeededExecMemBytes() {
-      return mNeededExecMemBytes;
+        return mNeededExecMemBytes;
     }
 
     /**
@@ -207,7 +255,7 @@
      * @return sensors needed
      */
     public int[] getNeededSensors() {
-      return mNeededSensors;
+        return mNeededSensors;
     }
 
     /**
@@ -216,7 +264,7 @@
      * @return generated events
      */
     public int[] getOutputEvents() {
-      return mOutputEvents;
+        return mOutputEvents;
     }
 
     /**
@@ -225,7 +273,7 @@
      * @return app binary
      */
     public byte[] getAppBinary() {
-      return mAppBinary;
+        return mAppBinary;
     }
 
     private NanoApp(Parcel in) {
@@ -256,6 +304,13 @@
     }
 
     public void writeToParcel(Parcel out, int flags) {
+        if (mAppBinary == null) {
+            throw new IllegalStateException("Must set non-null AppBinary for nanoapp " + mName);
+        }
+        if (!mAppIdSet) {
+            throw new IllegalStateException("Must set AppId for nanoapp " + mName);
+        }
+
         out.writeString(mPublisher);
         out.writeString(mName);
         out.writeInt(mAppId);
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index cc018e9..458c584 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -35,9 +35,11 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * Enrollment information about the different available keyphrases.
@@ -82,8 +84,16 @@
     public static final String EXTRA_VOICE_KEYPHRASE_LOCALE =
             "com.android.intent.extra.VOICE_KEYPHRASE_LOCALE";
 
-    private KeyphraseMetadata[] mKeyphrases;
-    private String mEnrollmentPackage;
+    /**
+     * List of available keyphrases.
+     */
+    final private KeyphraseMetadata[] mKeyphrases;
+
+    /**
+     * Map between KeyphraseMetadata and the package name of the enrollment app that provides it.
+     */
+    final private Map<KeyphraseMetadata, String> mKeyphrasePackageMap;
+
     private String mParseError;
 
     public KeyphraseEnrollmentInfo(PackageManager pm) {
@@ -94,15 +104,17 @@
                 new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
         if (ris == null || ris.isEmpty()) {
             // No application capable of enrolling for voice keyphrases is present.
-            mParseError = "No enrollment application found";
+            mParseError = "No enrollment applications found";
+            mKeyphrasePackageMap = null;
+            mKeyphrases = null;
             return;
         }
 
-        boolean found = false;
-        ApplicationInfo ai = null;
+        List<String> parseErrors = new LinkedList<String>();
+        mKeyphrasePackageMap = new HashMap<KeyphraseMetadata, String>();
         for (ResolveInfo ri : ris) {
             try {
-                ai = pm.getApplicationInfo(
+                ApplicationInfo ai = pm.getApplicationInfo(
                         ri.activityInfo.packageName, PackageManager.GET_META_DATA);
                 if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
                     // The application isn't privileged (/system/priv-app).
@@ -116,27 +128,45 @@
                     Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES");
                     continue;
                 }
-                mEnrollmentPackage = ai.packageName;
-                found = true;
-                break;
+
+                mKeyphrasePackageMap.put(
+                        getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors),
+                        ai.packageName);
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.w(TAG, "error parsing voice enrollment meta-data", e);
+                String error = "error parsing voice enrollment meta-data for "
+                        + ri.activityInfo.packageName;
+                parseErrors.add(error + ": " + e);
+                Slog.w(TAG, error, e);
             }
         }
 
-        if (!found) {
+        if (mKeyphrasePackageMap.isEmpty()) {
+            String error = "No suitable enrollment application found";
+            parseErrors.add(error);
+            Slog.w(TAG, error);
             mKeyphrases = null;
-            mParseError = "No suitable enrollment application found";
-            return;
+        } else {
+            mKeyphrases = mKeyphrasePackageMap.keySet().toArray(
+                    new KeyphraseMetadata[mKeyphrasePackageMap.size()]);
         }
 
+        if (!parseErrors.isEmpty()) {
+            mParseError = TextUtils.join("\n", parseErrors);
+        }
+    }
+
+    private KeyphraseMetadata getKeyphraseMetadataFromApplicationInfo(PackageManager pm,
+            ApplicationInfo ai, List<String> parseErrors) {
         XmlResourceParser parser = null;
+        String packageName = ai.packageName;
+        KeyphraseMetadata keyphraseMetadata = null;
         try {
             parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA);
             if (parser == null) {
-                mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for "
-                        + ai.packageName;
-                return;
+                String error = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for " + packageName;
+                parseErrors.add(error);
+                Slog.w(TAG, error);
+                return null;
             }
 
             Resources res = pm.getResourcesForApplication(ai);
@@ -149,48 +179,55 @@
 
             String nodeName = parser.getName();
             if (!"voice-enrollment-application".equals(nodeName)) {
-                mParseError = "Meta-data does not start with voice-enrollment-application tag";
-                return;
+                String error = "Meta-data does not start with voice-enrollment-application tag for "
+                        + packageName;
+                parseErrors.add(error);
+                Slog.w(TAG, error);
+                return null;
             }
 
             TypedArray array = res.obtainAttributes(attrs,
                     com.android.internal.R.styleable.VoiceEnrollmentApplication);
-            initializeKeyphrasesFromTypedArray(array);
+            keyphraseMetadata = getKeyphraseFromTypedArray(array, packageName, parseErrors);
             array.recycle();
         } catch (XmlPullParserException e) {
-            mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
-            Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
-            return;
+            String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
+            parseErrors.add(error + ": " + e);
+            Slog.w(TAG, error, e);
         } catch (IOException e) {
-            mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
-            Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
-            return;
+            String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
+            parseErrors.add(error + ": " + e);
+            Slog.w(TAG, error, e);
         } catch (PackageManager.NameNotFoundException e) {
-            mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
-            Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
-            return;
+            String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
+            parseErrors.add(error + ": " + e);
+            Slog.w(TAG, error, e);
         } finally {
             if (parser != null) parser.close();
         }
+        return keyphraseMetadata;
     }
 
-    private void initializeKeyphrasesFromTypedArray(TypedArray array) {
+    private KeyphraseMetadata getKeyphraseFromTypedArray(TypedArray array, String packageName,
+            List<String> parseErrors) {
         // Get the keyphrase ID.
         int searchKeyphraseId = array.getInt(
                 com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, -1);
         if (searchKeyphraseId <= 0) {
-            mParseError = "No valid searchKeyphraseId specified in meta-data";
-            Slog.w(TAG, mParseError);
-            return;
+            String error = "No valid searchKeyphraseId specified in meta-data for " + packageName;
+            parseErrors.add(error);
+            Slog.w(TAG, error);
+            return null;
         }
 
         // Get the keyphrase text.
         String searchKeyphrase = array.getString(
                 com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphrase);
         if (searchKeyphrase == null) {
-            mParseError = "No valid searchKeyphrase specified in meta-data";
-            Slog.w(TAG, mParseError);
-            return;
+            String error = "No valid searchKeyphrase specified in meta-data for " + packageName;
+            parseErrors.add(error);
+            Slog.w(TAG, error);
+            return null;
         }
 
         // Get the supported locales.
@@ -198,9 +235,11 @@
                 com.android.internal.R.styleable
                         .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
         if (searchKeyphraseSupportedLocales == null) {
-            mParseError = "No valid searchKeyphraseSupportedLocales specified in meta-data";
-            Slog.w(TAG, mParseError);
-            return;
+            String error = "No valid searchKeyphraseSupportedLocales specified in meta-data for "
+                    + packageName;
+            parseErrors.add(error);
+            Slog.w(TAG, error);
+            return null;
         }
         ArraySet<Locale> locales = new ArraySet<>();
         // Try adding locales if the locale string is non-empty.
@@ -214,9 +253,11 @@
                 // We catch a generic exception here because we don't want the system service
                 // to be affected by a malformed metadata because invalid locales were specified
                 // by the system application.
-                mParseError = "Error reading searchKeyphraseSupportedLocales from meta-data";
-                Slog.w(TAG, mParseError, ex);
-                return;
+                String error = "Error reading searchKeyphraseSupportedLocales from meta-data for "
+                        + packageName;
+                parseErrors.add(error);
+                Slog.w(TAG, error);
+                return null;
             }
         }
 
@@ -224,13 +265,13 @@
         int recognitionModes = array.getInt(com.android.internal.R.styleable
                 .VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, -1);
         if (recognitionModes < 0) {
-            mParseError = "No valid searchKeyphraseRecognitionFlags specified in meta-data";
-            Slog.w(TAG, mParseError);
-            return;
+            String error = "No valid searchKeyphraseRecognitionFlags specified in meta-data for "
+                    + packageName;
+            parseErrors.add(error);
+            Slog.w(TAG, error);
+            return null;
         }
-        mKeyphrases = new KeyphraseMetadata[1];
-        mKeyphrases[0] = new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales,
-                recognitionModes);
+        return new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales, recognitionModes);
     }
 
     public String getParseError() {
@@ -259,14 +300,15 @@
      *         given keyphrase/locale combination isn't possible.
      */
     public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
-        if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) {
+        if (mKeyphrasePackageMap == null || mKeyphrasePackageMap.isEmpty()) {
             Slog.w(TAG, "No enrollment application exists");
             return null;
         }
 
-        if (getKeyphraseMetadata(keyphrase, locale) != null) {
+        KeyphraseMetadata keyphraseMetadata = getKeyphraseMetadata(keyphrase, locale);
+        if (keyphraseMetadata != null) {
             Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES)
-                    .setPackage(mEnrollmentPackage)
+                    .setPackage(mKeyphrasePackageMap.get(keyphraseMetadata))
                     .putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase)
                     .putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale.toLanguageTag())
                     .putExtra(EXTRA_VOICE_KEYPHRASE_ACTION, action);
@@ -298,14 +340,13 @@
                 return keyphraseMetadata;
             }
         }
-        Slog.w(TAG, "Enrollment application doesn't support the given keyphrase/locale");
+        Slog.w(TAG, "No Enrollment application supports the given keyphrase/locale");
         return null;
     }
 
     @Override
     public String toString() {
-        return "KeyphraseEnrollmentInfo [Keyphrases=" + Arrays.toString(mKeyphrases)
-                + ", EnrollmentPackage=" + mEnrollmentPackage + ", ParseError=" + mParseError
-                + "]";
+        return "KeyphraseEnrollmentInfo [Keyphrases=" + mKeyphrasePackageMap.toString()
+                + ", ParseError=" + mParseError + "]";
     }
 }
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..9070ad9 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1131,7 +1131,8 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      *
-     * @deprecated Deprecated in favor of the cleaner {@link #unregisterNetworkCallback} API.
+     * @deprecated Deprecated in favor of the cleaner
+     *             {@link #unregisterNetworkCallback(NetworkCallback)} API.
      *             In {@link VERSION_CODES#M}, and above, this method is unsupported and will
      *             throw {@code UnsupportedOperationException} if called.
      */
@@ -2734,7 +2735,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(),
@@ -2811,7 +2812,7 @@
      * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
      *
      * This {@link NetworkRequest} will live until released via
-     * {@link #unregisterNetworkCallback} or the calling application exits.
+     * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits.
      * Status of the request can be followed by listening to the various
      * callbacks described in {@link NetworkCallback}.  The {@link Network}
      * can be used to direct traffic to the network.
@@ -2848,7 +2849,7 @@
      * This function behaves identically to the non-timedout version, but if a suitable
      * network is not found within the given time (in milliseconds) the
      * {@link NetworkCallback#unavailable} callback is called.  The request must
-     * still be released normally by calling {@link unregisterNetworkCallback}.
+     * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}.
      *
      * <p>This method requires the caller to hold either the
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -2956,7 +2957,8 @@
     /**
      * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)}
      * <p>
-     * This method has the same behavior as {@link #unregisterNetworkCallback} with respect to
+     * This method has the same behavior as
+     * {@link #unregisterNetworkCallback(android.app.PendingIntent)} with respect to
      * releasing network resources and disconnecting.
      *
      * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
@@ -2982,7 +2984,7 @@
     /**
      * Registers to receive notifications about all networks which satisfy the given
      * {@link NetworkRequest}.  The callbacks will continue to be called until
-     * either the application exits or {@link #unregisterNetworkCallback} is called
+     * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called.
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
@@ -3035,9 +3037,9 @@
     }
 
     /**
-     * Registers to receive notifications about whichever network currently satisfies the
-     * system default {@link NetworkRequest}.  The callbacks will continue to be called until
-     * either the application exits or {@link #unregisterNetworkCallback} is called
+     * Registers to receive notifications about changes in the system default network. The callbacks
+     * will continue to be called until either the application exits or
+     * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
@@ -3060,8 +3062,9 @@
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
      * {@link ConnectivityManager.NetworkCallback} if there is an update. Notice that this
-     * method assumes that the caller has previously called {@link #registerNetworkCallback} to
-     * listen for network changes.
+     * method assumes that the caller has previously called
+     * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} to listen for network
+     * changes.
      *
      * @param network {@link Network} specifying which network you're interested.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
@@ -3076,8 +3079,9 @@
 
     /**
      * Unregisters callbacks about and possibly releases networks originating from
-     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and {@link #registerNetworkCallback}
-     * calls.  If the given {@code NetworkCallback} had previously been used with
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and
+     * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} calls.
+     * If the given {@code NetworkCallback} had previously been used with
      * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request
      * will be disconnected.
      *
@@ -3093,6 +3097,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..e0fa63a 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -17,6 +17,7 @@
 package android.net.http;
 
 import android.annotation.SystemApi;
+import android.security.net.config.UserCertificateSource;
 
 import com.android.org.conscrypt.TrustManagerImpl;
 
@@ -43,7 +44,7 @@
     // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used.
     private final X509TrustManager mTrustManager;
     private final Method mCheckServerTrusted;
-    private final Method mIsUserAddedCertificate;
+    private final Method mIsSameTrustConfiguration;
 
     /**
      * Constructs a new X509TrustManagerExtensions wrapper.
@@ -56,7 +57,7 @@
             mDelegate = (TrustManagerImpl) tm;
             mTrustManager = null;
             mCheckServerTrusted = null;
-            mIsUserAddedCertificate = null;
+            mIsSameTrustConfiguration = null;
             return;
         }
         // Use duck typing if possible.
@@ -72,14 +73,15 @@
             throw new IllegalArgumentException("Required method"
                     + " checkServerTrusted(X509Certificate[], String, String, String) missing");
         }
-        // Check that isUserAddedCertificate is present.
+        // Get the option isSameTrustConfiguration method.
+        Method isSameTrustConfiguration = null;
         try {
-            mIsUserAddedCertificate = tm.getClass().getMethod("isUserAddedCertificate",
-                    X509Certificate.class);
-        } catch (NoSuchMethodException e) {
-            throw new IllegalArgumentException(
-                    "Required method isUserAddedCertificate(X509Certificate) missing");
+            isSameTrustConfiguration = tm.getClass().getMethod("isSameTrustConfiguration",
+                    String.class,
+                    String.class);
+        } catch (ReflectiveOperationException ignored) {
         }
+        mIsSameTrustConfiguration = isSameTrustConfiguration;
     }
 
     /**
@@ -117,29 +119,15 @@
     /**
      * Checks whether a CA certificate is added by an user.
      *
-     * <p>Since {@link X509TrustManager#checkServerTrusted} allows its parameter {@code chain} to
+     * <p>Since {@link X509TrustManager#checkServerTrusted} may allow its parameter {@code chain} to
      * chain up to user-added CA certificates, this method can be used to perform additional
      * policies for user-added CA certificates.
      *
-     * @return {@code true} to indicate that the certificate was added by the user, {@code false}
-     * otherwise.
+     * @return {@code true} to indicate that the certificate authority exists in the user added
+     * certificate store, {@code false} otherwise.
      */
     public boolean isUserAddedCertificate(X509Certificate cert) {
-        if (mDelegate != null) {
-            return mDelegate.isUserAddedCertificate(cert);
-        } else {
-            try {
-                return (Boolean) mIsUserAddedCertificate.invoke(mTrustManager, cert);
-            } catch (IllegalAccessException e) {
-                throw new RuntimeException("Failed to call isUserAddedCertificate", e);
-            } catch (InvocationTargetException e) {
-                if (e.getCause() instanceof RuntimeException) {
-                    throw (RuntimeException) e.getCause();
-                } else {
-                    throw new RuntimeException("isUserAddedCertificate failed", e.getCause());
-                }
-            }
-        }
+        return UserCertificateSource.getInstance().findBySubjectAndPublicKey(cert) != null;
     }
 
     /**
@@ -150,6 +138,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/ConnectivityServiceChangeEvent.java b/core/java/android/net/metrics/ConnectivityServiceChangeEvent.java
index c6fcb2d..a491ffc 100644
--- a/core/java/android/net/metrics/ConnectivityServiceChangeEvent.java
+++ b/core/java/android/net/metrics/ConnectivityServiceChangeEvent.java
@@ -25,18 +25,40 @@
 public class ConnectivityServiceChangeEvent extends IpConnectivityEvent implements Parcelable {
     public static final String TAG = "ConnectivityServiceChangeEvent";
 
-    private int mNetId;
+    // The ID of the network that has become the new default or NETID_UNSET if none.
+    private final int mNetId;
+    // The list of transport types of the new default network, for example TRANSPORT_WIFI, as
+    // defined in NetworkCapabilities.java.
+    private final int[] mTransportTypes;
+    // The ID of the network that was the default before or NETID_UNSET if none.
+    private final int mPrevNetId;
+    // Whether the previous network had IPv4/IPv6 connectivity.
+    private final boolean mPrevIPv4;
+    private final boolean mPrevIPv6;
 
-    public ConnectivityServiceChangeEvent(int netId) {
+    public ConnectivityServiceChangeEvent(int netId, int[] transportTypes,
+                int prevNetId, boolean prevIPv4, boolean prevIPv6) {
         mNetId = netId;
+        mTransportTypes = transportTypes;
+        mPrevNetId = prevNetId;
+        mPrevIPv4 = prevIPv4;
+        mPrevIPv6 = prevIPv6;
     }
 
     public ConnectivityServiceChangeEvent(Parcel in) {
         mNetId = in.readInt();
+        mTransportTypes = in.createIntArray();
+        mPrevNetId = in.readInt();
+        mPrevIPv4 = (in.readByte() > 0);
+        mPrevIPv6 = (in.readByte() > 0);
     }
 
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mNetId);
+        out.writeIntArray(mTransportTypes);
+        out.writeInt(mPrevNetId);
+        out.writeByte(mPrevIPv4 ? (byte) 1 : (byte) 0);
+        out.writeByte(mPrevIPv6 ? (byte) 1 : (byte) 0);
     }
 
     public static final Parcelable.Creator<ConnectivityServiceChangeEvent> CREATOR
@@ -50,8 +72,10 @@
         }
     };
 
-    public static void logEvent(int netId) {
+    public static void logEvent(int netId, int[] transportTypes,
+            int prevNetId, boolean prevIPv4, boolean prevIPv6) {
         IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_CONSRV_DEFAULT_NET_CHANGE,
-                new ConnectivityServiceChangeEvent(netId));
+                new ConnectivityServiceChangeEvent(
+                        netId, transportTypes, prevNetId, prevIPv4, prevIPv6));
     }
 };
diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java
new file mode 100644
index 0000000..3869823
--- /dev/null
+++ b/core/java/android/net/metrics/DhcpErrorEvent.java
@@ -0,0 +1,93 @@
+/*
+ * 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} Event class used to record error events when parsing DHCP response packets.
+ */
+public class DhcpErrorEvent extends IpConnectivityEvent implements Parcelable {
+    public static final String TAG = "DhcpErrorEvent";
+
+    public static final int L2_ERROR   = 1;
+    public static final int L3_ERROR   = 2;
+    public static final int L4_ERROR   = 3;
+    public static final int DHCP_ERROR = 4;
+    public static final int MISC_ERROR = 5;
+
+    public static final int L2_TOO_SHORT               = makeErrorCode(L2_ERROR, 1);
+    public static final int L2_WRONG_ETH_TYPE          = makeErrorCode(L2_ERROR, 2);
+
+    public static final int L3_TOO_SHORT               = makeErrorCode(L3_ERROR, 1);
+    public static final int L3_NOT_IPV4                = makeErrorCode(L3_ERROR, 2);
+    public static final int L3_INVALID_IP              = makeErrorCode(L3_ERROR, 3);
+
+    public static final int L4_NOT_UDP                 = makeErrorCode(L4_ERROR, 1);
+    public static final int L4_WRONG_PORT              = makeErrorCode(L4_ERROR, 2);
+
+    public static final int BOOTP_TOO_SHORT            = makeErrorCode(DHCP_ERROR, 1);
+    public static final int DHCP_BAD_MAGIC_COOKIE      = makeErrorCode(DHCP_ERROR, 2);
+    public static final int DHCP_INVALID_OPTION_LENGTH = makeErrorCode(DHCP_ERROR, 3);
+    public static final int DHCP_NO_MSG_TYPE           = makeErrorCode(DHCP_ERROR, 4);
+    public static final int DHCP_UNKNOWN_MSG_TYPE      = makeErrorCode(DHCP_ERROR, 5);
+
+    public static final int BUFFER_UNDERFLOW           = makeErrorCode(MISC_ERROR, 1);
+
+    // error code byte format (MSB to LSB):
+    // byte 0: error type
+    // byte 1: error subtype
+    // byte 2: unused
+    // byte 3: optional code
+    public final int errorCode;
+
+    private DhcpErrorEvent(int errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    private DhcpErrorEvent(Parcel in) {
+        this.errorCode = in.readInt();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(errorCode);
+    }
+
+    public static final Parcelable.Creator<DhcpErrorEvent> CREATOR
+        = new Parcelable.Creator<DhcpErrorEvent>() {
+        public DhcpErrorEvent createFromParcel(Parcel in) {
+            return new DhcpErrorEvent(in);
+        }
+
+        public DhcpErrorEvent[] newArray(int size) {
+            return new DhcpErrorEvent[size];
+        }
+    };
+
+    public static void logEvent(int errorCode) {
+        IpConnectivityEvent.logEvent(IPCE_DHCP_PARSE_ERROR, new DhcpErrorEvent(errorCode));
+    }
+
+    public static void logEvent(int errorCode, int option) {
+        logEvent((0xFFFF0000 & errorCode) | (0xFF & option));
+    }
+
+    private static int makeErrorCode(int type, int subtype) {
+        return (type << 24) | ((0xFF & subtype) << 16);
+    }
+}
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/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index c38bf3c..67d3959 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -45,6 +45,7 @@
     UserInfo getPrimaryUser();
     List<UserInfo> getUsers(boolean excludeDying);
     List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+    int[] getProfileIds(int userId, boolean enabledOnly);
     boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
     UserInfo getProfileParent(int userHandle);
     boolean isSameProfileGroup(int userHandle, int otherUserHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 707d5f5..d5b3b35 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1590,18 +1590,46 @@
      * @return A non-empty list of UserHandles associated with the calling user.
      */
     public List<UserHandle> getUserProfiles() {
-        ArrayList<UserHandle> profiles = new ArrayList<UserHandle>();
-        List<UserInfo> users;
+        int[] userIds = getProfileIds(UserHandle.myUserId(), true /* enabledOnly */);
+        List<UserHandle> result = new ArrayList<>(userIds.length);
+        for (int userId : userIds) {
+            result.add(UserHandle.of(userId));
+        }
+        return result;
+    }
+
+    /**
+     * Returns a list of ids for profiles associated with the specified user including the user
+     * itself.
+     *
+     * @param userId      id of the user to return profiles for
+     * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
+     * @return A non-empty list of ids of profiles associated with the specified user.
+     *
+     * @hide
+     */
+    public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
         try {
-            users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */);
+            return mService.getProfileIds(userId, enabledOnly);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
-        for (UserInfo info : users) {
-            UserHandle userHandle = new UserHandle(info.id);
-            profiles.add(userHandle);
-        }
-        return profiles;
+    }
+
+    /**
+     * @see #getProfileIds(int, boolean)
+     * @hide
+     */
+    public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
+        return getProfileIds(userId, false /* enabledOnly */);
+    }
+
+    /**
+     * @see #getProfileIds(int, boolean)
+     * @hide
+     */
+    public int[] getEnabledProfileIds(@UserIdInt int userId) {
+        return getProfileIds(userId, true /* enabledOnly */);
     }
 
     /**
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 2a3c3fe..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)));
@@ -6666,6 +6679,17 @@
        public static final String DEVICE_PROVISIONED = "device_provisioned";
 
        /**
+        * Whether mobile data should be allowed while the device is being provisioned.
+        * This allows the provisioning process to turn off mobile data before the user
+        * has an opportunity to set things up, preventing other processes from burning
+        * precious bytes before wifi is setup.
+        * (0 = false, 1 = true)
+        * @hide
+        */
+       public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED =
+               "device_provisioning_mobile_data";
+
+       /**
         * The saved value for WindowManagerService.setForcedDisplaySize().
         * Two integers separated by a comma.  If unset, then use the real display size.
         * @hide
@@ -7782,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/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index 3c292ca..d9cc82a 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -115,15 +115,6 @@
         return trustedChain;
     }
 
-    /**
-     * Check if the provided certificate is a user added certificate authority.
-     * This is required by android.net.http.X509TrustManagerExtensions.
-     */
-    public boolean isUserAddedCertificate(X509Certificate cert) {
-        // TODO: Figure out the right way to handle this, and if it is still even used.
-        return false;
-    }
-
     private void checkPins(List<X509Certificate> chain) throws CertificateException {
         PinSet pinSet = mNetworkSecurityConfig.getPins();
         if (pinSet.pins.isEmpty()
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index 19f6887..2a30f11 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -131,15 +131,6 @@
         return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
     }
 
-    /**
-     * Check if the provided certificate is a user added certificate authority.
-     * This is required by android.net.http.X509TrustManagerExtensions.
-     */
-    public boolean isUserAddedCertificate(X509Certificate cert) {
-        // TODO: Figure out the right way to handle this, and if it is still even used.
-        return false;
-    }
-
     @Override
     public X509Certificate[] getAcceptedIssuers() {
         // getAcceptedIssuers is meant to be used to determine which trust anchors the server will
@@ -148,4 +139,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/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index dbc28f7..78e6bc3 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -30,7 +30,8 @@
 oneway interface IVoiceInteractionSession {
     void show(in Bundle sessionArgs, int flags, IVoiceInteractionSessionShowCallback showCallback);
     void hide();
-    void handleAssist(in Bundle assistData, in AssistStructure structure, in AssistContent content);
+    void handleAssist(in Bundle assistData, in AssistStructure structure, in AssistContent content,
+                      int index, int count);
     void handleScreenshot(in Bitmap screenshot);
     void taskStarted(in Intent intent, int taskId);
     void taskFinished(in Intent intent, int taskId);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 6ff9fe7..e354ab3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -77,7 +77,7 @@
  */
 public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 {
     static final String TAG = "VoiceInteractionSession";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     /**
      * Flag received in {@link #onShow}: originator requested that the session be started with
@@ -110,6 +110,16 @@
      */
     public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
 
+    // Keys for Bundle values
+    /** @hide */
+    public static final String KEY_DATA = "data";
+    /** @hide */
+    public static final String KEY_STRUCTURE = "structure";
+    /** @hide */
+    public static final String KEY_CONTENT = "content";
+    /** @hide */
+    public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
+
     final Context mContext;
     final HandlerCaller mHandlerCaller;
 
@@ -229,7 +239,7 @@
 
         @Override
         public void handleAssist(final Bundle data, final AssistStructure structure,
-                final AssistContent content) {
+                final AssistContent content, final int index, final int count) {
             // We want to pre-warm the AssistStructure before handing it off to the main
             // thread.  We also want to do this on a separate thread, so that if the app
             // is for some reason slow (due to slow filling in of async children in the
@@ -247,8 +257,9 @@
                             failure = e;
                         }
                     }
-                    mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_HANDLE_ASSIST,
-                            data, failure == null ? structure : null, failure, content));
+                    mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOOII(MSG_HANDLE_ASSIST,
+                            data, failure == null ? structure : null, failure, content,
+                            index, count));
                 }
             };
             retriever.start();
@@ -831,9 +842,16 @@
                 case MSG_HANDLE_ASSIST:
                     args = (SomeArgs)msg.obj;
                     if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
-                            + " structure=" + args.arg2 + " content=" + args.arg3);
-                    doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
-                            (Throwable) args.arg3, (AssistContent) args.arg4);
+                            + " structure=" + args.arg2 + " content=" + args.arg3
+                            + " activityIndex=" + args.argi5 + " activityCount=" + args.argi6);
+                    if (args.argi5 == 0) {
+                        doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
+                                (Throwable) args.arg3, (AssistContent) args.arg4);
+                    } else {
+                        doOnHandleAssistSecondary((Bundle) args.arg1, (AssistStructure) args.arg2,
+                                (Throwable) args.arg3, (AssistContent) args.arg4,
+                                args.argi5, args.argi6);
+                    }
                     break;
                 case MSG_HANDLE_SCREENSHOT:
                     if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
@@ -1320,6 +1338,14 @@
         onHandleAssist(data, structure, content);
     }
 
+    void doOnHandleAssistSecondary(Bundle data, AssistStructure structure, Throwable failure,
+            AssistContent content, int index, int count) {
+        if (failure != null) {
+            onAssistStructureFailure(failure);
+        }
+        onHandleAssistSecondary(data, structure, content, index, count);
+    }
+
     /**
      * Called when there has been a failure transferring the {@link AssistStructure} to
      * the assistant.  This may happen, for example, if the data is too large and results
@@ -1356,6 +1382,45 @@
     }
 
     /**
+     * Called to receive data from other applications that the user was or is interacting with,
+     * that are currently on the screen in a multi-window display environment, not including the
+     * currently focused activity. This could be
+     * a free-form window, a picture-in-picture window, or another window in a split-screen display.
+     * <p>
+     * This method is very similar to
+     * {@link #onHandleAssist} except that it is called
+     * for additional non-focused activities along with an index and count that indicates
+     * which additional activity the data is for. {@code index} will be between 1 and
+     * {@code count}-1 and this method is called once for each additional window, in no particular
+     * order. The {@code count} indicates how many windows to expect assist data for, including the
+     * top focused activity, which continues to be returned via {@link #onHandleAssist}.
+     * <p>
+     * To be responsive to assist requests, process assist data as soon as it is received,
+     * without waiting for all queued activities to return assist data.
+     *
+     * @param data Arbitrary data supplied by the app through
+     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
+     * May be null if assist data has been disabled by the user or device policy.
+     * @param structure If available, the structure definition of all windows currently
+     * displayed by the app.  May be null if assist data has been disabled by the user
+     * or device policy; will be an empty stub if the application has disabled assist
+     * by marking its window as secure.
+     * @param content Additional content data supplied by the app through
+     * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+     * May be null if assist data has been disabled by the user or device policy; will
+     * not be automatically filled in with data from the app if the app has marked its
+     * window as secure.
+     * @param index the index of the additional activity that this data
+     *        is for.
+     * @param count the total number of additional activities for which the assist data is being
+     *        returned, including the focused activity that is returned via
+     *        {@link #onHandleAssist}.
+     */
+    public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure,
+            @Nullable AssistContent content, int index, int count) {
+    }
+
+    /**
      * Called to receive a screenshot of what the user was currently viewing when an assist
      * session is started.  May be null if screenshots are disabled by the user, policy,
      * or application.  If the original show request did not specify
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 1eaa7cf..9b238ce 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -457,8 +457,17 @@
     private class SynthHandler extends Handler {
         private SpeechItem mCurrentSpeechItem = null;
 
-        private ArrayList<Object> mFlushedObjects = new ArrayList<Object>();
-        private boolean mFlushAll;
+        // When a message with QUEUE_FLUSH arrives we add the caller identity to the List and when a
+        // message with QUEUE_DESTROY arrives we increment mFlushAll. Then a message is added to the
+        // handler queue that removes the caller identify from the list and decrements the mFlushAll
+        // counter. This is so that when a message is processed and the caller identity is in the
+        // list or mFlushAll is not zero, we know that the message should be flushed.
+        // It's important that mFlushedObjects is a List and not a Set, and that mFlushAll is an
+        // int and not a bool. This is because when multiple messages arrive with QUEUE_FLUSH or
+        // QUEUE_DESTROY, we want to keep flushing messages until we arrive at the last QUEUE_FLUSH
+        // or QUEUE_DESTROY message.
+        private List<Object> mFlushedObjects = new ArrayList<>();
+        private int mFlushAll = 0;
 
         public SynthHandler(Looper looper) {
             super(looper);
@@ -467,7 +476,7 @@
         private void startFlushingSpeechItems(Object callerIdentity) {
             synchronized (mFlushedObjects) {
                 if (callerIdentity == null) {
-                    mFlushAll = true;
+                    mFlushAll += 1;
                 } else {
                     mFlushedObjects.add(callerIdentity);
                 }
@@ -476,7 +485,7 @@
         private void endFlushingSpeechItems(Object callerIdentity) {
             synchronized (mFlushedObjects) {
                 if (callerIdentity == null) {
-                    mFlushAll = false;
+                    mFlushAll -= 1;
                 } else {
                     mFlushedObjects.remove(callerIdentity);
                 }
@@ -484,7 +493,7 @@
         }
         private boolean isFlushed(SpeechItem speechItem) {
             synchronized (mFlushedObjects) {
-                return mFlushAll || mFlushedObjects.contains(speechItem.getCallerIdentity());
+                return mFlushAll > 0 || mFlushedObjects.contains(speechItem.getCallerIdentity());
             }
         }
 
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/util/Patterns.java b/core/java/android/util/Patterns.java
index 9ed4850..0a452db 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -251,9 +251,9 @@
             + "|[1-9][0-9]|[0-9]))");
 
     /**
-     * Valid UCS characters defined in RFC 3987.
+     * Valid UCS characters defined in RFC 3987. Excludes space characters.
      */
-    private static final String UCS_CHAR =
+    private static final String UCS_CHAR = "[" +
             "\u00A0-\uD7FF" +
             "\uF900-\uFDCF" +
             "\uFDF0-\uFFEF" +
@@ -270,7 +270,8 @@
             "\uDA80\uDC00-\uDABF\uDFFD" +
             "\uDAC0\uDC00-\uDAFF\uDFFD" +
             "\uDB00\uDC00-\uDB3F\uDFFD" +
-            "\uDB44\uDC00-\uDB7F\uDFFD";
+            "\uDB44\uDC00-\uDB7F\uDFFD" +
+            "&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]";
 
     /**
      * Valid characters for IRI label defined in RFC 3987.
diff --git a/core/java/android/view/IDockedStackListener.aidl b/core/java/android/view/IDockedStackListener.aidl
index cbc8dbd..36a81db 100644
--- a/core/java/android/view/IDockedStackListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -42,4 +42,18 @@
      * @param animDuration The duration of the animation for changing the minimized state.
      */
     void onDockedStackMinimizedChanged(boolean minimized, long animDuration);
+
+    /**
+     * Called when window manager decides to adjust the divider for IME. Like the minimized state,
+     * the divider should make itself not interactable and shrink a bit, but in a different way.s
+     *
+     * @param minimized Whether the stacks are currently adjusted for the IME
+     * @param animDuration The duration of the animation for changing the adjusted state.
+     */
+    void onAdjustedForImeChanged(boolean adjustedForIme, 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 f44d4c1a..206ba16 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -903,6 +903,10 @@
         nSerializeDisplayListTree(mNativeProxy);
     }
 
+    public static boolean copySurfaceInto(Surface surface, Bitmap bitmap) {
+        return nCopySurfaceInto(surface, bitmap);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -1040,4 +1044,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..8a6b5da 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;
@@ -15741,8 +15742,10 @@
             return;
         }
 
-        // Destroy any previous software drawing cache if needed
-        if (mLayerType == LAYER_TYPE_SOFTWARE) {
+        if (layerType != LAYER_TYPE_SOFTWARE) {
+            // Destroy any previous software drawing cache if present
+            // NOTE: even if previous layer type is HW, we do this to ensure we've cleaned up
+            // drawing cache created in View#draw when drawing to a SW canvas.
             destroyDrawingCache();
         }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 94c4cef..e9ca623 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);
 
@@ -2558,6 +2559,7 @@
 
             if (mAttachInfo.mHardwareRenderer != null) {
                 mAttachInfo.mHardwareRenderer.fence();
+                mAttachInfo.mHardwareRenderer.setStopped(mStopped);
             }
 
             if (LOCAL_LOGV) {
@@ -2707,6 +2709,13 @@
                 // shortly before the draw commands get send to the renderer.
                 final boolean updated = updateContentDrawBounds();
 
+                if (mReportNextDraw) {
+                    // report next draw overrides setStopped()
+                    // This value is re-sync'd to the value of mStopped
+                    // in the handling of mReportNextDraw post-draw.
+                    mAttachInfo.mHardwareRenderer.setStopped(false);
+                }
+
                 mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
 
                 if (updated) {
@@ -3376,6 +3385,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..89e146b 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.
          */
@@ -1716,6 +1726,16 @@
          */
         public CharSequence accessibilityTitle;
 
+        /**
+         * Sets a timeout in milliseconds before which the window will be removed
+         * by the window manager. Useful for transient notifications like toasts
+         * so we don't have to rely on client cooperation to ensure the window
+         * is removed. Must be specified at window creation time.
+         *
+         * @hide
+         */
+        public long removeTimeoutMilliseconds = -1;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1764,13 +1784,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,9 +1852,11 @@
             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);
+            out.writeLong(removeTimeoutMilliseconds);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1874,9 +1906,11 @@
             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);
+            removeTimeoutMilliseconds = in.readLong();
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2075,6 +2109,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;
@@ -2092,6 +2131,9 @@
                 changes |= ACCESSIBILITY_TITLE_CHANGED;
             }
 
+            // This can't change, it's only set at window creation time.
+            removeTimeoutMilliseconds = o.removeTimeoutMilliseconds;
+
             return changes;
         }
 
@@ -2200,11 +2242,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 a42f4d9..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;
@@ -68,6 +71,7 @@
     // TODO: remove this
     private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
             "UntranslatableReplacementStringInSubtypeName";
+    private static final int SUBTYPE_ID_NONE = 0;
 
     private final boolean mIsAuxiliary;
     private final boolean mOverridesImplicitlyEnabledSubtype;
@@ -157,13 +161,13 @@
          * track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
          * stay enabled even if other attributes are different. If the ID is unspecified or 0,
          * Arrays.hashCode(new Object[] {locale, mode, extraValue,
-         * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+         * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
          */
         public InputMethodSubtypeBuilder setSubtypeId(int subtypeId) {
             mSubtypeId = subtypeId;
             return this;
         }
-        private int mSubtypeId = 0;
+        private int mSubtypeId = SUBTYPE_ID_NONE;
 
         /**
          * @param subtypeLocale is the locale supported by this subtype.
@@ -268,7 +272,7 @@
      * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
      * other attributes are different. If the ID is unspecified or 0,
      * Arrays.hashCode(new Object[] {locale, mode, extraValue,
-     * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+     * isAuxiliary, overridesImplicitlyEnabledSubtype, isAsciiCapable}) will be used instead.
      */
     public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
             boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
@@ -293,9 +297,12 @@
         mIsAsciiCapable = builder.mIsAsciiCapable;
         // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
         // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
-        mSubtypeHashCode = mSubtypeId != 0 ? mSubtypeId : hashCodeInternal(mSubtypeLocale,
-                mSubtypeMode, mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype,
-                mIsAsciiCapable);
+        if (mSubtypeId != SUBTYPE_ID_NONE) {
+            mSubtypeHashCode = mSubtypeId;
+        } else {
+            mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
+                    mIsAuxiliary, mOverridesImplicitlyEnabledSubtype, mIsAsciiCapable);
+        }
     }
 
     InputMethodSubtype(Parcel source) {
@@ -417,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() {
@@ -501,6 +566,22 @@
         return mSubtypeHashCode;
     }
 
+    /**
+     * @hide
+     * @return {@code true} if a valid subtype ID exists.
+     */
+    public final boolean hasSubtypeId() {
+        return mSubtypeId != SUBTYPE_ID_NONE;
+    }
+
+    /**
+     * @hide
+     * @return subtype ID. {@code 0} means that not subtype ID is specified.
+     */
+    public final int getSubtypeId() {
+        return mSubtypeId;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (o instanceof InputMethodSubtype) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 0ac5731..f13cbae 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -18,13 +18,14 @@
 
 import android.annotation.SystemApi;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.Application;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.XmlResourceParser;
+import android.content.pm.Signature;
 import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
@@ -32,27 +33,21 @@
 import android.os.StrictMode;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.provider.Settings;
-import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
+import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
 
 import dalvik.system.VMRuntime;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
-import org.xmlpull.v1.XmlPullParserException;
-
 /**
  * Top level factory, used creating all the main WebView implementation classes.
  *
@@ -192,52 +187,126 @@
         }
     }
 
+    /**
+     * Returns true if the signatures match, false otherwise
+     */
+    private static boolean signaturesEquals(Signature[] s1, Signature[] s2) {
+        if (s1 == null) {
+            return s2 == null;
+        }
+        if (s2 == null) return false;
+
+        ArraySet<Signature> set1 = new ArraySet<>();
+        for(Signature signature : s1) {
+            set1.add(signature);
+        }
+        ArraySet<Signature> set2 = new ArraySet<>();
+        for(Signature signature : s2) {
+            set2.add(signature);
+        }
+        return set1.equals(set2);
+    }
+
+    // Throws MissingWebViewPackageException on failure
+    private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) {
+        if (!chosen.packageName.equals(toUse.packageName)) {
+            throw new MissingWebViewPackageException("Failed to verify WebView provider, "
+                    + "packageName mismatch, expected: "
+                    + chosen.packageName + " actual: " + toUse.packageName);
+        }
+        if (chosen.versionCode > toUse.versionCode) {
+            throw new MissingWebViewPackageException("Failed to verify WebView provider, "
+                    + "version code mismatch, expected: " + chosen.versionCode
+                    + " actual: " + toUse.versionCode);
+        }
+        if (getWebViewLibrary(toUse.applicationInfo) == null) {
+            throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
+                    + toUse.packageName);
+        }
+        if (!signaturesEquals(chosen.signatures, toUse.signatures)) {
+            throw new MissingWebViewPackageException("Failed to verify WebView provider, "
+                    + "signature mismatch");
+        }
+    }
+
+    private static Context getWebViewContextAndSetProvider() {
+        Application initialApplication = AppGlobals.getInitialApplication();
+        try {
+            WebViewProviderResponse response = null;
+            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
+                    "WebViewUpdateService.waitForAndGetProvider()");
+            try {
+                response = getUpdateService().waitForAndGetProvider();
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
+            }
+            if (response.status != LIBLOAD_SUCCESS) {
+                throw new MissingWebViewPackageException("Failed to load WebView provider: "
+                        + getWebViewPreparationErrorReason(response.status));
+            }
+            // Register to be killed before fetching package info - so that we will be
+            // killed if the package info goes out-of-date.
+            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()");
+            try {
+                ActivityManagerNative.getDefault().addPackageDependency(
+                        response.packageInfo.packageName);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
+            }
+            // Fetch package info and verify it against the chosen package
+            PackageInfo newPackageInfo = null;
+            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
+            try {
+                newPackageInfo = initialApplication.getPackageManager().getPackageInfo(
+                    response.packageInfo.packageName,
+                    PackageManager.GET_SHARED_LIBRARY_FILES
+                    | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
+                    // Make sure that we fetch the current provider even if its not
+                    // installed for the current user
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                    // Fetch signatures for verification
+                    | PackageManager.GET_SIGNATURES
+                    // Get meta-data for meta data flag verification
+                    | PackageManager.GET_META_DATA);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
+            }
+
+            // Validate the newly fetched package info, throws MissingWebViewPackageException on
+            // failure
+            verifyPackageInfo(response.packageInfo, newPackageInfo);
+
+            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
+                    "initialApplication.createApplicationContext");
+            try {
+                // Construct an app context to load the Java code into the current app.
+                Context webViewContext = initialApplication.createApplicationContext(
+                        newPackageInfo.applicationInfo,
+                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+                sPackageInfo = response.packageInfo;
+                return webViewContext;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
+            }
+        } catch (RemoteException | PackageManager.NameNotFoundException e) {
+            throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
+        }
+    }
+
     private static Class<WebViewFactoryProvider> getProviderClass() {
+        Context webViewContext = null;
+        Application initialApplication = AppGlobals.getInitialApplication();
+
         try {
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
-                    "WebViewFactory.waitForProviderAndSetPackageInfo()");
+                    "WebViewFactory.getWebViewContextAndSetProvider()");
             try {
-                // First fetch the package info so we can log the webview package version.
-                int res = waitForProviderAndSetPackageInfo();
-                if (res != LIBLOAD_SUCCESS) {
-                    throw new MissingWebViewPackageException(
-                            "Failed to load WebView provider, error: "
-                            + getWebViewPreparationErrorReason(res));
-                }
+                webViewContext = getWebViewContextAndSetProvider();
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
-                sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
-
-            Application initialApplication = AppGlobals.getInitialApplication();
-            Context webViewContext = null;
-            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getApplicationInfo()");
-            try {
-                // Construct a package context to load the Java code into the current app.
-                // This is done as early as possible since by constructing a package context we
-                // register the WebView package as a dependency for the current application so that
-                // when the WebView package is updated this application will be killed.
-                ApplicationInfo applicationInfo =
-                    initialApplication.getPackageManager().getApplicationInfo(
-                        sPackageInfo.packageName, PackageManager.GET_SHARED_LIBRARY_FILES
-                        | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
-                        // make sure that we fetch the current provider even if its not installed
-                        // for the current user
-                        | PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
-                        "initialApplication.createApplicationContext");
-                try {
-                    webViewContext = initialApplication.createApplicationContext(applicationInfo,
-                            Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
-                } finally {
-                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new MissingWebViewPackageException(e);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
-            }
+                    sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
 
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
             try {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0d8d8ed..bb883ea 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);
@@ -1514,6 +1512,24 @@
         outParams.x = drawingLocation[0] + xOffset;
         outParams.y = drawingLocation[1] + anchorHeight + yOffset;
 
+        // Let the window manager know to align the top to y.
+        outParams.gravity = Gravity.LEFT | Gravity.TOP;
+        outParams.width = width;
+        outParams.height = height;
+
+        // If width or height is unspecified. We can leave it to the window manager to match
+        // to the parent size, but for our local purposes of calculating positioning, we need
+        // to fill in real width and height values.
+        final Rect displayFrame = new Rect();
+        anchor.getWindowVisibleDisplayFrame(displayFrame);
+        if (width < 0) {
+            width = displayFrame.right - displayFrame.left;
+        }
+        if (height < 0) {
+            height = displayFrame.bottom - displayFrame.top;
+        }
+
+
         // If we need to adjust for gravity RIGHT, align to the bottom-right
         // corner of the anchor (still accounting for offsets).
         final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
@@ -1522,17 +1538,9 @@
             outParams.x -= width - anchorWidth;
         }
 
-        // Let the window manager know to align the top to y.
-        outParams.gravity = Gravity.LEFT | Gravity.TOP;
-        outParams.width = width;
-        outParams.height = height;
-
         final int[] screenLocation = mTmpScreenLocation;
         anchor.getLocationOnScreen(screenLocation);
 
-        final Rect displayFrame = new Rect();
-        anchor.getWindowVisibleDisplayFrame(displayFrame);
-
         // First, attempt to fit the popup vertically without resizing.
         final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
                 anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
@@ -1581,7 +1589,7 @@
             return true;
         }
 
-        final int spaceAbove = displayFrameTop + anchorTopInScreen - anchorHeight;
+        final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
         if (height <= spaceAbove) {
             // Move everything up.
             if (mOverlapAnchor) {
@@ -2116,10 +2124,10 @@
 
         // If an explicit width/height has not specified, use the most recent
         // explicitly specified value (either from setWidth/Height or update).
-        if (width == -1) {
+        if (width < 0) {
             width = mWidth;
         }
-        if (height == -1) {
+        if (height < 0) {
             height = mHeight;
         }
 
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/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index ee716df..1a81d20 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -849,10 +849,12 @@
         mEnabledDayStart = MathUtils.constrain(enabledDayStart, 1, mDaysInMonth);
         mEnabledDayEnd = MathUtils.constrain(enabledDayEnd, mEnabledDayStart, mDaysInMonth);
 
+        updateMonthYearLabel();
+        updateDayOfWeekLabels();
+
         // Invalidate cached accessibility information.
         mTouchHelper.invalidateRoot();
-
-        updateMonthYearLabel();
+        invalidate();
 
         if (DEBUG_WRONG_DATE) {
             Log.d(LOG_TAG, "mMonth = " + mMonth);
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/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 207f675..7762675 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -163,6 +163,7 @@
      */
     public void setDuration(@Duration int duration) {
         mDuration = duration;
+        mTN.mDuration = duration;
     }
 
     /**
@@ -342,7 +343,7 @@
         };
 
         private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
-        final Handler mHandler = new Handler();    
+        final Handler mHandler = new Handler();
 
         int mGravity;
         int mX, mY;
@@ -352,9 +353,13 @@
 
         View mView;
         View mNextView;
+        int mDuration;
 
         WindowManager mWM;
 
+        static final long SHORT_DURATION_TIMEOUT = 5000;
+        static final long LONG_DURATION_TIMEOUT = 1000;
+
         TN() {
             // XXX This should be changed to use a Dialog, with a Theme.Toast
             // defined that sets up the layout params appropriately.
@@ -417,6 +422,8 @@
                 mParams.verticalMargin = mVerticalMargin;
                 mParams.horizontalMargin = mHorizontalMargin;
                 mParams.packageName = packageName;
+                mParams.removeTimeoutMilliseconds = mDuration ==
+                    Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
                 if (mView.getParent() != null) {
                     if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
                     mWM.removeView(mView);
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/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 46b49de..8d11783 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.inputmethod;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.text.TextUtils;
@@ -284,8 +285,22 @@
             return -1;
         }
 
+        /**
+         * Provides the basic operation to implement bi-directional IME rotation.
+         * @param onlyCurrentIme {@code true} to limit the search space to IME subtypes that belong
+         * to {@code imi}.
+         * @param imi {@link InputMethodInfo} that will be used in conjunction with {@code subtype}
+         * from which we find the adjacent IME subtype.
+         * @param subtype {@link InputMethodSubtype} that will be used in conjunction with
+         * {@code imi} from which we find the next IME subtype.  {@code null} if the input method
+         * does not have a subtype.
+         * @param forward {@code true} to do forward search the next IME subtype. Specify
+         * {@code false} to do backward search.
+         * @return The IME subtype found. {@code null} if no IME subtype is found.
+         */
+        @Nullable
         public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
-                InputMethodInfo imi, InputMethodSubtype subtype, boolean forward) {
+                InputMethodInfo imi, @Nullable InputMethodSubtype subtype, boolean forward) {
             if (imi == null) {
                 return null;
             }
@@ -298,7 +313,7 @@
             }
             final int N = mImeSubtypeList.size();
             for (int i = 1; i < N; ++i) {
-                // Start searching the next IME/subtype from the next of the current index.
+                // Start searching the next IME/subtype from +/- 1 indices.
                 final int offset = forward ? i : N - i;
                 final int candidateIndex = (currentIndex + offset) % N;
                 final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
@@ -371,8 +386,22 @@
             mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
         }
 
+        /**
+         * Provides the basic operation to implement bi-directional IME rotation.
+         * @param onlyCurrentIme {@code true} to limit the search space to IME subtypes that belong
+         * to {@code imi}.
+         * @param imi {@link InputMethodInfo} that will be used in conjunction with {@code subtype}
+         * from which we find the adjacent IME subtype.
+         * @param subtype {@link InputMethodSubtype} that will be used in conjunction with
+         * {@code imi} from which we find the next IME subtype.  {@code null} if the input method
+         * does not have a subtype.
+         * @param forward {@code true} to do forward search the next IME subtype. Specify
+         * {@code false} to do backward search.
+         * @return The IME subtype found. {@code null} if no IME subtype is found.
+         */
+        @Nullable
         public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
-                InputMethodInfo imi, InputMethodSubtype subtype, boolean forward) {
+                InputMethodInfo imi, @Nullable InputMethodSubtype subtype, boolean forward) {
             int currentUsageRank = getUsageRank(imi, subtype);
             if (currentUsageRank < 0) {
                 if (DEBUG) {
@@ -456,8 +485,22 @@
             mSwitchingUnawareRotationList = switchingUnawareRotationList;
         }
 
+        /**
+         * Provides the basic operation to implement bi-directional IME rotation.
+         * @param onlyCurrentIme {@code true} to limit the search space to IME subtypes that belong
+         * to {@code imi}.
+         * @param imi {@link InputMethodInfo} that will be used in conjunction with {@code subtype}
+         * from which we find the adjacent IME subtype.
+         * @param subtype {@link InputMethodSubtype} that will be used in conjunction with
+         * {@code imi} from which we find the next IME subtype.  {@code null} if the input method
+         * does not have a subtype.
+         * @param forward {@code true} to do forward search the next IME subtype. Specify
+         * {@code false} to do backward search.
+         * @return The IME subtype found. {@code null} if no IME subtype is found.
+         */
+        @Nullable
         public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
-                InputMethodSubtype subtype, boolean forward) {
+                @Nullable InputMethodSubtype subtype, boolean forward) {
             if (imi == null) {
                 return null;
             }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index bbefcb5..10afdb8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1745,6 +1745,9 @@
         }
 
         public void add(String name, T obj) {
+            if (name == null) {
+                name = "";
+            }
             mMap.put(name, obj);
             if (OVERFLOW_NAME.equals(name)) {
                 mCurOverflow = obj;
@@ -1776,6 +1779,9 @@
         }
 
         public T startObject(String name) {
+            if (name == null) {
+                name = "";
+            }
             T obj = mMap.get(name);
             if (obj != null) {
                 return obj;
@@ -1825,6 +1831,9 @@
         }
 
         public T stopObject(String name) {
+            if (name == null) {
+                name = "";
+            }
             T obj = mMap.get(name);
             if (obj != null) {
                 return obj;
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 113768e..c26fc3a 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -209,6 +209,18 @@
         return mH.obtainMessage(what, 0, 0, args);
     }
 
+    public Message obtainMessageOOOOII(int what, Object arg1, Object arg2,
+            Object arg3, Object arg4, int arg5, int arg6) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg1;
+        args.arg2 = arg2;
+        args.arg3 = arg3;
+        args.arg4 = arg4;
+        args.argi5 = arg5;
+        args.argi6 = arg6;
+        return mH.obtainMessage(what, 0, 0, args);
+    }
+
     public Message obtainMessageIIII(int what, int arg1, int arg2,
             int arg3, int arg4) {
         SomeArgs args = SomeArgs.obtain();
@@ -218,7 +230,7 @@
         args.argi4 = arg4;
         return mH.obtainMessage(what, 0, 0, args);
     }
-    
+
     public Message obtainMessageIIIIII(int what, int arg1, int arg2,
             int arg3, int arg4, int arg5, int arg6) {
         SomeArgs args = SomeArgs.obtain();
@@ -230,7 +242,7 @@
         args.argi6 = arg6;
         return mH.obtainMessage(what, 0, 0, args);
     }
-    
+
     public Message obtainMessageIIIIO(int what, int arg1, int arg2,
             int arg3, int arg4, Object arg5) {
         SomeArgs args = SomeArgs.obtain();
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/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 9e5c238..36e21b9 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -63,7 +63,7 @@
     void toggleSplitScreen();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
-    void showScreenPinningRequest();
+    void showScreenPinningRequest(int taskId);
 
     void toggleKeyboardShortcutsMenu(int deviceId);
 
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 5992f7a..b075db8 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -57,7 +57,7 @@
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
     public static final int BASE_DATA_CONNECTION_TRACKER                            = 0x00042000;
-    public static final int BASE_DNS_PINGER                                         = 0x00050000;
+    public static final int BASE_TETHERING                                          = 0x00050000;
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
     public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00080000;
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index ddca51f..5cb307f 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -347,14 +347,21 @@
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
 
-        // Apply "force show icon" setting; if the menu being shown is the top level menu, apply the
-        // setting set on this CascadingMenuPopup by its creating code. If it's a submenu, or if no
-        // "force" setting was explicitly set, determine the setting by examining the items.
+        // Apply "force show icon" setting. There are 4 cases:
+        // (1) This is the top level menu. Only add spacing for icons if forced.
+        // (2) This is a submenu. Add spacing if any of the visible menu items has an icon.
+        // (3) This is a top level menu that is not an overflow menu. Add spacing if any of the
+        //     visible menu items has an icon.
+        // (4) This is an overflow menu or a top level menu that doesn't have "force" set.
+        //     Don't allow spacing.
         if (!isShowing() && mForceShowIcon) {
-            adapter.setForceShowIcon(mForceShowIcon);
-        } else {
-            adapter.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(menu));
+          // Case 1
+          adapter.setForceShowIcon(true);
+        } else if (isShowing() || !isShowing() && !mForceShowIcon && !mOverflowOnly) {
+          // Case 2 or 3
+          adapter.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(menu));
         }
+        // Case 4: Else, don't allow spacing for icons.
 
         final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
         final MenuPopupWindow popupWindow = createPopupWindow();
@@ -734,4 +741,4 @@
             return window.getListView();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index 16e4156..10bd66f 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -187,6 +187,11 @@
     /**
      * Returns whether icon spacing needs to be preserved for the given menu, based on whether any
      * of its items contains an icon.
+     *
+     * NOTE: This should only be used for non-overflow-only menus, because this method does not
+     * take into account whether the menu items are being shown as part of the popup or or being
+     * shown as actions in the action bar.
+     *
      * @param menu
      * @return Whether to preserve icon spacing.
      */
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 25b487e..8b9d503 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,7 +17,8 @@
 
 package com.android.internal.widget;
 
-import android.annotation.NonNull;
+import com.android.internal.R;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -43,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";
@@ -90,10 +90,8 @@
     private final float mMinFlingVelocity;
     private final OverScroller mScroller;
     private final VelocityTracker mVelocityTracker;
-    private final Drawable mScrollIndicatorDrawable;
-    private final Drawable mFakeForeground;
 
-    private View mButtonBar;
+    private Drawable mScrollIndicatorDrawable;
 
     private OnDismissedListener mOnDismissedListener;
     private RunOnDismissedListener mRunOnDismissedListener;
@@ -115,8 +113,6 @@
                 }
             };
 
-    private final int[] mTempOffset = new int[2];
-
     public ResolverDrawerLayout(Context context) {
         this(context, null);
     }
@@ -139,7 +135,6 @@
         a.recycle();
 
         mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
-        mFakeForeground = new ColorDrawable(Color.TRANSPARENT);
 
         mScroller = new OverScroller(context, AnimationUtils.loadInterpolator(context,
                 android.R.interpolator.decelerate_quint));
@@ -152,13 +147,6 @@
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mButtonBar = findViewById(R.id.button_bar);
-    }
-
     public void setSmallCollapsed(boolean smallCollapsed) {
         mSmallCollapsed = smallCollapsed;
         requestLayout();
@@ -474,8 +462,9 @@
         notifyViewAccessibilityStateChangedIfNeeded(
                 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
 
-        // Set a fake foreground so that we receive onDrawForeground().
-        setForeground(isCollapsed ? mFakeForeground : null);
+        if (mScrollIndicatorDrawable != null) {
+            setWillNotDraw(!isCollapsed);
+        }
     }
 
     void dispatchOnDismissed() {
@@ -737,15 +726,7 @@
 
     @Override
     public void onDrawForeground(Canvas canvas) {
-        if (isCollapsed() && mButtonBar != null) {
-            // Draw the scroll indicator directly above the button bar.
-            final int height = mScrollIndicatorDrawable.getIntrinsicHeight();
-            mButtonBar.getLocationInWindow(mTempOffset);
-            final int barTop = mTempOffset[1];
-            getLocationInWindow(mTempOffset);
-            final int myTop = mTempOffset[1];
-            final int top = (barTop - myTop) - height;
-            mScrollIndicatorDrawable.setBounds(0, top, getWidth(), top + height);
+        if (mScrollIndicatorDrawable != null) {
             mScrollIndicatorDrawable.draw(canvas);
         }
 
@@ -838,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();
@@ -846,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;
@@ -866,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
@@ -905,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);
@@ -917,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();
         }
 
@@ -928,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/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7ff38fd5..d3dca5d9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -592,6 +592,7 @@
     char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
     char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
     char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX];
+    char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -701,6 +702,9 @@
     if (strcmp(useJitProfilesOptsBuf, "true") == 0) {
         addOption("-Xjitsaveprofilinginfo");
     }
+    parseRuntimeOption("dalvik.vm.jitprithreadweight",
+                       jitprithreadweightOptBuf,
+                       "-Xjitprithreadweight:");
 
     property_get("ro.config.low_ram", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 22a81d4..d681246 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -752,17 +752,32 @@
             getPremulBitmapCreateFlags(isMutable));
 }
 
-static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
-    SkBitmap src;
-    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& dstCT) {
     SkBitmap result;
 
     AshmemPixelAllocator allocator(env);
-    if (!src.copyTo(&result, &allocator)) {
+    if (!src.copyTo(&result, dstCT, &allocator)) {
         return NULL;
     }
     Bitmap* bitmap = allocator.getStorageObjAndReset();
     bitmap->peekAtPixelRef()->setImmutable();
+    return bitmap;
+}
+
+static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) {
+    SkBitmap src;
+    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+    SkColorType dstCT = src.colorType();
+    Bitmap* bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
+    jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
+    return ret;
+}
+
+static jobject Bitmap_copyAshmemConfig(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle) {
+    SkBitmap src;
+    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
+    SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
+    Bitmap* bitmap = Bitmap_copyAshmemImpl(env, src, dstCT);
     jobject ret = GraphicsJNI::createBitmap(env, bitmap, getPremulBitmapCreateFlags(false));
     return ret;
 }
@@ -1355,6 +1370,8 @@
         (void*)Bitmap_copy },
     {   "nativeCopyAshmem",         "(J)Landroid/graphics/Bitmap;",
         (void*)Bitmap_copyAshmem },
+    {   "nativeCopyAshmemConfig",   "(JI)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copyAshmemConfig },
     {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
     {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
     {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
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 91a3b4f..5c961d9 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -21,8 +21,8 @@
 
 #include <inttypes.h>
 #include <jni.h>
-#include <map>
 #include <queue>
+#include <unordered_map>
 #include <string.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -105,7 +105,7 @@
     context_hub_info_s hubInfo;
     jniInfo_s jniInfo;
     std::queue<int> freeIds;
-    std::map<int, app_instance_info_s *> appInstances;
+    std::unordered_map<int, app_instance_info_s> appInstances;
 };
 
 }  // unnamed namespace
@@ -137,7 +137,7 @@
     const context_hub_t *info = get_hub_info(hubHandle);
 
     if (info) {
-        msg->app = info->os_app_name;
+        msg->app_name = info->os_app_name;
         return 0;
     } else {
         ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
@@ -154,23 +154,23 @@
 }
 
 static int get_hub_id_for_app_instance(int id) {
-    if (db.appInstances.find(id) == db.appInstances.end()) {
+    if (!db.appInstances.count(id)) {
         ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
         return -1;
     }
 
-    int hubHandle = db.appInstances[id]->hubHandle;
+    int hubHandle = db.appInstances[id].hubHandle;
 
     return db.hubInfo.hubs[hubHandle].hub_id;
 }
 
 static int set_dest_app(hub_message_t *msg, int id) {
-    if (db.appInstances.find(id) == db.appInstances.end()) {
+    if (!db.appInstances.count(id)) {
         ALOGD("%s: Cannod find app for app instance %d", __FUNCTION__, id);
         return -1;
     }
 
-    msg->app = db.appInstances[id]->appInfo.name;
+    msg->app_name = db.appInstances[id].appInfo.app_name;
     return 0;
 }
 
@@ -210,76 +210,43 @@
 
 int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) {
     // Not checking if the apps are indeed distinct
-
-    app_instance_info_s *entry;
-    void *appName;
-    hub_app_name_t *name;
-
-    assert(appInfo && appInfo->name && appInfo->name->app_name);
-
-    entry = (app_instance_info_s *) malloc(sizeof(app_instance_info_s));
-    appName = malloc(appInfo->name->app_name_len);
-    name = (hub_app_name_t *) malloc(sizeof(hub_app_name_t));
-
+    app_instance_info_s entry;
     int appInstanceHandle = generate_id();
 
-    if (appInstanceHandle < 0 || !appName || !entry || !name) {
-        ALOGE("Cannot find resources to add app instance %d, %p, %p",
-            appInstanceHandle, appName, entry);
+    assert(appInfo);
 
-        free(appName);
-        free(entry);
-        free(name);
-
-        if (appInstanceHandle >= 0) {
-            return_id(appInstanceHandle);
-        }
-
+    if (appInstanceHandle < 0) {
+        ALOGE("Cannot find resources to add app instance %d",
+              appInstanceHandle);
         return -1;
     }
 
-    memcpy(&(entry->appInfo), appInfo, sizeof(entry->appInfo));
-    memcpy(appName, appInfo->name->app_name, appInfo->name->app_name_len);
-    name->app_name = appName;
-    name->app_name_len = appInfo->name->app_name_len;
-    entry->appInfo.name = name;
-    entry->truncName = 0;
-    memcpy(&(entry->truncName), name->app_name,
-           sizeof(entry->truncName) < name->app_name_len ?
-           sizeof(entry->truncName) : name->app_name_len);
-
-    // Not checking for sanity of hubId
-    entry->hubHandle = hubHandle;
-    entry->instanceId = appInstanceHandle;
+    entry.appInfo = *appInfo;
+    entry.instanceId = appInstanceHandle;
+    entry.truncName = appInfo->app_name.id;
+    entry.hubHandle = hubHandle;
     db.appInstances[appInstanceHandle] = entry;
 
     // Finally - let the service know of this app instance
     env->CallIntMethod(db.jniInfo.jContextHubService,
                        db.jniInfo.contextHubServiceAddAppInstance,
-                       hubHandle, entry->instanceId, entry->truncName,
-                       entry->appInfo.version);
+                       hubHandle, entry.instanceId, entry.truncName,
+                       entry.appInfo.version);
 
     ALOGW("Added App 0x%" PRIx64 " on hub Handle %" PRId32
-          " as appInstance %d, original name_length %" PRId32, entry->truncName,
-          entry->hubHandle, appInstanceHandle, name->app_name_len);
+          " as appInstance %d", entry.truncName,
+          entry.hubHandle, appInstanceHandle);
 
     return appInstanceHandle;
 }
 
 int delete_app_instance(int id) {
-    if (db.appInstances.find(id) == db.appInstances.end()) {
+    if (!db.appInstances.count(id)) {
         return -1;
     }
 
     return_id(id);
-
-    if (db.appInstances[id]) {
-        // Losing the const cast below. This is intentional.
-        free((void *)db.appInstances[id]->appInfo.name->app_name);
-        free((void *)db.appInstances[id]->appInfo.name);
-        free(db.appInstances[id]);
-        db.appInstances.erase(id);
-    }
+    db.appInstances.erase(id);
 
     return 0;
 }
@@ -353,27 +320,20 @@
 }
 
 int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) {
-    int i;
     JNIEnv *env;
     if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
             return -1;
     }
 
     int numApps = msgLen/sizeof(hub_app_info);
-    hub_app_info *info = (hub_app_info *)malloc(msgLen); // handle possible alignment
+    hub_app_info info;
+    hub_app_info *unalignedInfoAddr = (hub_app_info*)msg;
 
-    if (!info) {
-        return -1;
+    for (int i = 0; i < numApps; i++, unalignedInfoAddr++) {
+        memcpy(&info, unalignedInfoAddr, sizeof(info));
+        add_app_instance(&info, hubHandle, env);
     }
 
-    memcpy(info, msg, msgLen);
-    for (i = 0; i < numApps; i++) {
-        add_app_instance(info, hubHandle, env);
-        info++;
-    }
-
-    free(info);
-
     return 0;
 }
 
@@ -410,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 ef45c87..68c818e 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -669,6 +669,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
 // ----------------------------------------------------------------------------
@@ -775,6 +783,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..1ed93cc 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" />
@@ -3007,6 +3008,12 @@
     <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
         android:protectionLevel="signature|setup" />
 
+    <!-- @SystemApi Allows an application to replace the app name displayed alongside notifications
+         in the N-release and later.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 117774a..575404d 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -17,10 +17,10 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
 	android:shareInterpolator="false">
-    <translate android:fromYDelta="0" android:toYDelta="10%"
-			android:interpolator="@interpolator/accelerate_quint"
-            android:duration="@android:integer/config_shortAnimTime"/>
+    <translate android:fromYDelta="0" android:toYDelta="8%"
+			android:interpolator="@interpolator/fast_out_linear_in"
+            android:duration="150"/>
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-			android:interpolator="@interpolator/accelerate_cubic"
-            android:duration="@android:integer/config_shortAnimTime"/>
+			android:interpolator="@interpolator/fast_out_linear_in"
+            android:duration="150"/>
 </set>
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..65a5015 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -18,7 +18,7 @@
 <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_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 fe43e1c..ae94503 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -75,15 +75,16 @@
         android:scrollIndicators="top|bottom"
         android:divider="@null" />
 
-    <TextView
-        android:id="@+id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_alwaysShow="true"
-        android:text="@string/noApplications"
-        android:padding="32dp"
-        android:gravity="center"
-        android:visibility="gone" />
+    <TextView android:id="@+id/empty"
+              android:layout_width="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"
+              android:gravity="center"
+              android:visibility="gone" />
 
     <LinearLayout
         android:id="@+id/button_bar"
@@ -93,6 +94,7 @@
         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"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index ed7ef5e..02dc2ed 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -153,7 +153,6 @@
         android:background="@color/white"
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
-        android:divider="@null"
-        android:scrollIndicators="top|bottom" />
+        android:divider="@null" />
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-mcc310-mnc160/strings.xml b/core/res/res/values-mcc310-mnc160/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc160/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc200/strings.xml b/core/res/res/values-mcc310-mnc200/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc200/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc210/strings.xml b/core/res/res/values-mcc310-mnc210/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc210/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc220/strings.xml b/core/res/res/values-mcc310-mnc220/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc220/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc230/strings.xml b/core/res/res/values-mcc310-mnc230/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc230/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc240/strings.xml b/core/res/res/values-mcc310-mnc240/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc240/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc250/strings.xml b/core/res/res/values-mcc310-mnc250/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc250/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc260/strings.xml b/core/res/res/values-mcc310-mnc260/strings.xml
deleted file mode 100644
index dc79877..0000000
--- a/core/res/res/values-mcc310-mnc260/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc270/strings.xml b/core/res/res/values-mcc310-mnc270/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc270/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc310/strings.xml b/core/res/res/values-mcc310-mnc310/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc310/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc490/strings.xml b/core/res/res/values-mcc310-mnc490/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc490/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc660/strings.xml b/core/res/res/values-mcc310-mnc660/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc660/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
diff --git a/core/res/res/values-mcc310-mnc800/strings.xml b/core/res/res/values-mcc310-mnc800/strings.xml
deleted file mode 100644
index 526d08b..0000000
--- a/core/res/res/values-mcc310-mnc800/strings.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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 my 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false">
-        <item>REG09</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages">
-        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
-    </string-array>
-    <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier</item>
-    </string-array>
-    <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
-</resources>
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-h426dp-port/integers.xml b/core/res/res/values-w320dp-h426dp/integers.xml
similarity index 100%
rename from core/res/res/values-h426dp-port/integers.xml
rename to core/res/res/values-w320dp-h426dp/integers.xml
diff --git a/core/res/res/values-w426dp-land/integers.xml b/core/res/res/values-w426dp-h320dp/integers.xml
similarity index 100%
rename from core/res/res/values-w426dp-land/integers.xml
rename to core/res/res/values-w426dp-h320dp/integers.xml
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 dfa5143b..aada05d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -420,9 +420,6 @@
     <!-- Boolean indicating we re-try re-associating once upon disconnection and RSSI is high failure  -->
     <bool translatable="true" name="config_wifi_enable_disconnection_debounce">true</bool>
 
-    <!-- Boolean indicating autojoin will prefer 5GHz and choose 5GHz BSSIDs -->
-    <bool translatable="true" name="config_wifi_enable_5GHz_preference">true</bool>
-
     <!-- Boolean indicating whether or not to revert to default country code when cellular
          radio is unable to find any MCC information to infer wifi country code from -->
     <bool translatable="false" name="config_wifi_revert_country_code_on_cellular_loss">false</bool>
@@ -439,8 +436,6 @@
     <!-- Integer specifying the basic autojoin parameters -->
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">40</integer>
-    <integer translatable="false" name="config_wifi_framework_current_association_hysteresis_high">16</integer>
-    <integer translatable="false" name="config_wifi_framework_current_association_hysteresis_low">10</integer>
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_penalty_threshold">-75</integer>
     <integer translatable="false" name="config_wifi_framework_RSSI_SCORE_OFFSET">85</integer>
     <integer translatable="false" name="config_wifi_framework_RSSI_SCORE_SLOPE">4</integer>
@@ -495,15 +490,9 @@
     <!-- Integer indicating disconnect mode short scan interval in milliseconds -->
     <integer translatable="false" name="config_wifi_disconnected_short_scan_interval">15000</integer>
 
-    <!-- Integer indicating disconnect mode long scan interval in milliseconds -->
-    <integer translatable="false" name="config_wifi_disconnected_long_scan_interval">120000</integer>
-
     <!-- Integer indicating associated partial scan short interval in milliseconds -->
     <integer translatable="false" name="config_wifi_associated_short_scan_interval">20000</integer>
 
-    <!-- Integer indicating associated partial scan long interval in milliseconds -->
-    <integer translatable="false" name="config_wifi_associated_long_scan_interval">180000</integer>
-
     <!-- Integer indicating associated full scan backoff, representing a fraction: xx/8 -->
     <integer translatable="false" name="config_wifi_framework_associated_full_scan_backoff">12</integer>
 
@@ -516,18 +505,6 @@
     <!-- Integer indicating associated full scan max num active channels -->
     <integer translatable="false" name="config_wifi_framework_associated_partial_scan_max_num_active_channels">6</integer>
 
-    <!-- Integer indicating associated full scan max num passive channels -->
-    <integer translatable="false" name="config_wifi_framework_associated_partial_scan_max_num_passive_channels">3</integer>
-
-    <!-- Integer indicating number of association errors leading to blacklisting of the network -->
-    <integer translatable="false" name="config_wifi_framework_max_connection_errors_to_blacklist">4</integer>
-
-    <!-- Integer indicating number of authentication errors leading to blacklisting of the network -->
-    <integer translatable="false" name="config_wifi_framework_max_auth_errors_to_blacklist">4</integer>
-
-    <!-- Integer indicating minimum blacklisting delay of a wofo configuration due to connectin or auth errors -->
-    <integer translatable="false" name="config_wifi_framework_network_black_list_min_time_milli">120000</integer>
-
     <!-- Integer indicating RSSI boost given to current network -->
     <integer translatable="false" name="config_wifi_framework_current_network_boost">16</integer>
 
@@ -543,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..8200df8 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -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/public.xml b/core/res/res/values/public.xml
index 6ff7139..80b3a64 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2713,11 +2713,14 @@
     <public type="attr" name="forceHasOverlappingRendering" />
     <public type="attr" name="contentInsetStartWithNavigation" />
     <public type="attr" name="contentInsetEndWithActions" />
+    <public type="attr" name="numberPickerStyle" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
     <public type="style" name="Widget.Material.SeekBar.Discrete" />
     <public type="style" name="Widget.Material.CompoundButton.Switch" />
     <public type="style" name="Widget.Material.Light.CompoundButton.Switch" />
+    <public type="style" name="Widget.Material.NumberPicker" />
+    <public type="style" name="Widget.Material.Light.NumberPicker" />
 
     <public type="id" name="accessibilityActionSetProgress" />
     <public type="id" name="icon_frame" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 96731cf..836efde 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -245,16 +245,19 @@
 
     <!-- Displayed when WFC registration fails -->
     <string name="wfcRegErrorTitle">Wi-Fi Calling</string>
-    <!-- WFC Operator Error Codes -->
-    <string-array name="wfcOperatorErrorCodes" translatable="false" />
     <!-- WFC Operator Error Messages showed as alerts -->
-    <string-array name="wfcOperatorErrorAlertMessages" />
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
     <!-- WFC Operator Error Messages showed as notifications -->
-    <string-array name="wfcOperatorErrorNotificationMessages" />
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
     <!-- Template for showing cellular network operator name while WFC is active -->
-    <string name="wfcSpnFormat">%s</string>
-    <!-- Template for showing operator name for data connection while WFC is active -->
-    <string name="wfcDataSpnFormat">%s</string>
+    <string-array name="wfcSpnFormats">
+        <item>%s</item>
+        <item>%s Wi-Fi Calling</item>
+    </string-array>
     <!-- WFC, summary for Disabled -->
     <string name="wifi_calling_off_summary">Off</string>
     <!-- WFC, summary for Wi-Fi Preferred -->
@@ -349,18 +352,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 +2555,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 +2585,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 +4051,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 8d8b832..a35b409 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -295,7 +295,6 @@
   <java-symbol type="bool" name="config_wifi_framework_enable_associated_network_selection" />
   <java-symbol type="bool" name="config_wifi_only_link_same_credential_configurations" />
   <java-symbol type="bool" name="config_wifi_enable_disconnection_debounce" />
-  <java-symbol type="bool" name="config_wifi_enable_5GHz_preference" />
   <java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
   <java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
   <java-symbol type="integer" name="config_wifi_logger_ring_buffer_size_limit_kb" />
@@ -312,8 +311,6 @@
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
-  <java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_high" />
-  <java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_low" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_OFFSET" />
   <java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_SLOPE" />
@@ -322,14 +319,11 @@
   <java-symbol type="integer" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD" />
   <java-symbol type="integer" name="config_wifi_framework_SECURITY_AWARD" />
   <java-symbol type="integer" name="config_wifi_disconnected_short_scan_interval" />
-  <java-symbol type="integer" name="config_wifi_disconnected_long_scan_interval" />
   <java-symbol type="integer" name="config_wifi_associated_short_scan_interval" />
-  <java-symbol type="integer" name="config_wifi_associated_long_scan_interval" />
   <java-symbol type="integer" name="config_wifi_framework_associated_full_scan_backoff" />
   <java-symbol type="integer" name="config_wifi_framework_associated_full_scan_max_interval" />
   <java-symbol type="integer" name="config_wifi_framework_associated_full_scan_max_total_dwell_time" />
   <java-symbol type="integer" name="config_wifi_framework_associated_partial_scan_max_num_active_channels" />
-  <java-symbol type="integer" name="config_wifi_framework_associated_partial_scan_max_num_passive_channels" />
   <java-symbol type="integer" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz" />
   <java-symbol type="integer" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz" />
   <java-symbol type="integer" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz" />
@@ -359,9 +353,6 @@
   <java-symbol type="integer" name="config_wifi_framework_associated_partial_scan_rx_packet_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_network_switch_tx_packet_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_network_switch_rx_packet_threshold" />
-  <java-symbol type="integer" name="config_wifi_framework_max_connection_errors_to_blacklist" />
-  <java-symbol type="integer" name="config_wifi_framework_max_auth_errors_to_blacklist" />
-  <java-symbol type="integer" name="config_wifi_framework_network_black_list_min_time_milli" />
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="integer" name="config_bluetooth_max_advertisers" />
   <java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
@@ -399,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" />
@@ -782,11 +771,9 @@
   <java-symbol type="string" name="phoneTypeWorkMobile" />
   <java-symbol type="string" name="phoneTypeWorkPager" />
   <java-symbol type="string" name="wfcRegErrorTitle" />
-  <java-symbol type="array" name="wfcOperatorErrorCodes" />
   <java-symbol type="array" name="wfcOperatorErrorAlertMessages" />
   <java-symbol type="array" name="wfcOperatorErrorNotificationMessages" />
-  <java-symbol type="string" name="wfcSpnFormat" />
-  <java-symbol type="string" name="wfcDataSpnFormat" />
+  <java-symbol type="array" name="wfcSpnFormats" />
   <java-symbol type="string" name="wifi_calling_off_summary" />
   <java-symbol type="string" name="wfc_mode_wifi_preferred_summary" />
   <java-symbol type="string" name="wfc_mode_cellular_preferred_summary" />
@@ -1106,7 +1093,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" />
@@ -1126,6 +1112,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" />
@@ -1397,7 +1384,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" />
@@ -2114,6 +2100,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" />
@@ -2210,14 +2197,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" />
@@ -2247,6 +2242,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" />
@@ -2505,6 +2501,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" />
@@ -2534,11 +2532,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_material.xml b/core/res/res/values/themes_material.xml
index 6611eb1..2ea5c5e 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -890,6 +890,11 @@
         <item name="listDivider">@null</item>
 
         <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+
+        <item name="windowFixedWidthMajor">@null</item>
+        <item name="windowFixedWidthMinor">@null</item>
+        <item name="windowFixedHeightMajor">@null</item>
+        <item name="windowFixedHeightMinor">@null</item>
     </style>
 
     <!-- Theme overlay that overrides window properties to display as a date picker dialog. -->
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/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 348f8fd..edb3082 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -419,6 +419,36 @@
                 Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
     }
 
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchUnicodeSpaces() throws Exception {
+        String part1 = "http://and";
+        String part2 = "roid";
+        String[] emptySpaces = new String[]{
+                "\u00A0", // no-break space
+                "\u2000", // en quad
+                "\u2001", // em quad
+                "\u2002", // en space
+                "\u2003", // em space
+                "\u2004", // three-per-em space
+                "\u2005", // four-per-em space
+                "\u2006", // six-per-em space
+                "\u2007", // figure space
+                "\u2008", // punctuation space
+                "\u2009", // thin space
+                "\u200A", // hair space
+                "\u2028", // line separator
+                "\u2029", // paragraph separator
+                "\u202F", // narrow no-break space
+                "\u3000" // ideographic space
+        };
+
+        for (String emptySpace : emptySpaces) {
+            String url = part1 + emptySpace + part2;
+            assertFalse("Should not match empty space - code:" + emptySpace.codePointAt(0),
+                    Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+        }
+    }
+
     // Tests for Patterns.IP_ADDRESS
 
     @SmallTest
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/data/fonts/fonts.xml b/data/fonts/fonts.xml
index bcac6a1..117e17b 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -320,6 +320,7 @@
     </family>
     <family lang="und-Tibt">
         <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansTibetan-Bold.ttf</font>
     </family>
     <family lang="und-Tfng">
         <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
@@ -350,6 +351,9 @@
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
     <family>
+        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
+    </family>
+    <family>
         <font weight="400" style="normal">DroidSansFallback.ttf</font>
     </family>
     <!--
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/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd
index aa1ed0a..026f7a0 100644
--- a/docs/html/training/articles/keystore.jd
+++ b/docs/html/training/articles/keystore.jd
@@ -152,8 +152,10 @@
 
 <p>Generating a new {@link java.security.PrivateKey} requires that
   you also specify the initial X.509 attributes that the self-signed
-  certificate will have. You can replace the certificate at a later
-  time with a certificate signed by a Certificate Authority.</p>
+  certificate will have. You can use
+  {@link java.security.KeyStore#setKeyEntry(String, java.security.Key, char[], java.security.cert.Certificate[]) KeyStore.setKeyEntry}
+  to replace the certificate at a later time with a certificate signed
+  by a Certificate Authority (CA).</p>
 
 <p>To generate the key, use a {@link java.security.KeyPairGenerator}
   with {@link android.security.KeyPairGeneratorSpec}:</p>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 169ef0b..ac2a88a 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -25,14 +25,14 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
 
-import libcore.util.NativeAllocationRegistry;
-
 public final class Bitmap implements Parcelable {
     private static final String TAG = "Bitmap";
 
@@ -614,6 +614,22 @@
     }
 
     /**
+     * Creates a new immutable bitmap backed by ashmem which can efficiently
+     * be passed between processes.
+     *
+     * @hide
+     */
+    public Bitmap createAshmemBitmap(Config config) {
+        checkRecycled("Can't copy a recycled bitmap");
+        Bitmap b = nativeCopyAshmemConfig(mNativePtr, config.nativeInt);
+        if (b != null) {
+            b.setPremultiplied(mRequestPremultiplied);
+            b.mDensity = mDensity;
+        }
+        return b;
+    }
+
+    /**
      * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
      * specified width and height are the same as the current width and height of
      * the source bitmap, the source bitmap is returned and no new bitmap is
@@ -1675,6 +1691,7 @@
     private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
                                             boolean isMutable);
     private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
+    private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
     private static native long nativeGetNativeFinalizer();
     private static native boolean nativeRecycle(long nativeBitmap);
     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
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..7f3a437 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
@@ -119,6 +121,47 @@
  *     whose overall size is modified based on the current level.
  * </ul>
  *
+ * <a name="Custom"></a>
+ * <h3>Custom drawables</h3>
+ *
+ * <p>
+ * All versions of Android allow the Drawable class to be extended and used at
+ * run time in place of framework-provided drawable classes. Starting in
+ * {@link android.os.Build.VERSION_CODES#N API 24}, custom drawables classes
+ * may also be used in XML.
+ * <p>
+ * <strong>Note:</strong> Custom drawable classes are only accessible from
+ * within your application package. Other applications will not be able to load
+ * them.
+ * <p>
+ * At a minimum, custom drawable classes must implement the abstract methods on
+ * Drawable and should override the {@link Drawable#draw(Canvas)} method to
+ * draw content.
+ * <p>
+ * Custom drawables classes may be used in XML in multiple ways:
+ * <ul>
+ *     <li>
+ *         Using the fully-qualified class name as the XML element name. For
+ *         this method, the custom drawable class must be a public top-level
+ *         class.
+ * <pre>
+ * &lt;com.myapp.MyCustomDrawable xmlns:android="http://schemas.android.com/apk/res/android"
+ *     android:color="#ffff0000" /&gt;
+ * </pre>
+ *     </li>
+ *     <li>
+ *         Using <em>drawable</em> as the XML element name and specifying the
+ *         fully-qualified class name from the <em>class</em> attribute. This
+ *         method may be used for both public top-level classes and public
+ *         static inner classes.
+ * <pre>
+ * &lt;drawable xmlns:android="http://schemas.android.com/apk/res/android"
+ *     class="com.myapp.MyTopLevelClass$InnerCustomDrawable"
+ *     android:color="#ffff0000" /&gt;
+ * </pre>
+ *     </li>
+ * </ul>
+ *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about how to use drawables, read the
@@ -148,7 +191,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 +219,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 +231,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 +243,7 @@
      *
      * @return A copy of the drawable's bounds
      */
+    @NonNull
     public final Rect copyBounds() {
         return new Rect(mBounds);
     }
@@ -219,6 +263,7 @@
      * @see #copyBounds()
      * @see #copyBounds(android.graphics.Rect)
      */
+    @NonNull
     public final Rect getBounds() {
         if (mBounds == ZERO_BOUNDS_RECT) {
             mBounds = new Rect();
@@ -237,6 +282,7 @@
      *
      * @return The dirty bounds of this drawable
      */
+    @NonNull
     public Rect getDirtyBounds() {
         return getBounds();
     }
@@ -354,8 +400,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 +412,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 +443,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 +459,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 +473,7 @@
      *         {@link android.view.View#LAYOUT_DIRECTION_RTL}
      * @see #setLayoutDirection(int)
      */
-    public int getLayoutDirection() {
+    public @View.ResolvedLayoutDir int getLayoutDirection() {
         return mLayoutDirection;
     }
 
@@ -441,6 +485,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 +502,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 +515,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 +524,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 +538,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 +641,7 @@
      *
      * @return the current color filter, or {@code null} if none set
      */
-    public ColorFilter getColorFilter() {
+    public @Nullable ColorFilter getColorFilter() {
         return null;
     }
 
@@ -629,7 +678,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 +726,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 +741,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 +758,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 +778,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 +791,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 +888,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 +905,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 +935,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 +948,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 +959,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 +1044,7 @@
      *
      * @hide
      */
-    public Insets getOpticalInsets() {
+    public @NonNull Insets getOpticalInsets() {
         return Insets.NONE;
     }
 
@@ -1020,7 +1078,7 @@
      * @see ConstantState
      * @see #getConstantState()
      */
-    public Drawable mutate() {
+    public @NonNull Drawable mutate() {
         return this;
     }
 
@@ -1126,9 +1184,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 +1281,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 +1314,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 +1328,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 +1346,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 +1361,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 +1385,7 @@
      * @see ConstantState
      * @see Drawable#mutate()
      */
-    public ConstantState getConstantState() {
+    public @Nullable ConstantState getConstantState() {
         return null;
     }
 
@@ -1345,8 +1403,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 +1423,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 +1476,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 +1496,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/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/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 35fe06d..fe68239 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -253,7 +253,9 @@
     path.transform(skTransform, &transformed);
     SkRegion region;
     regionFromPath(transformed, region);
-    clipRegion(region, op);
+    enterRegionMode();
+    mClipRegion.op(region, op);
+    onClipRegionUpdated();
 }
 
 /*
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index e209e2a..aba5d4b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -137,6 +137,9 @@
 
         // whether children with non-zero Z in the chunk should be reordered
         bool reorderChildren;
+#if HWUI_NEW_OPS
+        const ClipBase* reorderClip;
+#endif
     };
 
     DisplayList();
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index f12e523..6fc74a5 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -269,7 +269,8 @@
 }
 
 template <typename V>
-void FrameBuilder::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
+        const V& zTranslatedNodes) {
     const int size = zTranslatedNodes.size();
     if (size == 0
             || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -305,7 +306,7 @@
             // attempt to render the shadow if the caster about to be drawn is its caster,
             // OR if its caster's Z value is similar to the previous potential caster
             if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) {
-                deferShadow(*casterNodeOp);
+                deferShadow(reorderClip, *casterNodeOp);
 
                 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
                 shadowIndex++;
@@ -319,7 +320,7 @@
     }
 }
 
-void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterNodeOp) {
     auto& node = *casterNodeOp.renderNode;
     auto& properties = node.properties();
 
@@ -365,6 +366,10 @@
         casterPath = frameAllocatedPath;
     }
 
+    // apply reorder clip to shadow, so it respects clip at beginning of reorderable chunk
+    int restoreTo = mCanvasState.save(SaveFlags::MatrixClip);
+    mCanvasState.writableSnapshot()->applyClip(reorderClip,
+            *mCanvasState.currentSnapshot()->transform);
     if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) {
         Matrix4 shadowMatrixXY(casterNodeOp.localMatrix);
         Matrix4 shadowMatrixZ(casterNodeOp.localMatrix);
@@ -386,6 +391,7 @@
             currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
         }
     }
+    mCanvasState.restoreToCount(restoreTo);
 }
 
 void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
@@ -438,11 +444,11 @@
 
     // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
     const DisplayList& displayList = *(renderNode.getDisplayList());
-    for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+    for (auto& chunk : displayList.getChunks()) {
         FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
         buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
 
-        defer3dChildren(ChildrenSelectMode::Negative, zTranslatedNodes);
+        defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Negative, zTranslatedNodes);
         for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
             const RecordedOp* op = displayList.getOps()[opIndex];
             receivers[op->opId](*this, *op);
@@ -453,7 +459,7 @@
                 deferProjectedChildren(renderNode);
             }
         }
-        defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes);
+        defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Positive, zTranslatedNodes);
     }
 }
 
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index 039ab6b..a6fd761 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -193,9 +193,10 @@
     void deferNodePropsAndOps(RenderNode& node);
 
     template <typename V>
-    void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
+    void defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
+            const V& zTranslatedNodes);
 
-    void deferShadow(const RenderNodeOp& casterOp);
+    void deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterOp);
 
     void deferProjectedChildren(const RenderNode& renderNode);
 
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/PathCache.cpp b/libs/hwui/PathCache.cpp
index 8f914ac..a8ace8c 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -19,6 +19,7 @@
 #include <SkColor.h>
 #include <SkPaint.h>
 #include <SkPath.h>
+#include <SkPathEffect.h>
 #include <SkRect.h>
 
 #include <utils/JenkinsHash.h>
@@ -35,18 +36,34 @@
 namespace android {
 namespace uirenderer {
 
+template <class T>
+static bool compareWidthHeight(const T& lhs, const T& rhs) {
+    return (lhs.mWidth == rhs.mWidth) && (lhs.mHeight == rhs.mHeight);
+}
+
+static bool compareRoundRects(const PathDescription::Shape::RoundRect& lhs,
+        const PathDescription::Shape::RoundRect& rhs) {
+    return compareWidthHeight(lhs, rhs) && lhs.mRx == rhs.mRx && lhs.mRy == rhs.mRy;
+}
+
+static bool compareArcs(const PathDescription::Shape::Arc& lhs, const PathDescription::Shape::Arc& rhs) {
+    return compareWidthHeight(lhs, rhs) && lhs.mStartAngle == rhs.mStartAngle &&
+            lhs.mSweepAngle == rhs.mSweepAngle && lhs.mUseCenter == rhs.mUseCenter;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Cache entries
 ///////////////////////////////////////////////////////////////////////////////
 
 PathDescription::PathDescription()
-        : type(kShapeNone)
+        : type(ShapeType::None)
         , join(SkPaint::kDefault_Join)
         , cap(SkPaint::kDefault_Cap)
         , style(SkPaint::kFill_Style)
         , miter(4.0f)
         , strokeWidth(1.0f)
         , pathEffect(nullptr) {
+    // Shape bits should be set to zeroes, because they are used for hash calculation.
     memset(&shape, 0, sizeof(Shape));
 }
 
@@ -58,11 +75,12 @@
         , miter(paint->getStrokeMiter())
         , strokeWidth(paint->getStrokeWidth())
         , pathEffect(paint->getPathEffect()) {
+    // Shape bits should be set to zeroes, because they are used for hash calculation.
     memset(&shape, 0, sizeof(Shape));
 }
 
 hash_t PathDescription::hash() const {
-    uint32_t hash = JenkinsHashMix(0, type);
+    uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
     hash = JenkinsHashMix(hash, join);
     hash = JenkinsHashMix(hash, cap);
     hash = JenkinsHashMix(hash, style);
@@ -73,6 +91,32 @@
     return JenkinsHashWhiten(hash);
 }
 
+bool PathDescription::operator==(const PathDescription& rhs) const {
+    if (type != rhs.type) return false;
+    if (join != rhs.join) return false;
+    if (cap != rhs.cap) return false;
+    if (style != rhs.style) return false;
+    if (miter != rhs.miter) return false;
+    if (strokeWidth != rhs.strokeWidth) return false;
+    if (pathEffect != rhs.pathEffect) return false;
+    switch (type) {
+        case ShapeType::None:
+            return 0;
+        case ShapeType::Rect:
+            return compareWidthHeight(shape.rect, rhs.shape.rect);
+        case ShapeType::RoundRect:
+            return compareRoundRects(shape.roundRect, rhs.shape.roundRect);
+        case ShapeType::Circle:
+            return shape.circle.mRadius == rhs.shape.circle.mRadius;
+        case ShapeType::Oval:
+            return compareWidthHeight(shape.oval, rhs.shape.oval);
+        case ShapeType::Arc:
+            return compareArcs(shape.arc, rhs.shape.arc);
+        case ShapeType::Path:
+            return shape.path.mGenerationID == rhs.shape.path.mGenerationID;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Utilities
 ///////////////////////////////////////////////////////////////////////////////
@@ -322,7 +366,7 @@
             LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
             while (iter.next()) {
                 const PathDescription& key = iter.key();
-                if (key.type == kShapePath && key.shape.path.mGenerationID == generationID) {
+                if (key.type == ShapeType::Path && key.shape.path.mGenerationID == generationID) {
                     pathsToRemove.push(key);
                 }
             }
@@ -336,7 +380,7 @@
 }
 
 PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
-    PathDescription entry(kShapePath, paint);
+    PathDescription entry(ShapeType::Path, paint);
     entry.shape.path.mGenerationID = path->getGenerationID();
 
     PathTexture* texture = mCache.get(entry);
@@ -366,9 +410,8 @@
     return texture;
 }
 
-void PathCache::remove(const SkPath* path, const SkPaint* paint)
-{
-    PathDescription entry(kShapePath, paint);
+void PathCache::remove(const SkPath* path, const SkPaint* paint) {
+    PathDescription entry(ShapeType::Path, paint);
     entry.shape.path.mGenerationID = path->getGenerationID();
     mCache.remove(entry);
 }
@@ -378,7 +421,7 @@
         return;
     }
 
-    PathDescription entry(kShapePath, paint);
+    PathDescription entry(ShapeType::Path, paint);
     entry.shape.path.mGenerationID = path->getGenerationID();
 
     PathTexture* texture = mCache.get(entry);
@@ -417,7 +460,7 @@
 
 PathTexture* PathCache::getRoundRect(float width, float height,
         float rx, float ry, const SkPaint* paint) {
-    PathDescription entry(kShapeRoundRect, paint);
+    PathDescription entry(ShapeType::RoundRect, paint);
     entry.shape.roundRect.mWidth = width;
     entry.shape.roundRect.mHeight = height;
     entry.shape.roundRect.mRx = rx;
@@ -442,7 +485,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) {
-    PathDescription entry(kShapeCircle, paint);
+    PathDescription entry(ShapeType::Circle, paint);
     entry.shape.circle.mRadius = radius;
 
     PathTexture* texture = get(entry);
@@ -462,7 +505,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) {
-    PathDescription entry(kShapeOval, paint);
+    PathDescription entry(ShapeType::Oval, paint);
     entry.shape.oval.mWidth = width;
     entry.shape.oval.mHeight = height;
 
@@ -485,7 +528,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) {
-    PathDescription entry(kShapeRect, paint);
+    PathDescription entry(ShapeType::Rect, paint);
     entry.shape.rect.mWidth = width;
     entry.shape.rect.mHeight = height;
 
@@ -509,7 +552,7 @@
 
 PathTexture* PathCache::getArc(float width, float height,
         float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
-    PathDescription entry(kShapeArc, paint);
+    PathDescription entry(ShapeType::Arc, paint);
     entry.shape.arc.mWidth = width;
     entry.shape.arc.mHeight = height;
     entry.shape.arc.mStartAngle = startAngle;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index d2633aa..6368ddd 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -25,6 +25,7 @@
 #include "utils/Pair.h"
 
 #include <GLES2/gl2.h>
+#include <SkPaint.h>
 #include <SkPath.h>
 #include <utils/LruCache.h>
 #include <utils/Mutex.h>
@@ -108,18 +109,18 @@
     sp<Task<SkBitmap*> > mTask;
 }; // struct PathTexture
 
-enum ShapeType {
-    kShapeNone,
-    kShapeRect,
-    kShapeRoundRect,
-    kShapeCircle,
-    kShapeOval,
-    kShapeArc,
-    kShapePath
+enum class ShapeType {
+    None,
+    Rect,
+    RoundRect,
+    Circle,
+    Oval,
+    Arc,
+    Path
 };
 
 struct PathDescription {
-    DESCRIPTION_TYPE(PathDescription);
+    HASHABLE_TYPE(PathDescription);
     ShapeType type;
     SkPaint::Join join;
     SkPaint::Cap cap;
@@ -159,8 +160,6 @@
 
     PathDescription();
     PathDescription(ShapeType shapeType, const SkPaint* paint);
-
-    hash_t hash() const;
 };
 
 /**
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%
copy from telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
copy 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/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index b78497d..ab733f1 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -57,6 +57,16 @@
     return displayList;
 }
 
+void RecordingCanvas::insertReorderBarrier(bool enableReorder) {
+    if (enableReorder) {
+        mDeferredBarrierType = DeferredBarrierType::OutOfOrder;
+        mDeferredBarrierClip = getRecordedClip();
+    } else {
+        mDeferredBarrierType = DeferredBarrierType::InOrder;
+        mDeferredBarrierClip = nullptr;
+    }
+}
+
 SkCanvas* RecordingCanvas::asSkCanvas() {
     LOG_ALWAYS_FATAL_IF(!mDisplayList,
             "attempting to get an SkCanvas when we are not recording!");
@@ -610,6 +620,7 @@
         newChunk.beginOpIndex = insertIndex;
         newChunk.endOpIndex = insertIndex + 1;
         newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
+        newChunk.reorderClip = mDeferredBarrierClip;
 
         int nextChildIndex = mDisplayList->children.size();
         newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index acb88e2..219296c 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -55,10 +55,7 @@
 // ----------------------------------------------------------------------------
 // MISC HWUI OPERATIONS - TODO: CATEGORIZE
 // ----------------------------------------------------------------------------
-    virtual void insertReorderBarrier(bool enableReorder) override {
-        mDeferredBarrierType = enableReorder
-                ? DeferredBarrierType::OutOfOrder : DeferredBarrierType::InOrder;
-    }
+    virtual void insertReorderBarrier(bool enableReorder) override;
 
     virtual void drawLayer(DeferredLayerUpdater* layerHandle) override;
     virtual void drawRenderNode(RenderNode* renderNode) override;
@@ -312,6 +309,7 @@
     std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
     ResourceCache& mResourceCache;
     DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None;
+    const ClipBase* mDeferredBarrierClip = nullptr;
     DisplayList* mDisplayList = nullptr;
     bool mHighContrastText = false;
     SkAutoTUnref<SkDrawFilter> mDrawFilter;
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 14c8f39..cfdc084 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -35,13 +35,14 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 TessellationCache::Description::Description()
-        : type(kNone)
+        : type(Type::None)
         , scaleX(1.0f)
         , scaleY(1.0f)
         , aa(false)
         , cap(SkPaint::kDefault_Cap)
         , style(SkPaint::kFill_Style)
         , strokeWidth(1.0f) {
+    // Shape bits should be set to zeroes, because they are used for hash calculation.
     memset(&shape, 0, sizeof(Shape));
 }
 
@@ -52,11 +53,30 @@
         , style(paint.getStyle())
         , strokeWidth(paint.getStrokeWidth()) {
     PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
+    // Shape bits should be set to zeroes, because they are used for hash calculation.
     memset(&shape, 0, sizeof(Shape));
 }
 
+bool TessellationCache::Description::operator==(const TessellationCache::Description& rhs) const {
+    if (type != rhs.type) return false;
+    if (scaleX != rhs.scaleX) return false;
+    if (scaleY != rhs.scaleY) return false;
+    if (aa != rhs.aa) return false;
+    if (cap != rhs.cap) return false;
+    if (style != rhs.style) return false;
+    if (strokeWidth != rhs.strokeWidth) return false;
+    if (type == Type::None) return true;
+    const Shape::RoundRect& lRect = shape.roundRect;
+    const Shape::RoundRect& rRect = rhs.shape.roundRect;
+
+    if (lRect.width != rRect.width) return false;
+    if (lRect.height != rRect.height) return false;
+    if (lRect.rx != rRect.rx) return false;
+    return lRect.ry == rRect.ry;
+}
+
 hash_t TessellationCache::Description::hash() const {
-    uint32_t hash = JenkinsHashMix(0, type);
+    uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
     hash = JenkinsHashMix(hash, aa);
     hash = JenkinsHashMix(hash, cap);
     hash = JenkinsHashMix(hash, style);
@@ -77,17 +97,23 @@
 
 TessellationCache::ShadowDescription::ShadowDescription()
         : nodeKey(nullptr) {
-    memset(&matrixData, 0, 16 * sizeof(float));
+    memset(&matrixData, 0, sizeof(matrixData));
 }
 
-TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
+TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform)
         : nodeKey(nodeKey) {
-    memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
+    memcpy(&matrixData, drawTransform->data, sizeof(matrixData));
+}
+
+bool TessellationCache::ShadowDescription::operator==(
+        const TessellationCache::ShadowDescription& rhs) const {
+    return nodeKey == rhs.nodeKey
+            && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0;
 }
 
 hash_t TessellationCache::ShadowDescription::hash() const {
     uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
-    hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float));
+    hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, sizeof(matrixData));
     return JenkinsHashWhiten(hash);
 }
 
@@ -428,7 +454,7 @@
 TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
         const Matrix4& transform, const SkPaint& paint,
         float width, float height, float rx, float ry) {
-    Description entry(Description::kRoundRect, transform, paint);
+    Description entry(Description::Type::RoundRect, transform, paint);
     entry.shape.roundRect.width = width;
     entry.shape.roundRect.height = height;
     entry.shape.roundRect.rx = rx;
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index 0bd6365..6141b4e 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -52,10 +52,10 @@
     typedef Pair<VertexBuffer*, VertexBuffer*> vertexBuffer_pair_t;
 
     struct Description {
-        DESCRIPTION_TYPE(Description);
-        enum Type {
-            kNone,
-            kRoundRect,
+        HASHABLE_TYPE(Description);
+        enum class Type {
+            None,
+            RoundRect,
         };
 
         Type type;
@@ -76,18 +76,16 @@
 
         Description();
         Description(Type type, const Matrix4& transform, const SkPaint& paint);
-        hash_t hash() const;
         void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
     };
 
     struct ShadowDescription {
-        DESCRIPTION_TYPE(ShadowDescription);
-        const void* nodeKey;
+        HASHABLE_TYPE(ShadowDescription);
+        const SkPath* nodeKey;
         float matrixData[16];
 
         ShadowDescription();
-        ShadowDescription(const void* nodeKey, const Matrix4* drawTransform);
-        hash_t hash() const;
+        ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform);
     };
 
     class ShadowTask : public Task<vertexBuffer_pair_t> {
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/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index b39de26..a455f57 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -24,7 +24,8 @@
 
 MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
         int ttcIndex) :
-    mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) {
+    MinikinFont(typeface->uniqueID()), mTypeface(typeface), mFontData(fontData),
+    mFontSize(fontSize), mTtcIndex(ttcIndex) {
 }
 
 MinikinFontSkia::~MinikinFontSkia() {
@@ -99,10 +100,6 @@
     return mTtcIndex;
 }
 
-int32_t MinikinFontSkia::GetUniqueId() const {
-    return mTypeface->uniqueID();
-}
-
 uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
     uint32_t flags = paint->getFlags();
     SkPaint::Hinting hinting = paint->getHinting();
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index dbc65f9..a7c9fb0 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -41,8 +41,6 @@
 
     const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
 
-    int32_t GetUniqueId() const;
-
     SkTypeface* GetSkTypeface() const;
 
     // Access to underlying raw font bytes
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c5a2dc7..5e37856 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"
@@ -616,6 +617,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 32d3283..c39319d 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -127,6 +127,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/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index b864703..54ca68d 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -132,6 +132,7 @@
         auto serializedClip = area.serializeClip(allocator);
         ASSERT_NE(nullptr, serializedClip);
         ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode);
+        ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
         EXPECT_EQ(Rect(200, 200), serializedClip->rect);
         EXPECT_EQ(serializedClip, area.serializeClip(allocator))
                 << "Requery of clip on unmodified ClipArea must return same pointer.";
@@ -145,6 +146,7 @@
         auto serializedClip = area.serializeClip(allocator);
         ASSERT_NE(nullptr, serializedClip);
         ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
+        ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
         auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
         EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
         EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect);
@@ -160,6 +162,7 @@
         auto serializedClip = area.serializeClip(allocator);
         ASSERT_NE(nullptr, serializedClip);
         ASSERT_EQ(ClipMode::Region, serializedClip->mode);
+        ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot";
         auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip);
         EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds())
                 << "Clip region should be 200x200";
@@ -169,6 +172,18 @@
     }
 }
 
+TEST(ClipArea, serializeClip_pathIntersectWithRoot) {
+    ClipArea area(createClipArea());
+    LinearAllocator allocator;
+    SkPath circlePath;
+    circlePath.addCircle(100, 100, 100);
+    area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op);
+
+    auto serializedClip = area.serializeClip(allocator);
+    ASSERT_NE(nullptr, serializedClip);
+    EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
+}
+
 TEST(ClipArea, serializeIntersectedClip) {
     ClipArea area(createClipArea());
     LinearAllocator allocator;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 9877439..209a104 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -1722,6 +1722,35 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
+RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
+    class ShadowClippingTestRenderer : public TestRendererBase {
+    public:
+        void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
+                    << "Shadow must respect pre-barrier canvas clip value.";
+        }
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(1, mIndex++);
+        }
+    };
+    auto parent = TestUtils::createNode(0, 0, 100, 100,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        // Apply a clip before the reorder barrier/shadow casting child is drawn.
+        // This clip must be applied to the shadow cast by the child.
+        canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
+        canvas.insertReorderBarrier(true);
+        canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+    });
+
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+            TestUtils::createSyncedNodeList(parent),
+            (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
+    ShadowClippingTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(2, renderer.getIndex());
+}
+
 static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
         std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
     class PropertyTestRenderer : public TestRendererBase {
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index c49ff71..18171de 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -603,6 +603,36 @@
     EXPECT_TRUE(chunks[1].reorderChildren);
 }
 
+TEST(RecordingCanvas, insertReorderBarrier_clip) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        // first chunk: no recorded clip
+        canvas.insertReorderBarrier(true);
+        canvas.drawRect(0, 0, 400, 400, SkPaint());
+
+        // second chunk: no recorded clip, since inorder region
+        canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+        canvas.insertReorderBarrier(false);
+        canvas.drawRect(0, 0, 400, 400, SkPaint());
+
+        // third chunk: recorded clip
+        canvas.insertReorderBarrier(true);
+        canvas.drawRect(0, 0, 400, 400, SkPaint());
+    });
+
+    auto chunks = dl->getChunks();
+    ASSERT_EQ(3u, chunks.size());
+
+    EXPECT_TRUE(chunks[0].reorderChildren);
+    EXPECT_EQ(nullptr, chunks[0].reorderClip);
+
+    EXPECT_FALSE(chunks[1].reorderChildren);
+    EXPECT_EQ(nullptr, chunks[1].reorderClip);
+
+    EXPECT_TRUE(chunks[2].reorderChildren);
+    ASSERT_NE(nullptr, chunks[2].reorderClip);
+    EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
+}
+
 TEST(RecordingCanvas, refPaint) {
     SkPaint paint;
 
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/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index ccf2287..7212897b 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -23,12 +23,10 @@
         Type(const Type&) = delete; \
         void operator=(const Type&) = delete
 
-#define DESCRIPTION_TYPE(Type) \
-        int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
-        bool operator==(const Type& other) const { return compare(other) == 0; } \
-        bool operator!=(const Type& other) const { return compare(other) != 0; } \
-        friend inline int strictly_order_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs) < 0; } \
-        friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
+#define HASHABLE_TYPE(Type) \
+        bool operator==(const Type& other) const; \
+        hash_t hash() const; \
+        bool operator!=(const Type& other) const { return !(*this == other); } \
         friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
 
 #define REQUIRE_COMPATIBLE_LAYOUT(Type) \
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index ca306cc..4504e69 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -37,6 +37,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 /**
  * The AudioRecord class manages the audio resources for Java applications
  * to record audio from the audio input hardware of the platform. This is
@@ -874,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);
      }
 
     /**
@@ -1320,6 +1324,7 @@
      * Note: The query is only valid if the AudioRecord is currently recording. If it is not,
      * <code>getRoutedDevice()</code> will return null.
      */
+    @Override
     public AudioDeviceInfo getRoutedDevice() {
         int deviceId = native_getRoutedDeviceId();
         if (deviceId == 0) {
@@ -1338,8 +1343,8 @@
     /*
      * Call BEFORE adding a routing callback handler.
      */
-    private void testEnableNativeRoutingCallbacks() {
-        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+    private void testEnableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback();
         }
     }
@@ -1347,24 +1352,23 @@
     /*
      * Call AFTER removing a routing callback handler.
      */
-    private void testDisableNativeRoutingCallbacks() {
-        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+    private void testDisableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0) {
             native_disableDeviceCallback();
         }
     }
 
     //--------------------------------------------------------------------------
-    // >= "N" (Re)Routing Info
+    // (Re)Routing Info
     //--------------------
     /**
      * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
-     * {@link AudioRecord#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
-     *      android.os.Handler)}
-     * by an app to receive (re)routing notifications.
+     * {@link AudioRecord#addOnRoutingChangedListener} by an app to receive
+     * (re)routing notifications.
      */
-    private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
-    mNewRoutingChangeListeners =
-        new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+    @GuardedBy("mRoutingChangeListeners")
+    private ArrayMap<AudioRouting.OnRoutingChangedListener,
+            NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
 
     /**
      * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of
@@ -1375,14 +1379,15 @@
      * the callback. If <code>null</code>, the {@link Handler} associated with the main
      * {@link Looper} will be used.
      */
-    public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+    @Override
+    public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
             android.os.Handler handler) {
-        if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
-            synchronized (mNewRoutingChangeListeners) {
-                testEnableNativeRoutingCallbacks();
-                mNewRoutingChangeListeners.put(
-                    listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
-                            handler != null ? handler : new Handler(mInitializationLooper)));
+        synchronized (mRoutingChangeListeners) {
+            if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+                testEnableNativeRoutingCallbacksLocked();
+                mRoutingChangeListeners.put(
+                        listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                                handler != null ? handler : new Handler(mInitializationLooper)));
             }
         }
     }
@@ -1393,39 +1398,42 @@
     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
     * to remove.
     */
-    public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
-        synchronized (mNewRoutingChangeListeners) {
-            if (mNewRoutingChangeListeners.containsKey(listener)) {
-                mNewRoutingChangeListeners.remove(listener);
-                testDisableNativeRoutingCallbacks();
+    @Override
+    public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
+        synchronized (mRoutingChangeListeners) {
+            if (mRoutingChangeListeners.containsKey(listener)) {
+                mRoutingChangeListeners.remove(listener);
+                testDisableNativeRoutingCallbacksLocked();
             }
         }
     }
 
     //--------------------------------------------------------------------------
-    // Marshmallow (Re)Routing Info
+    // (Re)Routing Info
     //--------------------
     /**
-     * Defines the interface by which applications can receive notifications of routing
-     * changes for the associated {@link AudioRecord}.
+     * Defines the interface by which applications can receive notifications of
+     * routing changes for the associated {@link AudioRecord}.
+     *
+     * @deprecated users should switch to the general purpose
+     *             {@link AudioRouting.OnRoutingChangedListener} class instead.
      */
-    public interface OnRoutingChangedListener {
+    @Deprecated
+    public interface OnRoutingChangedListener extends AudioRouting.OnRoutingChangedListener {
         /**
-         * Called when the routing of an AudioRecord changes from either and explicit or
-         * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
-         * device.
+         * Called when the routing of an AudioRecord changes from either and
+         * explicit or policy rerouting. Use {@link #getRoutedDevice()} to
+         * retrieve the newly routed-from device.
          */
         public void onRoutingChanged(AudioRecord audioRecord);
-    }
 
-    /**
-     * The list of AudioRecord.OnRoutingChangedListener interface added (with
-     * {@link AudioRecord#addOnRoutingChangedListener(OnRoutingChangedListener,android.os.Handler)}
-     * by an app to receive (re)routing notifications.
-     */
-    private ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>
-        mRoutingChangeListeners =
-            new ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>();
+        @Override
+        default public void onRoutingChanged(AudioRouting router) {
+            if (router instanceof AudioRecord) {
+                onRoutingChanged((AudioRecord) router);
+            }
+        }
+    }
 
     /**
      * Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
@@ -1435,88 +1443,28 @@
      * @param handler  Specifies the {@link Handler} object for the thread on which to execute
      * the callback. If <code>null</code>, the {@link Handler} associated with the main
      * {@link Looper} will be used.
+     * @deprecated users should switch to the general purpose
+     *             {@link AudioRouting.OnRoutingChangedListener} class instead.
      */
     @Deprecated
     public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
             android.os.Handler handler) {
-        if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
-            synchronized (mRoutingChangeListeners) {
-                testEnableNativeRoutingCallbacks();
-                mRoutingChangeListeners.put(
-                    listener, new NativeRoutingEventHandlerDelegate(this, listener,
-                            handler != null ? handler : new Handler(mInitializationLooper)));
-            }
-        }
+        addOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener, handler);
     }
 
     /**
       * Removes an {@link OnRoutingChangedListener} which has been previously added
      * to receive rerouting notifications.
      * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
+     * @deprecated users should switch to the general purpose
+     *             {@link AudioRouting.OnRoutingChangedListener} class instead.
      */
     @Deprecated
     public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
-        synchronized (mRoutingChangeListeners) {
-            if (mRoutingChangeListeners.containsKey(listener)) {
-                mRoutingChangeListeners.remove(listener);
-                testDisableNativeRoutingCallbacks();
-            }
-        }
+        removeOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener);
     }
 
     /**
-     * >= "N" Routing
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeNewRoutingEventHandlerDelegate {
-        private final Handler mHandler;
-
-        NativeNewRoutingEventHandlerDelegate(final AudioRecord record,
-                                   final AudioRouting.OnRoutingChangedListener listener,
-                                   Handler handler) {
-            // find the looper for our new event handler
-            Looper looper;
-            if (handler != null) {
-                looper = handler.getLooper();
-            } else {
-                // no given handler, use the looper the AudioRecord was created in
-                looper = mInitializationLooper;
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (record == null) {
-                            return;
-                        }
-                        switch(msg.what) {
-                        case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
-                            if (listener != null) {
-                                listener.onRoutingChanged(record);
-                            }
-                            break;
-                        default:
-                            loge("Unknown native event type: " + msg.what);
-                            break;
-                        }
-                    }
-                };
-            } else {
-                mHandler = null;
-            }
-        }
-
-        Handler getHandler() {
-            return mHandler;
-        }
-    }
-
-    /**
-     * Marshmallow Routing
      * Helper class to handle the forwarding of native events to the appropriate listener
      * (potentially) handled in a different thread
      */
@@ -1524,7 +1472,7 @@
         private final Handler mHandler;
 
         NativeRoutingEventHandlerDelegate(final AudioRecord record,
-                                   final OnRoutingChangedListener listener,
+                                   final AudioRouting.OnRoutingChangedListener listener,
                                    Handler handler) {
             // find the looper for our new event handler
             Looper looper;
@@ -1571,26 +1519,12 @@
      */
     private void broadcastRoutingChange() {
         AudioManager.resetAudioPortGeneration();
-        // Marshmallow Routing
-        Collection<NativeRoutingEventHandlerDelegate> values;
         synchronized (mRoutingChangeListeners) {
-            values = mRoutingChangeListeners.values();
-        }
-        for(NativeRoutingEventHandlerDelegate delegate : values) {
-            Handler handler = delegate.getHandler();
-            if (handler != null) {
-                handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
-            }
-        }
-        // >= "N" Routing
-        Collection<NativeNewRoutingEventHandlerDelegate> newValues;
-        synchronized (mNewRoutingChangeListeners) {
-            newValues = mNewRoutingChangeListeners.values();
-        }
-        for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
-            Handler handler = delegate.getHandler();
-            if (handler != null) {
-                handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+            for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
+                Handler handler = delegate.getHandler();
+                if (handler != null) {
+                    handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+                }
             }
         }
     }
@@ -1623,6 +1557,7 @@
      * @return true if successful, false if the specified {@link AudioDeviceInfo} is non-null and
      * does not correspond to a valid audio input device.
      */
+    @Override
     public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
         // Do some validation....
         if (deviceInfo != null && !deviceInfo.isSource()) {
@@ -1643,6 +1578,7 @@
      * Returns the selected input specified by {@link #setPreferredDevice}. Note that this
      * is not guarenteed to correspond to the actual device being used for recording.
      */
+    @Override
     public AudioDeviceInfo getPreferredDevice() {
         synchronized (this) {
             return mPreferredDevice;
@@ -1683,7 +1619,6 @@
      * (potentially) handled in a different thread
      */
     private class NativeEventHandler extends Handler {
-
         private final AudioRecord mAudioRecord;
 
         NativeEventHandler(AudioRecord recorder, Looper looper) {
@@ -1714,8 +1649,7 @@
                 break;
             }
         }
-    };
-
+    }
 
     //---------------------------------------------------------
     // Java methods called from the native side
@@ -1793,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/AudioRouting.java b/media/java/android/media/AudioRouting.java
index 41f92d4..26fa631 100644
--- a/media/java/android/media/AudioRouting.java
+++ b/media/java/android/media/AudioRouting.java
@@ -57,7 +57,7 @@
      * the callback. If <code>null</code>, the {@link Handler} associated with the main
      * {@link Looper} will be used.
      */
-    public void addOnRoutingListener(OnRoutingChangedListener listener,
+    public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
             Handler handler);
 
     /**
@@ -66,7 +66,7 @@
      * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
      * to remove.
      */
-    public void removeOnRoutingListener(OnRoutingChangedListener listener);
+    public void removeOnRoutingChangedListener(OnRoutingChangedListener listener);
 
     /**
      * Defines the interface by which applications can receive notifications of routing
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 621129d..9d360db 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -40,6 +40,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
 
 /**
@@ -1489,6 +1490,7 @@
      * @deprecated Applications should use {@link #setVolume} instead, as it
      * more gracefully scales down to mono, and up to multi-channel content beyond stereo.
      */
+    @Deprecated
     public int setStereoVolume(float leftGain, float rightGain) {
         if (isRestricted()) {
             return SUCCESS;
@@ -2397,6 +2399,7 @@
      * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
      * does not correspond to a valid audio output device.
      */
+    @Override
     public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
         // Do some validation....
         if (deviceInfo != null && !deviceInfo.isSink()) {
@@ -2416,6 +2419,7 @@
      * Returns the selected output specified by {@link #setPreferredDevice}. Note that this
      * is not guaranteed to correspond to the actual device being used for playback.
      */
+    @Override
     public AudioDeviceInfo getPreferredDevice() {
         synchronized (this) {
             return mPreferredDevice;
@@ -2427,6 +2431,7 @@
      * Note: The query is only valid if the AudioTrack is currently playing. If it is not,
      * <code>getRoutedDevice()</code> will return null.
      */
+    @Override
     public AudioDeviceInfo getRoutedDevice() {
         int deviceId = native_getRoutedDeviceId();
         if (deviceId == 0) {
@@ -2445,8 +2450,8 @@
     /*
      * Call BEFORE adding a routing callback handler.
      */
-    private void testEnableNativeRoutingCallbacks() {
-        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+    private void testEnableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback();
         }
     }
@@ -2454,24 +2459,23 @@
     /*
      * Call AFTER removing a routing callback handler.
      */
-    private void testDisableNativeRoutingCallbacks() {
-        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+    private void testDisableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0) {
             native_disableDeviceCallback();
         }
     }
 
     //--------------------------------------------------------------------------
-    // >= "N" (Re)Routing Info
+    // (Re)Routing Info
     //--------------------
     /**
      * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
-     * {@link AudioTrack#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
-     *          android.os.Handler)}
-     * by an app to receive (re)routing notifications.
+     * {@link AudioRecord#addOnRoutingChangedListener} by an app to receive
+     * (re)routing notifications.
      */
-   private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
-    mNewRoutingChangeListeners =
-        new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+    @GuardedBy("mRoutingChangeListeners")
+    private ArrayMap<AudioRouting.OnRoutingChangedListener,
+            NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
 
    /**
     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
@@ -2482,14 +2486,15 @@
     * the callback. If <code>null</code>, the {@link Handler} associated with the main
     * {@link Looper} will be used.
     */
-    public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+    @Override
+    public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
             Handler handler) {
-        if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
-            synchronized (mNewRoutingChangeListeners) {
-                testEnableNativeRoutingCallbacks();
-                mNewRoutingChangeListeners.put(
-                    listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
-                            handler != null ? handler : new Handler(mInitializationLooper)));
+        synchronized (mRoutingChangeListeners) {
+            if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+                testEnableNativeRoutingCallbacksLocked();
+                mRoutingChangeListeners.put(
+                        listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                                handler != null ? handler : new Handler(mInitializationLooper)));
             }
         }
     }
@@ -2500,39 +2505,42 @@
      * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
      * to remove.
      */
-    public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
-        if (mNewRoutingChangeListeners.containsKey(listener)) {
-            mNewRoutingChangeListeners.remove(listener);
+    @Override
+    public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
+        synchronized (mRoutingChangeListeners) {
+            if (mRoutingChangeListeners.containsKey(listener)) {
+                mRoutingChangeListeners.remove(listener);
+            }
+            testDisableNativeRoutingCallbacksLocked();
         }
-        testDisableNativeRoutingCallbacks();
     }
 
     //--------------------------------------------------------------------------
-    // Marshmallow (Re)Routing Info
+    // (Re)Routing Info
     //--------------------
     /**
-     * Defines the interface by which applications can receive notifications of routing
-     * changes for the associated {@link AudioTrack}.
+     * Defines the interface by which applications can receive notifications of
+     * routing changes for the associated {@link AudioTrack}.
+     *
+     * @deprecated users should switch to the general purpose
+     *             {@link AudioRouting.OnRoutingChangedListener} class instead.
      */
     @Deprecated
-    public interface OnRoutingChangedListener {
+    public interface OnRoutingChangedListener extends AudioRouting.OnRoutingChangedListener {
         /**
-         * Called when the routing of an AudioTrack changes from either and explicit or
-         * policy rerouting.  Use {@link #getRoutedDevice()} to retrieve the newly routed-to
-         * device.
+         * Called when the routing of an AudioTrack changes from either and
+         * explicit or policy rerouting. Use {@link #getRoutedDevice()} to
+         * retrieve the newly routed-to device.
          */
-        @Deprecated
         public void onRoutingChanged(AudioTrack audioTrack);
-    }
 
-    /**
-     * The list of AudioTrack.OnRoutingChangedListener interfaces added (with
-     * {@link AudioTrack#addOnRoutingChangedListener(OnRoutingChangedListener, android.os.Handler)}
-     * by an app to receive (re)routing notifications.
-     */
-    private ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>
-        mRoutingChangeListeners =
-            new ArrayMap<OnRoutingChangedListener, NativeRoutingEventHandlerDelegate>();
+        @Override
+        default public void onRoutingChanged(AudioRouting router) {
+            if (router instanceof AudioTrack) {
+                onRoutingChanged((AudioTrack) router);
+            }
+        }
+    }
 
     /**
      * Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
@@ -2542,33 +2550,25 @@
      * @param handler  Specifies the {@link Handler} object for the thread on which to execute
      * the callback. If <code>null</code>, the {@link Handler} associated with the main
      * {@link Looper} will be used.
+     * @deprecated users should switch to the general purpose
+     *             {@link AudioRouting.OnRoutingChangedListener} class instead.
      */
     @Deprecated
     public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
             android.os.Handler handler) {
-        if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
-            synchronized (mRoutingChangeListeners) {
-                testEnableNativeRoutingCallbacks();
-                mRoutingChangeListeners.put(
-                    listener, new NativeRoutingEventHandlerDelegate(this, listener,
-                            handler != null ? handler : new Handler(mInitializationLooper)));
-            }
-        }
+        addOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener, handler);
     }
 
     /**
      * Removes an {@link OnRoutingChangedListener} which has been previously added
      * to receive rerouting notifications.
      * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
+     * @deprecated users should switch to the general purpose
+     *             {@link AudioRouting.OnRoutingChangedListener} class instead.
      */
     @Deprecated
     public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
-        synchronized (mRoutingChangeListeners) {
-            if (mRoutingChangeListeners.containsKey(listener)) {
-                mRoutingChangeListeners.remove(listener);
-            }
-            testDisableNativeRoutingCallbacks();
-        }
+        removeOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener);
     }
 
     /**
@@ -2576,27 +2576,12 @@
      */
     private void broadcastRoutingChange() {
         AudioManager.resetAudioPortGeneration();
-
-        // Marshmallow Routing
-        Collection<NativeRoutingEventHandlerDelegate> values;
         synchronized (mRoutingChangeListeners) {
-            values = mRoutingChangeListeners.values();
-        }
-        for(NativeRoutingEventHandlerDelegate delegate : values) {
-            Handler handler = delegate.getHandler();
-            if (handler != null) {
-                handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
-            }
-        }
-        // >= "N" Routing
-        Collection<NativeNewRoutingEventHandlerDelegate> newValues;
-        synchronized (mNewRoutingChangeListeners) {
-            newValues = mNewRoutingChangeListeners.values();
-        }
-        for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
-            Handler handler = delegate.getHandler();
-            if (handler != null) {
-                handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+            for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
+                Handler handler = delegate.getHandler();
+                if (handler != null) {
+                    handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+                }
             }
         }
     }
@@ -2681,7 +2666,6 @@
     }
 
     /**
-     * Marshmallow Routing API.
      * Helper class to handle the forwarding of native events to the appropriate listener
      * (potentially) handled in a different thread
      */
@@ -2689,57 +2673,6 @@
         private final Handler mHandler;
 
         NativeRoutingEventHandlerDelegate(final AudioTrack track,
-                                   final OnRoutingChangedListener listener,
-                                   Handler handler) {
-            // find the looper for our new event handler
-            Looper looper;
-            if (handler != null) {
-                looper = handler.getLooper();
-            } else {
-                // no given handler, use the looper the AudioTrack was created in
-                looper = mInitializationLooper;
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        if (track == null) {
-                            return;
-                        }
-                        switch(msg.what) {
-                        case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
-                            if (listener != null) {
-                                listener.onRoutingChanged(track);
-                            }
-                            break;
-                        default:
-                            loge("Unknown native event type: " + msg.what);
-                            break;
-                        }
-                    }
-                };
-            } else {
-                mHandler = null;
-            }
-        }
-
-        Handler getHandler() {
-            return mHandler;
-        }
-    }
-
-    /**
-     * Marshmallow Routing API.
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeNewRoutingEventHandlerDelegate {
-        private final Handler mHandler;
-
-        NativeNewRoutingEventHandlerDelegate(final AudioTrack track,
                                    final AudioRouting.OnRoutingChangedListener listener,
                                    Handler handler) {
             // find the looper for our new event handler
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 2bd9781..7af9c24 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2611,10 +2611,13 @@
         public static final int VP8ProfileMain = 0x01;
 
         // from OMX_VIDEO_VP9PROFILETYPE
-        public static final int VP9Profile0 = 0x00;
-        public static final int VP9Profile1 = 0x01;
-        public static final int VP9Profile2 = 0x02;
-        public static final int VP9Profile3 = 0x03;
+        public static final int VP9Profile0 = 0x01;
+        public static final int VP9Profile1 = 0x02;
+        public static final int VP9Profile2 = 0x04;
+        public static final int VP9Profile3 = 0x08;
+        // HDR profiles also support passing HDR metadata
+        public static final int VP9Profile2HDR = 0x1000;
+        public static final int VP9Profile3HDR = 0x2000;
 
         // from OMX_VIDEO_VP9LEVELTYPE
         public static final int VP9Level1  = 0x0;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index b78869e..55d5f42 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1107,6 +1107,9 @@
      * as this call returns.
      *
      * @param afd the AssetFileDescriptor for the file you want to play
+     * @throws IllegalStateException if it is called in an invalid state
+     * @throws IllegalArgumentException if afd is not a valid AssetFileDescriptor
+     * @throws IOException if afd can not be read
      */
     public void setDataSource(@NonNull AssetFileDescriptor afd)
             throws IOException, IllegalArgumentException, IllegalStateException {
@@ -1127,6 +1130,8 @@
      *
      * @param fd the FileDescriptor for the file you want to play
      * @throws IllegalStateException if it is called in an invalid state
+     * @throws IllegalArgumentException if fd is not a valid FileDescriptor
+     * @throws IOException if fd can not be read
      */
     public void setDataSource(FileDescriptor fd)
             throws IOException, IllegalArgumentException, IllegalStateException {
@@ -1143,6 +1148,8 @@
      * @param offset the offset into the file where the data to be played starts, in bytes
      * @param length the length in bytes of the data to be played
      * @throws IllegalStateException if it is called in an invalid state
+     * @throws IllegalArgumentException if fd is not a valid FileDescriptor
+     * @throws IOException if fd can not be read
      */
     public void setDataSource(FileDescriptor fd, long offset, long length)
             throws IOException, IllegalArgumentException, IllegalStateException {
@@ -1157,6 +1164,7 @@
      *
      * @param dataSource the MediaDataSource for the media you want to play
      * @throws IllegalStateException if it is called in an invalid state
+     * @throws IllegalArgumentException if dataSource is not a valid MediaDataSource
      */
     public void setDataSource(MediaDataSource dataSource)
             throws IllegalArgumentException, IllegalStateException {
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index e8c50e3..db7b43a 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1217,6 +1217,18 @@
         public static final String COLUMN_SEARCHABLE = "searchable";
 
         /**
+         * The flag indicating whether recording of this program is prohibited.
+         *
+         * <p>A value of 1 indicates that recording of this program is prohibited and application
+         * will not schedule any recording for this program. A value of 0 indicates that the
+         * recording is not prohibited. If not specified, this value is set to 0 (not prohibited) by
+         * default.
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        public static final String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+
+        /**
          * Internal data used by individual TV input services.
          *
          * <p>This is internal to the provider that inserted it, and should not be decoded by other
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..bfd938e 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}.
@@ -483,26 +483,19 @@
         }
 
         /**
-         * This is called when the start playback position is changed.
-         *
-         * <p>The start playback position of the time shifted program should be adjusted when the TV
-         * input cannot retain the whole recorded program due to some reason (e.g. limitation on
-         * storage space). This is necessary to prevent the application from allowing the user to
-         * seek to a time position that is not reachable.
+         * This is called when the start position for time shifting has changed.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback.
-         * @param timeMs The start playback position of the time shifted program, in milliseconds
-         *            since the epoch.
+         * @param timeMs The start position for time shifting, in milliseconds since the epoch.
          */
         public void onTimeShiftStartPositionChanged(Session session, long timeMs) {
         }
 
         /**
-         * This is called when the current playback position is changed.
+         * This is called when the current position for time shifting is changed.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback.
-         * @param timeMs The current playback position of the time shifted program, in milliseconds
-         *            since the epoch.
+         * @param timeMs The current position for time shifting, in milliseconds since the epoch.
          */
         public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
         }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 97ef6d8..e134635 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.
@@ -995,15 +996,18 @@
         }
 
         /**
-         * Returns the start playback position for time shifting, in milliseconds since the epoch.
+         * Returns the start position for time shifting, in milliseconds since the epoch.
          * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
          * moment.
          *
-         * <p>The start playback position of the time shifted program should be adjusted when the
-         * implementation cannot retain the whole recorded program due to some reason (e.g.
-         * limitation on storage space). It is the earliest possible time position that the user can
-         * seek to, thus failure to notifying its change immediately might result in bad experience
-         * where the application allows the user to seek to an invalid time position.
+         * <p>The start position for time shifting indicates the earliest possible time the user can
+         * seek to. Initially this is equivalent to the time when the implementation starts
+         * recording. Later it may be adjusted because there is insufficient space or the duration
+         * of recording is limited by the implementation. The application does not allow the user to
+         * seek to a position earlier than the start position.
+         *
+         * <p>For playback of a recorded program initiated by {@link #onTimeShiftPlay(Uri)}, the
+         * start position is the time when playback starts. It does not change.
          *
          * @see #onTimeShiftPlay(Uri)
          * @see #onTimeShiftResume()
@@ -1017,13 +1021,13 @@
         }
 
         /**
-         * Returns the current playback position for time shifting, in milliseconds since the epoch.
+         * Returns the current position for time shifting, in milliseconds since the epoch.
          * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
          * moment.
          *
-         * <p>Note that the current playback position should be equal to or greater than the start
-         * playback position reported by {@link #onTimeShiftGetStartPosition}. Failure to notifying
-         * the correct current position might lead to bad user experience.
+         * <p>The current position for time shifting is the same as the current position of
+         * playback. It should be equal to or greater than the start position reported by
+         * {@link #onTimeShiftGetStartPosition()}.
          *
          * @see #onTimeShiftPlay(Uri)
          * @see #onTimeShiftResume()
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index e623353..c9c881c 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -16,17 +16,29 @@
 
 package android.media.tv;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 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.
      */
@@ -93,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;
     }
@@ -245,6 +258,37 @@
         dest.writeBundle(mExtra);
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+          return true;
+        }
+
+        if (!(o instanceof TvTrackInfo)) {
+          return false;
+        }
+
+        TvTrackInfo obj = (TvTrackInfo) o;
+        return TextUtils.equals(mId, obj.mId)
+                && mType == obj.mType
+                && TextUtils.equals(mLanguage, obj.mLanguage)
+                && TextUtils.equals(mDescription, obj.mDescription)
+                && Objects.equals(mExtra, obj.mExtra)
+                && (mType == TYPE_AUDIO
+                        ? mAudioChannelCount == obj.mAudioChannelCount
+                        && mAudioSampleRate == obj.mAudioSampleRate
+                        : (mType == TYPE_VIDEO
+                                ? mVideoWidth == obj.mVideoWidth
+                                && mVideoHeight == obj.mVideoHeight
+                                && mVideoFrameRate == obj.mVideoFrameRate
+                                && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio : true));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mId);
+    }
+
     public static final Parcelable.Creator<TvTrackInfo> CREATOR =
             new Parcelable.Creator<TvTrackInfo>() {
                 @Override
@@ -285,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..10cec1f 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) {
@@ -847,32 +848,34 @@
     public abstract static class TimeShiftPositionCallback {
 
         /**
-         * This is called when the start playback position is changed.
+         * This is called when the start position for time shifting has changed.
          *
-         * <p>The start playback position of the time shifted program can be adjusted by the TV
-         * input when it cannot retain the whole recorded program due to some reason (e.g.
-         * limitation on storage space). The application should not allow the user to seek to a
-         * position earlier than the start position.
+         * <p>The start position for time shifting indicates the earliest possible time the user can
+         * seek to. Initially this is equivalent to the time when the underlying TV input starts
+         * recording. Later it may be adjusted because there is insufficient space or the duration
+         * of recording is limited. The application must not allow the user to seek to a position
+         * earlier than the start position.
          *
-         * <p>Note that {@code timeMs} is not relative time in the program but wall-clock time,
-         * which is intended to avoid calling this method unnecessarily around program boundaries.
+         * <p>For playback of a recorded program initiated by {@link #timeShiftPlay(String, Uri)},
+         * the start position is the time when playback starts. It does not change.
          *
          * @param inputId The ID of the TV input bound to this view.
-         * @param timeMs The start playback position of the time shifted program, in milliseconds
-         *            since the epoch.
+         * @param timeMs The start position for time shifting, in milliseconds since the epoch.
          */
         public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
         }
 
         /**
-         * This is called when the current playback position is changed.
+         * This is called when the current position for time shifting has changed.
          *
-         * <p>Note that {@code timeMs} is not relative time in the program but wall-clock time,
-         * which is intended to avoid calling this method unnecessarily around program boundaries.
+         * <p>The current position for time shifting is the same as the current position of
+         * playback. During playback, the current position changes continuously. When paused, it
+         * does not change.
+         *
+         * <p>Note that {@code timeMs} is wall-clock time.
          *
          * @param inputId The ID of the TV input bound to this view.
-         * @param timeMs The current playback position of the time shifted program, in milliseconds
-         *            since the epoch.
+         * @param timeMs The current position for time shifting, in milliseconds since the epoch.
          */
         public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
         }
diff --git a/media/jni/android_media_ExifInterface.cpp b/media/jni/android_media_ExifInterface.cpp
index 418a3f2..ae23720 100644
--- a/media/jni/android_media_ExifInterface.cpp
+++ b/media/jni/android_media_ExifInterface.cpp
@@ -136,7 +136,8 @@
 
     KeyedVector<String8, String8> map;
 
-    if (image_data.thumbnail.length > 0) {
+    if (image_data.thumbnail.length > 0
+            && image_data.thumbnail.format == ::piex::Image::kJpegCompressed) {
         map.add(String8("hasThumbnail"), String8("true"));
         map.add(String8("thumbnailOffset"), String8::format("%d", image_data.thumbnail.offset));
         map.add(String8("thumbnailLength"), String8::format("%d", image_data.thumbnail.length));
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 39f2a32..d6bf9ad 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -932,8 +932,9 @@
                     break;
                 }
 
-                if (image_data.thumbnail.length == 0) {
-                    // No thumbnail.
+                if (image_data.thumbnail.length == 0
+                        || image_data.thumbnail.format == ::piex::Image::kJpegCompressed) {
+                    // No thumbnail or non jpeg thumbnail.
                     break;
                 }
 
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/DocumentClipper.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java
index 15bfc3b..059b5e0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java
@@ -32,6 +32,7 @@
 import libcore.io.IoUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -76,7 +77,8 @@
      * This should be run from inside an AsyncTask.
      */
     public List<DocumentInfo> getClippedDocuments() {
-        return getDocumentsFromClipData(mClipboard.getPrimaryClip());
+        ClipData data = mClipboard.getPrimaryClip();
+        return data == null ? Collections.EMPTY_LIST : getDocumentsFromClipData(data);
     }
 
     /**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 40b54d3..7a7d3a1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -232,12 +232,13 @@
         final MenuItem list = menu.findItem(R.id.menu_list);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
 
-        boolean recents = cwd == null;
 
-        createDir.setVisible(picking && !recents && cwd.isCreateSupported());
+        createDir.setVisible(picking);
+        createDir.setEnabled(canCreateDirectory());
 
         // No display options in recent directories
-        if (picking && recents) {
+        boolean inRecents = cwd == null;
+        if (picking && inRecents) {
             grid.setVisible(false);
             list.setVisible(false);
         }
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/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 0e27622..2be93b8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -17,6 +17,7 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.Shared.TAG;
+import static com.android.documentsui.State.ACTION_CREATE;
 
 import android.app.Fragment;
 import android.app.FragmentManager;
@@ -107,8 +108,9 @@
                 mAdapter.update(data);
 
                 // When launched into empty recents, show drawer
-                if (mAdapter.isEmpty() && !state.hasLocationChanged() &&
-                        context instanceof DocumentsActivity) {
+                if (mAdapter.isEmpty() && !state.hasLocationChanged()
+                        && state.action != ACTION_CREATE
+                        && context instanceof DocumentsActivity) {
                     ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index f3b750a..d830c61 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -84,6 +84,13 @@
                 new View.OnKeyListener() {
                     @Override
                     public boolean onKey(View v, int keyCode, KeyEvent event) {
+                        // Only handle key-down events. This is simpler, consistent with most other
+                        // UIs, and enables the handling of repeated key events from holding down a
+                        // key.
+                        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+                            return false;
+                        }
+
                         if (keyCode == KeyEvent.KEYCODE_ENTER && mSave.isEnabled()) {
                             performSave();
                             return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index bc2133e..ccb2886 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.
@@ -833,6 +842,15 @@
                 getActivity(),
                 DocumentsActivity.class);
 
+
+        // Relay any config overrides bits present in the original intent.
+        Intent original = getActivity().getIntent();
+        if (original != null && original.hasExtra(Shared.EXTRA_PRODUCTIVITY_MODE)) {
+            intent.putExtra(
+                    Shared.EXTRA_PRODUCTIVITY_MODE,
+                    original.getBooleanExtra(Shared.EXTRA_PRODUCTIVITY_MODE, false));
+        }
+
         // Set an appropriate title on the drawer when it is shown in the picker.
         // Coupled with the fact that we auto-open the drawer for copy/move operations
         // it should basically be the thing people see first.
@@ -1095,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)) {
@@ -1157,14 +1179,23 @@
                 case DragEvent.ACTION_DROP:
                     // After a drop event, always stop highlighting the target.
                     setDropTargetHighlight(v, false);
+
+                    ClipData clipData = event.getClipData();
+                    if (clipData == null) {
+                        Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring.");
+                        return false;
+                    }
+
                     // Don't copy from the cwd into the cwd. Note: this currently doesn't work for
                     // multi-window drag, because localState isn't carried over from one process to
                     // another.
                     Object src = event.getLocalState();
                     DocumentInfo dst = getDestination(v);
                     if (Objects.equals(src, dst)) {
+                        if (DEBUG) Log.d(TAG, "Drop target same as source. Ignoring.");
                         return false;
                     }
+
                     // Recognize multi-window drag and drop based on the fact that localState is not
                     // carried between processes. It will stop working when the localsState behavior
                     // is changed. The info about window should be passed in the localState then.
@@ -1173,7 +1204,8 @@
                     Metrics.logUserAction(getContext(),
                             src == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW
                                     : Metrics.USER_ACTION_DRAG_N_DROP);
-                    copyFromClipData(event.getClipData(), dst);
+
+                    copyFromClipData(clipData, dst);
                     return true;
             }
             return false;
@@ -1183,7 +1215,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);
             }
 
@@ -1256,8 +1291,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));
@@ -1386,7 +1423,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());
@@ -1412,7 +1449,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
@@ -1423,9 +1461,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/DocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
index 450341f..2288fe74 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -139,11 +139,13 @@
             }
 
             // Do everything in global coordinates - it makes things simpler.
-            Rect rect = new Rect();
-            mSelectionHotspot.getGlobalVisibleRect(rect);
+            int[] coords = new int[2];
+            mSelectionHotspot.getLocationOnScreen(coords);
+            Rect rect = new Rect(coords[0], coords[1], coords[0] + mSelectionHotspot.getWidth(),
+                    coords[1] + mSelectionHotspot.getHeight());
 
             // If the tap occurred within the icon rect, consider it a selection.
-            if (rect.contains((int)event.getRawX(), (int)event.getRawY())) {
+            if (rect.contains((int) event.getRawX(), (int) event.getRawY())) {
                 return mEventListener.onSelect(this);
             } else {
                 return mEventListener.onActivate(this);
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..e3eae33 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -90,6 +90,10 @@
      */
     private static final class DocumentsTuner 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 DocumentsTuner(Context context, State state) {
             super(context, state);
         }
@@ -178,10 +182,12 @@
                 showDrawer = true;
             }
 
-            if (showDrawer && !mState.hasInitialLocationChanged() && !isSearch) {
+            if (showDrawer && !mState.hasInitialLocationChanged() && !isSearch
+                    && !mModelPreviousLoaded) {
                 // This noops on layouts without drawer, so no need to guard.
                 ((BaseActivity) mContext).setRootsDrawerOpen(true);
             }
+            mModelPreviousLoaded = true;
         }
 
         @Override
@@ -195,6 +201,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 +240,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/src/com/android/documentsui/services/FileOperationService.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
index 580fa38..871e135 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
@@ -229,31 +229,32 @@
             @OpType int operationType, String id, List<DocumentInfo> srcs, DocumentInfo srcParent,
             DocumentStack stack) {
 
+        if (srcs.isEmpty()) {
+            Log.w(TAG, "Ignoring job request with empty srcs list. Id: " + id);
+            return null;
+        }
+
         if (mRunning.containsKey(id)) {
             Log.w(TAG, "Duplicate job id: " + id
                     + ". Ignoring job request for srcs: " + srcs + ", stack: " + stack + ".");
             return null;
         }
 
-        Job job = null;
         switch (operationType) {
             case OPERATION_COPY:
-                job = jobFactory.createCopy(this, getApplicationContext(), this, id, stack, srcs);
-                break;
+                return jobFactory.createCopy(
+                        this, getApplicationContext(), this, id, stack, srcs);
             case OPERATION_MOVE:
-                job = jobFactory.createMove(this, getApplicationContext(), this, id, stack, srcs,
+                return jobFactory.createMove(
+                        this, getApplicationContext(), this, id, stack, srcs,
                         srcParent);
-                break;
             case OPERATION_DELETE:
-                job = jobFactory.createDelete(this, getApplicationContext(), this, id, stack, srcs,
+                return jobFactory.createDelete(
+                        this, getApplicationContext(), this, id, stack, srcs,
                         srcParent);
-                break;
             default:
                 throw new UnsupportedOperationException();
         }
-
-        assert(job != null);
-        return job;
     }
 
     @GuardedBy("mRunning")
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
index c723ac6..b4f1299 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
@@ -291,18 +291,24 @@
 
         Job createCopy(Context service, Context appContext, Listener listener,
                 String id, DocumentStack stack, List<DocumentInfo> srcs) {
+            assert(!srcs.isEmpty());
+            assert(stack.peek().isCreateSupported());
             return new CopyJob(service, appContext, listener, id, stack, srcs);
         }
 
         Job createMove(Context service, Context appContext, Listener listener,
                 String id, DocumentStack stack, List<DocumentInfo> srcs,
                 DocumentInfo srcParent) {
+            assert(!srcs.isEmpty());
+            assert(stack.peek().isCreateSupported());
             return new MoveJob(service, appContext, listener, id, stack, srcs, srcParent);
         }
 
         Job createDelete(Context service, Context appContext, Listener listener,
                 String id, DocumentStack stack, List<DocumentInfo> srcs,
                 DocumentInfo srcParent) {
+            assert(!srcs.isEmpty());
+            assert(stack.peek().isDirectory());  // we can't currently delete from archives.
             return new DeleteJob(service, appContext, listener, id, stack, srcs, srcParent);
         }
     }
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/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java
index 4d5392e..96d963f 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java
@@ -79,6 +79,16 @@
         mJobFactory.assertAllJobsStarted();
     }
 
+    public void testRunsJobs_AfterExceptionInJobCreation() throws Exception {
+        startService(createCopyIntent(new ArrayList<DocumentInfo>(), BETA_DOC));
+        startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
+
+        mJobFactory.assertJobsCreated(1);
+
+        mExecutor.runAll();
+        mJobFactory.assertAllJobsStarted();
+    }
+
     public void testRunsJobs_AfterFailure() throws Exception {
         startService(createCopyIntent(newArrayList(ALPHA_DOC), BETA_DOC));
         startService(createCopyIntent(newArrayList(GAMMA_DOC), DELTA_DOC));
@@ -182,9 +192,18 @@
             }
         }
 
+        void assertJobsCreated(int expected) {
+            assertEquals(expected, jobs.size());
+        }
+
         @Override
         Job createCopy(Context service, Context appContext, Listener listener, String id,
                 DocumentStack stack, List<DocumentInfo> srcs) {
+
+            if (srcs.isEmpty()) {
+                throw new RuntimeException("Empty srcs not supported!");
+            }
+
             TestJob job = new TestJob(service, appContext, listener, OPERATION_COPY, id, stack);
             jobs.add(job);
             return job;
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/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 329afdd..6ed4ea1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -196,9 +196,10 @@
                 }
                 task.loadObjectInfoList(NUM_LOADING_ENTRIES);
                 final boolean shouldNotify =
-                        task.mLastNotified.getTime() <
-                        new Date().getTime() - NOTIFY_PERIOD_MS ||
-                        task.getState() != LoaderTask.STATE_LOADING;
+                        task.getState() != LoaderTask.STATE_CANCELLED &&
+                        (task.mLastNotified.getTime() <
+                         new Date().getTime() - NOTIFY_PERIOD_MS ||
+                         task.getState() != LoaderTask.STATE_LOADING);
                 if (shouldNotify) {
                     task.notify(mResolver);
                 }
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/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index 60dd7e1..a3c6bd7 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -143,9 +143,9 @@
         }
     }
 
-    public void testCancelTask() throws IOException, InterruptedException {
+    public void testCancelTask() throws IOException, InterruptedException, TimeoutException {
         setUpDocument(mManager,
-                DocumentLoader.NUM_INITIAL_ENTRIES + DocumentLoader.NUM_LOADING_ENTRIES + 1);
+                DocumentLoader.NUM_INITIAL_ENTRIES + 1);
 
         // Block the first iteration in the background thread.
         mManager.blockDocument(
@@ -155,19 +155,24 @@
                 MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
             assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
         }
-        Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
+
+        final Uri uri = DocumentsContract.buildChildDocumentsUri(
+                MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
+        assertEquals(0, mResolver.getChangeCount(uri));
 
         // Clear task while the first iteration is being blocked.
+        mLoader.cancelTask(mParentIdentifier);
         mManager.unblockDocument(
                 0, DocumentLoader.NUM_INITIAL_ENTRIES + 1);
-        mLoader.cancelTask(mParentIdentifier);
-
-        Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS * 2);
+        Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
+        assertEquals(0, mResolver.getChangeCount(uri));
 
         // Check if it's OK to query invalidated task.
         try (final Cursor cursor = mLoader.queryChildDocuments(
                 MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
+            assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
         }
+        mResolver.waitForNotification(uri, 1);
     }
 
     private void setUpLoader() {
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/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
new file mode 100644
index 0000000..89dc10b
--- /dev/null
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -0,0 +1,86 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="60dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <com.android.internal.widget.PreferenceImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="48dp"
+            android:maxHeight="48dp" />
+    </LinearLayout>
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee" />
+
+        <TextView android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10" />
+
+        <TextView android:id="@+id/additional_summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/summary"
+            android:layout_alignStart="@android:id/summary"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10"
+            android:visibility="gone" />
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingStart="16dp"
+        android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
index 4484613..4cdc177 100644
--- a/packages/SettingsLib/res/values/attrs.xml
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -15,10 +15,18 @@
 -->
 
 <resources>
+
     <declare-styleable name="RestrictedPreference">
+        <!-- The user restriction on which the preference disabled by admin state will be based on. -->
         <attr name="userRestriction" format="string" />
+        <!-- If true then we can use enabled/disabled by admin strings for summary (android.R.id.summary). -->
         <attr name="useAdminDisabledSummary" format="boolean" />
+        <!-- If true, an additional summary will be added in addition to the existing summary and
+        this will be used for enabled/disabled by admin strings leaving android.R.id.summary untouched.
+        As such when this is true, useAdminDisabledSummary will be overwritten to false. -->
+        <attr name="useAdditionalSummary" format="boolean" />
     </declare-styleable>
+
     <declare-styleable name="WifiEncryptionState">
         <attr name="state_encrypted" format="boolean" />
     </declare-styleable>
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/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index f381286..dabbc61 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -17,12 +17,14 @@
 package com.android.settingslib;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.os.UserHandle;
 import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v7.preference.PreferenceManager;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.support.v14.preference.SwitchPreference;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.View;
 import android.widget.TextView;
 
@@ -34,12 +36,28 @@
  */
 public class RestrictedSwitchPreference extends SwitchPreference {
     RestrictedPreferenceHelper mHelper;
+    boolean mUseAdditionalSummary = false;
 
     public RestrictedSwitchPreference(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         setWidgetLayoutResource(R.layout.restricted_switch_widget);
         mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+        if (attrs != null) {
+            final TypedArray attributes = context.obtainStyledAttributes(attrs,
+                    R.styleable.RestrictedPreference);
+            final TypedValue useAdditionalSummary =
+                    attributes.peekValue(R.styleable.RestrictedPreference_useAdditionalSummary);
+            if (useAdditionalSummary != null) {
+                mUseAdditionalSummary =
+                        (useAdditionalSummary.type == TypedValue.TYPE_INT_BOOLEAN
+                                && useAdditionalSummary.data != 0);
+            }
+        }
+        if (mUseAdditionalSummary) {
+            setLayoutResource(R.layout.restricted_switch_preference);
+            useAdminDisabledSummary(false);
+        }
     }
 
     public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -67,11 +85,29 @@
         if (switchWidget != null) {
             switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE);
         }
-        final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
-        if (summaryView != null && isDisabledByAdmin()) {
-            summaryView.setText(
-                    isChecked() ? R.string.enabled_by_admin : R.string.disabled_by_admin);
-            summaryView.setVisibility(View.VISIBLE);
+        if (mUseAdditionalSummary) {
+            final TextView additionalSummaryView = (TextView) holder.findViewById(
+                    R.id.additional_summary);
+            if (additionalSummaryView != null) {
+                if (isDisabledByAdmin()) {
+                    additionalSummaryView.setText(
+                            isChecked() ? R.string.enabled_by_admin : R.string.disabled_by_admin);
+                    additionalSummaryView.setVisibility(View.VISIBLE);
+                }
+            } else {
+                additionalSummaryView.setVisibility(View.GONE);
+            }
+        } else {
+            final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
+            if (summaryView != null) {
+                if (isDisabledByAdmin()) {
+                    summaryView.setText(
+                            isChecked() ? R.string.enabled_by_admin : R.string.disabled_by_admin);
+                    summaryView.setVisibility(View.VISIBLE);
+                }
+            }
+            // No need to change the visibility to GONE in the else case here since Preference class
+            // would have already changed it if there is no summary to display.
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index d353f31..8c2af45 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) {
@@ -825,8 +839,18 @@
 
                     for (int i = 0; i < mEntriesMap.size(); i++) {
                         int userId = mEntriesMap.keyAt(i);
-                        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(launchIntent,
-                                PackageManager.GET_DISABLED_COMPONENTS, userId);
+                        // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+                        // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+                        // according to the user's lock state. When the user is locked, components
+                        // with ComponentInfo#directBootAware == false will be filtered. We should
+                        // explicitly include both direct boot aware and unaware components here.
+                        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+                                launchIntent,
+                                PackageManager.GET_DISABLED_COMPONENTS
+                                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                userId
+                        );
                         synchronized (mEntriesMap) {
                             if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
                             HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
@@ -1102,6 +1126,10 @@
 
         public boolean mounted;
 
+        /**
+         * Setting this to {@code true} prevents the entry to be filtered by
+         * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}.
+         */
         public boolean hasLauncherEntry;
 
         public String getNormalizedLabel() {
@@ -1272,6 +1300,9 @@
         }
     };
 
+    /**
+     * Displays a combined list with "downloaded" and "visible in launcher" apps only.
+     */
     public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
         public void init() {
         }
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 5621642..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];
@@ -1831,14 +1834,11 @@
         private void maybeNotifyProfiles(int userId, Uri uri, String name,
                 Set<String> keysCloned) {
             if (keysCloned.contains(name)) {
-                List<UserInfo> profiles = mUserManager.getProfiles(userId);
-                int size = profiles.size();
-                for (int i = 0; i < size; i++) {
-                    UserInfo profile = profiles.get(i);
+                for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) {
                     // the notification for userId has already been sent.
-                    if (profile.id != userId) {
+                    if (profileId != userId) {
                         mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
-                                profile.id, 0, uri).sendToTarget();
+                                profileId, 0, uri).sendToTarget();
                     }
                 }
             }
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 e1e0c3b..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 {
@@ -179,42 +181,37 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        final NumberFormat nf = NumberFormat.getPercentInstance();
-        nf.setMinimumFractionDigits(2);
-        nf.setMaximumFractionDigits(2);
-
-        assertProgressNotification(NAME, nf.format(0));
+        assertProgressNotification(NAME, 0f);
 
         SystemProperties.set(PROGRESS_PROPERTY, "108");
-        assertProgressNotification(NAME, nf.format(0.108));
+        assertProgressNotification(NAME, 10.80f);
 
-        SystemProperties.set(PROGRESS_PROPERTY, "500");
-        assertProgressNotification(NAME, nf.format(0.50));
+        assertProgressNotification(NAME, 50.00f);
 
         SystemProperties.set(PROGRESS_PROPERTY, "950");
-        assertProgressNotification(NAME, nf.format(0.95));
+        assertProgressNotification(NAME, 95.00f);
 
         // Make sure progress never goes back...
         SystemProperties.set(MAX_PROPERTY, "2000");
         Thread.sleep(POLLING_FREQUENCY + DateUtils.SECOND_IN_MILLIS);
-        assertProgressNotification(NAME, nf.format(0.95));
+        assertProgressNotification(NAME, 95.00f);
 
         SystemProperties.set(PROGRESS_PROPERTY, "1000");
-        assertProgressNotification(NAME, nf.format(0.95));
+        assertProgressNotification(NAME, 95.00f);
 
         // ...only forward...
         SystemProperties.set(PROGRESS_PROPERTY, "1902");
-        assertProgressNotification(NAME, nf.format(0.9510));
+        assertProgressNotification(NAME, 95.10f);
 
         SystemProperties.set(PROGRESS_PROPERTY, "1960");
-        assertProgressNotification(NAME, nf.format(0.98));
+        assertProgressNotification(NAME, 98.00f);
 
         // ...but never more than the capped value.
         SystemProperties.set(PROGRESS_PROPERTY, "2000");
-        assertProgressNotification(NAME, nf.format(0.99));
+        assertProgressNotification(NAME, 99.00f);
 
         SystemProperties.set(PROGRESS_PROPERTY, "3000");
-        assertProgressNotification(NAME, nf.format(0.99));
+        assertProgressNotification(NAME, 99.00f);
 
         Bundle extras =
                 sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath, mScreenshotPath);
@@ -233,7 +230,7 @@
         nf.setMinimumFractionDigits(2);
         nf.setMaximumFractionDigits(2);
 
-        assertProgressNotification(NAME, nf.format(0));
+        assertProgressNotification(NAME, 00.00f);
 
         openProgressNotification(ID);
         UiObject cancelButton = mUiBot.getVisibleObject(mContext.getString(
@@ -338,7 +335,7 @@
         detailsUi.clickOk();
 
         assertPropertyValue(NAME_PROPERTY, NEW_NAME);
-        assertProgressNotification(NEW_NAME, "0.00%");
+        assertProgressNotification(NEW_NAME, 00.00f);
 
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mPlainTextPath,
                 mScreenshotPath);
@@ -375,7 +372,7 @@
         detailsUi.clickOk();
 
         assertPropertyValue(NAME_PROPERTY, NEW_NAME);
-        assertProgressNotification(NEW_NAME, "0.00%");
+        assertProgressNotification(NEW_NAME, 00.00f);
 
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID,
                 plainText? mPlainTextPath : mZipPath, mScreenshotPath);
@@ -437,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);
@@ -508,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);
@@ -518,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");
 
@@ -531,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 {
@@ -571,13 +594,13 @@
         }
     }
 
-    private void assertProgressNotification(String name, String percent) {
+    private void assertProgressNotification(String name, float percent) {
         // TODO: it currently looks for 3 distinct objects, without taking advantage of their
         // relationship.
         openProgressNotification(ID);
         Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
         mUiBot.getObject(name);
-        mUiBot.getObject(percent);
+        // TODO: need a way to get the ProgresBar from the "android:id/progress" UIObject...
     }
 
     private UiObject openProgressNotification(int id) {
@@ -628,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);
     }
 
     /**
@@ -637,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();
     }
@@ -657,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));
     }
 
     /**
@@ -894,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%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
copy 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%
copy from packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
copy 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/anim/tv_pip_onboarding_button_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_button_enter_animation.xml
new file mode 100644
index 0000000..01c263b
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_onboarding_button_enter_animation.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="114dp"
+        android:valueTo="0dp"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueTo="1"
+        android:interpolator="@android:interpolator/linear"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_description_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_description_enter_animation.xml
new file mode 100644
index 0000000..a12b3b6
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_onboarding_description_enter_animation.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="84dp"
+        android:valueTo="0dp"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueTo="1"
+        android:interpolator="@android:interpolator/linear"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_image_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_image_enter_animation.xml
new file mode 100644
index 0000000..ae9677e
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_onboarding_image_enter_animation.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="114dp"
+        android:valueTo="0dp"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueTo="1"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_title_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_title_enter_animation.xml
new file mode 100644
index 0000000..4bde070
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_onboarding_title_enter_animation.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueFrom="84dp"
+        android:valueTo="0dp"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueTo="1"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="@integer/tv_pip_onboarding_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_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_overlay_fade_in_animation.xml
index 681ff91..33bceaa 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
+++ b/packages/SystemUI/res/anim/tv_pip_overlay_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="350" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_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_overlay_fade_out_animation.xml
index e6deb0f..a12ddff 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
+++ b/packages/SystemUI/res/anim/tv_pip_overlay_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="500" />
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote.png b/packages/SystemUI/res/drawable-xhdpi/remote.png
new file mode 100644
index 0000000..c0ccfe6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_0.png b/packages/SystemUI/res/drawable-xhdpi/remote_0.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_1.png b/packages/SystemUI/res/drawable-xhdpi/remote_1.png
new file mode 100644
index 0000000..252ff5e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_10.png b/packages/SystemUI/res/drawable-xhdpi/remote_10.png
new file mode 100644
index 0000000..5e52b37
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_100.png b/packages/SystemUI/res/drawable-xhdpi/remote_100.png
new file mode 100644
index 0000000..f604808
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_101.png b/packages/SystemUI/res/drawable-xhdpi/remote_101.png
new file mode 100644
index 0000000..0272e45
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_101.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_102.png b/packages/SystemUI/res/drawable-xhdpi/remote_102.png
new file mode 100644
index 0000000..aaa35c0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_102.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_103.png b/packages/SystemUI/res/drawable-xhdpi/remote_103.png
new file mode 100644
index 0000000..20f95a5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_103.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_104.png b/packages/SystemUI/res/drawable-xhdpi/remote_104.png
new file mode 100644
index 0000000..052f23e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_104.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_105.png b/packages/SystemUI/res/drawable-xhdpi/remote_105.png
new file mode 100644
index 0000000..c5c8256
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_105.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_106.png b/packages/SystemUI/res/drawable-xhdpi/remote_106.png
new file mode 100644
index 0000000..4e804fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_106.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_107.png b/packages/SystemUI/res/drawable-xhdpi/remote_107.png
new file mode 100644
index 0000000..76669cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_107.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_108.png b/packages/SystemUI/res/drawable-xhdpi/remote_108.png
new file mode 100644
index 0000000..c1b2f3b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_108.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_109.png b/packages/SystemUI/res/drawable-xhdpi/remote_109.png
new file mode 100644
index 0000000..79e1d7b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_109.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_11.png b/packages/SystemUI/res/drawable-xhdpi/remote_11.png
new file mode 100644
index 0000000..cfec6cb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_11.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_110.png b/packages/SystemUI/res/drawable-xhdpi/remote_110.png
new file mode 100644
index 0000000..d902297
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_110.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_111.png b/packages/SystemUI/res/drawable-xhdpi/remote_111.png
new file mode 100644
index 0000000..e48ed0b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_111.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_112.png b/packages/SystemUI/res/drawable-xhdpi/remote_112.png
new file mode 100644
index 0000000..90dd1a2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_112.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_113.png b/packages/SystemUI/res/drawable-xhdpi/remote_113.png
new file mode 100644
index 0000000..8fb4ad2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_113.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_114.png b/packages/SystemUI/res/drawable-xhdpi/remote_114.png
new file mode 100644
index 0000000..76873fb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_114.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_115.png b/packages/SystemUI/res/drawable-xhdpi/remote_115.png
new file mode 100644
index 0000000..e923d4c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_115.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_116.png b/packages/SystemUI/res/drawable-xhdpi/remote_116.png
new file mode 100644
index 0000000..41d5124
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_116.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_117.png b/packages/SystemUI/res/drawable-xhdpi/remote_117.png
new file mode 100644
index 0000000..eacde64
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_117.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_118.png b/packages/SystemUI/res/drawable-xhdpi/remote_118.png
new file mode 100644
index 0000000..5dc1fb0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_118.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_119.png b/packages/SystemUI/res/drawable-xhdpi/remote_119.png
new file mode 100644
index 0000000..a16f037
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_119.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_12.png b/packages/SystemUI/res/drawable-xhdpi/remote_12.png
new file mode 100644
index 0000000..28bb387
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_12.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_120.png b/packages/SystemUI/res/drawable-xhdpi/remote_120.png
new file mode 100644
index 0000000..fe3ef41
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_120.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_121.png b/packages/SystemUI/res/drawable-xhdpi/remote_121.png
new file mode 100644
index 0000000..ef2b892
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_121.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_122.png b/packages/SystemUI/res/drawable-xhdpi/remote_122.png
new file mode 100644
index 0000000..5342976
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_122.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_123.png b/packages/SystemUI/res/drawable-xhdpi/remote_123.png
new file mode 100644
index 0000000..bb8a53a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_123.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_124.png b/packages/SystemUI/res/drawable-xhdpi/remote_124.png
new file mode 100644
index 0000000..b68337e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_124.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_125.png b/packages/SystemUI/res/drawable-xhdpi/remote_125.png
new file mode 100644
index 0000000..81fa3a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_125.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_126.png b/packages/SystemUI/res/drawable-xhdpi/remote_126.png
new file mode 100644
index 0000000..2339d74
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_126.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_127.png b/packages/SystemUI/res/drawable-xhdpi/remote_127.png
new file mode 100644
index 0000000..90d1e0b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_127.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_128.png b/packages/SystemUI/res/drawable-xhdpi/remote_128.png
new file mode 100644
index 0000000..6de4eb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_128.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_129.png b/packages/SystemUI/res/drawable-xhdpi/remote_129.png
new file mode 100644
index 0000000..9086074
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_129.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_13.png b/packages/SystemUI/res/drawable-xhdpi/remote_13.png
new file mode 100644
index 0000000..a1e212d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_13.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_130.png b/packages/SystemUI/res/drawable-xhdpi/remote_130.png
new file mode 100644
index 0000000..2bc9698
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_130.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_131.png b/packages/SystemUI/res/drawable-xhdpi/remote_131.png
new file mode 100644
index 0000000..d18d2c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_131.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_132.png b/packages/SystemUI/res/drawable-xhdpi/remote_132.png
new file mode 100644
index 0000000..10a00cd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_132.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_133.png b/packages/SystemUI/res/drawable-xhdpi/remote_133.png
new file mode 100644
index 0000000..6f495b4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_133.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_134.png b/packages/SystemUI/res/drawable-xhdpi/remote_134.png
new file mode 100644
index 0000000..703f2c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_134.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_135.png b/packages/SystemUI/res/drawable-xhdpi/remote_135.png
new file mode 100644
index 0000000..f4105b0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_135.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_136.png b/packages/SystemUI/res/drawable-xhdpi/remote_136.png
new file mode 100644
index 0000000..0c3a5bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_136.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_137.png b/packages/SystemUI/res/drawable-xhdpi/remote_137.png
new file mode 100644
index 0000000..cbebc05
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_137.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_138.png b/packages/SystemUI/res/drawable-xhdpi/remote_138.png
new file mode 100644
index 0000000..6dfefb0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_138.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_139.png b/packages/SystemUI/res/drawable-xhdpi/remote_139.png
new file mode 100644
index 0000000..1acfdd6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_139.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_14.png b/packages/SystemUI/res/drawable-xhdpi/remote_14.png
new file mode 100644
index 0000000..a503cdf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_14.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_140.png b/packages/SystemUI/res/drawable-xhdpi/remote_140.png
new file mode 100644
index 0000000..70d27c1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_140.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_141.png b/packages/SystemUI/res/drawable-xhdpi/remote_141.png
new file mode 100644
index 0000000..d523a0c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_141.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_142.png b/packages/SystemUI/res/drawable-xhdpi/remote_142.png
new file mode 100644
index 0000000..ed6a65d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_142.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_143.png b/packages/SystemUI/res/drawable-xhdpi/remote_143.png
new file mode 100644
index 0000000..9b048e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_143.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_144.png b/packages/SystemUI/res/drawable-xhdpi/remote_144.png
new file mode 100644
index 0000000..9e2337d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_144.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_145.png b/packages/SystemUI/res/drawable-xhdpi/remote_145.png
new file mode 100644
index 0000000..3f30629
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_145.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_146.png b/packages/SystemUI/res/drawable-xhdpi/remote_146.png
new file mode 100644
index 0000000..1288039
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_146.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_147.png b/packages/SystemUI/res/drawable-xhdpi/remote_147.png
new file mode 100644
index 0000000..d060539
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_147.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_148.png b/packages/SystemUI/res/drawable-xhdpi/remote_148.png
new file mode 100644
index 0000000..5be839d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_148.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_149.png b/packages/SystemUI/res/drawable-xhdpi/remote_149.png
new file mode 100644
index 0000000..39d31ba
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_149.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_15.png b/packages/SystemUI/res/drawable-xhdpi/remote_15.png
new file mode 100644
index 0000000..5695595
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_15.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_150.png b/packages/SystemUI/res/drawable-xhdpi/remote_150.png
new file mode 100644
index 0000000..ec2667c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_150.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_151.png b/packages/SystemUI/res/drawable-xhdpi/remote_151.png
new file mode 100644
index 0000000..ec2667c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_151.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_152.png b/packages/SystemUI/res/drawable-xhdpi/remote_152.png
new file mode 100644
index 0000000..a5d58c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_152.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_153.png b/packages/SystemUI/res/drawable-xhdpi/remote_153.png
new file mode 100644
index 0000000..a5d58c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_153.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_154.png b/packages/SystemUI/res/drawable-xhdpi/remote_154.png
new file mode 100644
index 0000000..a5d58c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_154.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_155.png b/packages/SystemUI/res/drawable-xhdpi/remote_155.png
new file mode 100644
index 0000000..793d411
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_155.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_156.png b/packages/SystemUI/res/drawable-xhdpi/remote_156.png
new file mode 100644
index 0000000..793d411
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_156.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_157.png b/packages/SystemUI/res/drawable-xhdpi/remote_157.png
new file mode 100644
index 0000000..f9d6cdc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_157.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_158.png b/packages/SystemUI/res/drawable-xhdpi/remote_158.png
new file mode 100644
index 0000000..da8d5d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_158.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_159.png b/packages/SystemUI/res/drawable-xhdpi/remote_159.png
new file mode 100644
index 0000000..1e0b097
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_159.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_16.png b/packages/SystemUI/res/drawable-xhdpi/remote_16.png
new file mode 100644
index 0000000..4ae106c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_16.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_160.png b/packages/SystemUI/res/drawable-xhdpi/remote_160.png
new file mode 100644
index 0000000..8aa68ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_160.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_161.png b/packages/SystemUI/res/drawable-xhdpi/remote_161.png
new file mode 100644
index 0000000..e49fdd9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_161.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_162.png b/packages/SystemUI/res/drawable-xhdpi/remote_162.png
new file mode 100644
index 0000000..69257a0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_162.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_163.png b/packages/SystemUI/res/drawable-xhdpi/remote_163.png
new file mode 100644
index 0000000..8f0c3d5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_163.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_164.png b/packages/SystemUI/res/drawable-xhdpi/remote_164.png
new file mode 100644
index 0000000..a4c3229
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_164.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_165.png b/packages/SystemUI/res/drawable-xhdpi/remote_165.png
new file mode 100644
index 0000000..46fae23
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_165.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_166.png b/packages/SystemUI/res/drawable-xhdpi/remote_166.png
new file mode 100644
index 0000000..40d5a9b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_166.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_167.png b/packages/SystemUI/res/drawable-xhdpi/remote_167.png
new file mode 100644
index 0000000..6bd0380
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_167.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_168.png b/packages/SystemUI/res/drawable-xhdpi/remote_168.png
new file mode 100644
index 0000000..03904bf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_168.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_169.png b/packages/SystemUI/res/drawable-xhdpi/remote_169.png
new file mode 100644
index 0000000..564a161
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_169.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_17.png b/packages/SystemUI/res/drawable-xhdpi/remote_17.png
new file mode 100644
index 0000000..0703e1a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_17.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_170.png b/packages/SystemUI/res/drawable-xhdpi/remote_170.png
new file mode 100644
index 0000000..0411f94
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_170.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_171.png b/packages/SystemUI/res/drawable-xhdpi/remote_171.png
new file mode 100644
index 0000000..ec141e9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_171.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_172.png b/packages/SystemUI/res/drawable-xhdpi/remote_172.png
new file mode 100644
index 0000000..cb9ecaf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_172.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_173.png b/packages/SystemUI/res/drawable-xhdpi/remote_173.png
new file mode 100644
index 0000000..484ee51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_173.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_174.png b/packages/SystemUI/res/drawable-xhdpi/remote_174.png
new file mode 100644
index 0000000..85a3135
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_174.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_175.png b/packages/SystemUI/res/drawable-xhdpi/remote_175.png
new file mode 100644
index 0000000..edd6507
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_175.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_176.png b/packages/SystemUI/res/drawable-xhdpi/remote_176.png
new file mode 100644
index 0000000..d95a68b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_176.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_177.png b/packages/SystemUI/res/drawable-xhdpi/remote_177.png
new file mode 100644
index 0000000..305641f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_177.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_178.png b/packages/SystemUI/res/drawable-xhdpi/remote_178.png
new file mode 100644
index 0000000..59de0e5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_178.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_179.png b/packages/SystemUI/res/drawable-xhdpi/remote_179.png
new file mode 100644
index 0000000..414e548
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_179.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_18.png b/packages/SystemUI/res/drawable-xhdpi/remote_18.png
new file mode 100644
index 0000000..74df1e4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_18.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_180.png b/packages/SystemUI/res/drawable-xhdpi/remote_180.png
new file mode 100644
index 0000000..b5d925c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_180.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_181.png b/packages/SystemUI/res/drawable-xhdpi/remote_181.png
new file mode 100644
index 0000000..e8a7127
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_181.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_182.png b/packages/SystemUI/res/drawable-xhdpi/remote_182.png
new file mode 100644
index 0000000..29c7037
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_182.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_183.png b/packages/SystemUI/res/drawable-xhdpi/remote_183.png
new file mode 100644
index 0000000..9491d94
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_183.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_184.png b/packages/SystemUI/res/drawable-xhdpi/remote_184.png
new file mode 100644
index 0000000..4aa0e32
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_184.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_185.png b/packages/SystemUI/res/drawable-xhdpi/remote_185.png
new file mode 100644
index 0000000..2a0dde8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_185.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_186.png b/packages/SystemUI/res/drawable-xhdpi/remote_186.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_186.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_187.png b/packages/SystemUI/res/drawable-xhdpi/remote_187.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_187.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_188.png b/packages/SystemUI/res/drawable-xhdpi/remote_188.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_188.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_189.png b/packages/SystemUI/res/drawable-xhdpi/remote_189.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_189.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_19.png b/packages/SystemUI/res/drawable-xhdpi/remote_19.png
new file mode 100644
index 0000000..222ea31
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_19.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_190.png b/packages/SystemUI/res/drawable-xhdpi/remote_190.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_190.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_191.png b/packages/SystemUI/res/drawable-xhdpi/remote_191.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_191.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_192.png b/packages/SystemUI/res/drawable-xhdpi/remote_192.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_192.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_193.png b/packages/SystemUI/res/drawable-xhdpi/remote_193.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_193.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_194.png b/packages/SystemUI/res/drawable-xhdpi/remote_194.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_194.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_195.png b/packages/SystemUI/res/drawable-xhdpi/remote_195.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_195.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_196.png b/packages/SystemUI/res/drawable-xhdpi/remote_196.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_196.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_197.png b/packages/SystemUI/res/drawable-xhdpi/remote_197.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_197.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_198.png b/packages/SystemUI/res/drawable-xhdpi/remote_198.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_198.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_199.png b/packages/SystemUI/res/drawable-xhdpi/remote_199.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_199.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_2.png b/packages/SystemUI/res/drawable-xhdpi/remote_2.png
new file mode 100644
index 0000000..fb3f7ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_20.png b/packages/SystemUI/res/drawable-xhdpi/remote_20.png
new file mode 100644
index 0000000..646587c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_200.png b/packages/SystemUI/res/drawable-xhdpi/remote_200.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_200.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_201.png b/packages/SystemUI/res/drawable-xhdpi/remote_201.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_201.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_202.png b/packages/SystemUI/res/drawable-xhdpi/remote_202.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_202.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_203.png b/packages/SystemUI/res/drawable-xhdpi/remote_203.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_203.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_204.png b/packages/SystemUI/res/drawable-xhdpi/remote_204.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_204.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_205.png b/packages/SystemUI/res/drawable-xhdpi/remote_205.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_205.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_206.png b/packages/SystemUI/res/drawable-xhdpi/remote_206.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_206.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_207.png b/packages/SystemUI/res/drawable-xhdpi/remote_207.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_207.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_208.png b/packages/SystemUI/res/drawable-xhdpi/remote_208.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_208.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_209.png b/packages/SystemUI/res/drawable-xhdpi/remote_209.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_209.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_21.png b/packages/SystemUI/res/drawable-xhdpi/remote_21.png
new file mode 100644
index 0000000..5858ba92
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_21.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_210.png b/packages/SystemUI/res/drawable-xhdpi/remote_210.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_210.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_211.png b/packages/SystemUI/res/drawable-xhdpi/remote_211.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_211.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_212.png b/packages/SystemUI/res/drawable-xhdpi/remote_212.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_212.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_213.png b/packages/SystemUI/res/drawable-xhdpi/remote_213.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_213.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_214.png b/packages/SystemUI/res/drawable-xhdpi/remote_214.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_214.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_215.png b/packages/SystemUI/res/drawable-xhdpi/remote_215.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_215.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_216.png b/packages/SystemUI/res/drawable-xhdpi/remote_216.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_216.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_217.png b/packages/SystemUI/res/drawable-xhdpi/remote_217.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_217.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_218.png b/packages/SystemUI/res/drawable-xhdpi/remote_218.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_218.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_219.png b/packages/SystemUI/res/drawable-xhdpi/remote_219.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_219.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_22.png b/packages/SystemUI/res/drawable-xhdpi/remote_22.png
new file mode 100644
index 0000000..1974802
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_22.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_220.png b/packages/SystemUI/res/drawable-xhdpi/remote_220.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_220.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_221.png b/packages/SystemUI/res/drawable-xhdpi/remote_221.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_221.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_222.png b/packages/SystemUI/res/drawable-xhdpi/remote_222.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_222.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_223.png b/packages/SystemUI/res/drawable-xhdpi/remote_223.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_223.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_224.png b/packages/SystemUI/res/drawable-xhdpi/remote_224.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_224.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_225.png b/packages/SystemUI/res/drawable-xhdpi/remote_225.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_225.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_226.png b/packages/SystemUI/res/drawable-xhdpi/remote_226.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_226.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_227.png b/packages/SystemUI/res/drawable-xhdpi/remote_227.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_227.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_228.png b/packages/SystemUI/res/drawable-xhdpi/remote_228.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_228.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_229.png b/packages/SystemUI/res/drawable-xhdpi/remote_229.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_229.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_23.png b/packages/SystemUI/res/drawable-xhdpi/remote_23.png
new file mode 100644
index 0000000..96b7c35
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_23.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_230.png b/packages/SystemUI/res/drawable-xhdpi/remote_230.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_230.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_231.png b/packages/SystemUI/res/drawable-xhdpi/remote_231.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_231.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_232.png b/packages/SystemUI/res/drawable-xhdpi/remote_232.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_232.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_233.png b/packages/SystemUI/res/drawable-xhdpi/remote_233.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_233.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_234.png b/packages/SystemUI/res/drawable-xhdpi/remote_234.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_234.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_235.png b/packages/SystemUI/res/drawable-xhdpi/remote_235.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_235.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_236.png b/packages/SystemUI/res/drawable-xhdpi/remote_236.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_236.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_237.png b/packages/SystemUI/res/drawable-xhdpi/remote_237.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_237.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_238.png b/packages/SystemUI/res/drawable-xhdpi/remote_238.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_238.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_239.png b/packages/SystemUI/res/drawable-xhdpi/remote_239.png
new file mode 100644
index 0000000..5bda52e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_239.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_24.png b/packages/SystemUI/res/drawable-xhdpi/remote_24.png
new file mode 100644
index 0000000..0437200
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_24.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_25.png b/packages/SystemUI/res/drawable-xhdpi/remote_25.png
new file mode 100644
index 0000000..60b0f15
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_25.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_26.png b/packages/SystemUI/res/drawable-xhdpi/remote_26.png
new file mode 100644
index 0000000..c34ed97
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_26.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_27.png b/packages/SystemUI/res/drawable-xhdpi/remote_27.png
new file mode 100644
index 0000000..1a83664
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_27.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_28.png b/packages/SystemUI/res/drawable-xhdpi/remote_28.png
new file mode 100644
index 0000000..a3685ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_28.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_29.png b/packages/SystemUI/res/drawable-xhdpi/remote_29.png
new file mode 100644
index 0000000..f7135eb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_29.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_3.png b/packages/SystemUI/res/drawable-xhdpi/remote_3.png
new file mode 100644
index 0000000..937da65
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_30.png b/packages/SystemUI/res/drawable-xhdpi/remote_30.png
new file mode 100644
index 0000000..718cf52
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_31.png b/packages/SystemUI/res/drawable-xhdpi/remote_31.png
new file mode 100644
index 0000000..c0b55df
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_31.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_32.png b/packages/SystemUI/res/drawable-xhdpi/remote_32.png
new file mode 100644
index 0000000..7a1ce9f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_32.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_33.png b/packages/SystemUI/res/drawable-xhdpi/remote_33.png
new file mode 100644
index 0000000..5428bcf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_33.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_34.png b/packages/SystemUI/res/drawable-xhdpi/remote_34.png
new file mode 100644
index 0000000..264efe8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_34.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_35.png b/packages/SystemUI/res/drawable-xhdpi/remote_35.png
new file mode 100644
index 0000000..a5c450f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_35.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_36.png b/packages/SystemUI/res/drawable-xhdpi/remote_36.png
new file mode 100644
index 0000000..3e469e4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_36.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_37.png b/packages/SystemUI/res/drawable-xhdpi/remote_37.png
new file mode 100644
index 0000000..124ebe4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_37.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_38.png b/packages/SystemUI/res/drawable-xhdpi/remote_38.png
new file mode 100644
index 0000000..b2b4844
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_38.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_39.png b/packages/SystemUI/res/drawable-xhdpi/remote_39.png
new file mode 100644
index 0000000..a6d1733
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_39.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_4.png b/packages/SystemUI/res/drawable-xhdpi/remote_4.png
new file mode 100644
index 0000000..c282f40
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_40.png b/packages/SystemUI/res/drawable-xhdpi/remote_40.png
new file mode 100644
index 0000000..4cd615c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_41.png b/packages/SystemUI/res/drawable-xhdpi/remote_41.png
new file mode 100644
index 0000000..c746ae0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_41.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_42.png b/packages/SystemUI/res/drawable-xhdpi/remote_42.png
new file mode 100644
index 0000000..a93f198
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_42.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_43.png b/packages/SystemUI/res/drawable-xhdpi/remote_43.png
new file mode 100644
index 0000000..966e563
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_43.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_44.png b/packages/SystemUI/res/drawable-xhdpi/remote_44.png
new file mode 100644
index 0000000..beb7031
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_44.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_45.png b/packages/SystemUI/res/drawable-xhdpi/remote_45.png
new file mode 100644
index 0000000..718e167
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_45.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_46.png b/packages/SystemUI/res/drawable-xhdpi/remote_46.png
new file mode 100644
index 0000000..aa54ddb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_46.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_47.png b/packages/SystemUI/res/drawable-xhdpi/remote_47.png
new file mode 100644
index 0000000..1d11270
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_47.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_48.png b/packages/SystemUI/res/drawable-xhdpi/remote_48.png
new file mode 100644
index 0000000..ab31163
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_48.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_49.png b/packages/SystemUI/res/drawable-xhdpi/remote_49.png
new file mode 100644
index 0000000..219b7f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_49.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_5.png b/packages/SystemUI/res/drawable-xhdpi/remote_5.png
new file mode 100644
index 0000000..15f69e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_5.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_50.png b/packages/SystemUI/res/drawable-xhdpi/remote_50.png
new file mode 100644
index 0000000..8a9725f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_51.png b/packages/SystemUI/res/drawable-xhdpi/remote_51.png
new file mode 100644
index 0000000..bc839cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_51.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_52.png b/packages/SystemUI/res/drawable-xhdpi/remote_52.png
new file mode 100644
index 0000000..7bab6e5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_52.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_53.png b/packages/SystemUI/res/drawable-xhdpi/remote_53.png
new file mode 100644
index 0000000..34ab855
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_53.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_54.png b/packages/SystemUI/res/drawable-xhdpi/remote_54.png
new file mode 100644
index 0000000..5bd9f59
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_54.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_55.png b/packages/SystemUI/res/drawable-xhdpi/remote_55.png
new file mode 100644
index 0000000..1ff17f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_55.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_56.png b/packages/SystemUI/res/drawable-xhdpi/remote_56.png
new file mode 100644
index 0000000..d57e067
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_56.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_57.png b/packages/SystemUI/res/drawable-xhdpi/remote_57.png
new file mode 100644
index 0000000..a1bdae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_57.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_58.png b/packages/SystemUI/res/drawable-xhdpi/remote_58.png
new file mode 100644
index 0000000..c8bc6a4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_58.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_59.png b/packages/SystemUI/res/drawable-xhdpi/remote_59.png
new file mode 100644
index 0000000..526a24e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_59.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_6.png b/packages/SystemUI/res/drawable-xhdpi/remote_6.png
new file mode 100644
index 0000000..2b6732f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_6.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_60.png b/packages/SystemUI/res/drawable-xhdpi/remote_60.png
new file mode 100644
index 0000000..080619e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_61.png b/packages/SystemUI/res/drawable-xhdpi/remote_61.png
new file mode 100644
index 0000000..5932a8c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_61.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_62.png b/packages/SystemUI/res/drawable-xhdpi/remote_62.png
new file mode 100644
index 0000000..d1233dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_62.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_63.png b/packages/SystemUI/res/drawable-xhdpi/remote_63.png
new file mode 100644
index 0000000..4f230c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_63.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_64.png b/packages/SystemUI/res/drawable-xhdpi/remote_64.png
new file mode 100644
index 0000000..6b89fcb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_64.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_65.png b/packages/SystemUI/res/drawable-xhdpi/remote_65.png
new file mode 100644
index 0000000..87597b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_65.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_66.png b/packages/SystemUI/res/drawable-xhdpi/remote_66.png
new file mode 100644
index 0000000..0ee8c1e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_66.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_67.png b/packages/SystemUI/res/drawable-xhdpi/remote_67.png
new file mode 100644
index 0000000..9aca8fd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_67.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_68.png b/packages/SystemUI/res/drawable-xhdpi/remote_68.png
new file mode 100644
index 0000000..5f263ae
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_68.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_69.png b/packages/SystemUI/res/drawable-xhdpi/remote_69.png
new file mode 100644
index 0000000..1a22b61
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_69.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_7.png b/packages/SystemUI/res/drawable-xhdpi/remote_7.png
new file mode 100644
index 0000000..9d9d699
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_7.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_70.png b/packages/SystemUI/res/drawable-xhdpi/remote_70.png
new file mode 100644
index 0000000..0372c50
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_71.png b/packages/SystemUI/res/drawable-xhdpi/remote_71.png
new file mode 100644
index 0000000..854e3e2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_71.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_72.png b/packages/SystemUI/res/drawable-xhdpi/remote_72.png
new file mode 100644
index 0000000..6919624
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_72.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_73.png b/packages/SystemUI/res/drawable-xhdpi/remote_73.png
new file mode 100644
index 0000000..d8e9ae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_73.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_74.png b/packages/SystemUI/res/drawable-xhdpi/remote_74.png
new file mode 100644
index 0000000..24e5b6a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_74.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_75.png b/packages/SystemUI/res/drawable-xhdpi/remote_75.png
new file mode 100644
index 0000000..369a3a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_75.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_76.png b/packages/SystemUI/res/drawable-xhdpi/remote_76.png
new file mode 100644
index 0000000..96824c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_76.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_77.png b/packages/SystemUI/res/drawable-xhdpi/remote_77.png
new file mode 100644
index 0000000..dd60cca
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_77.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_78.png b/packages/SystemUI/res/drawable-xhdpi/remote_78.png
new file mode 100644
index 0000000..aa3460b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_78.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_79.png b/packages/SystemUI/res/drawable-xhdpi/remote_79.png
new file mode 100644
index 0000000..9a60e3c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_79.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_8.png b/packages/SystemUI/res/drawable-xhdpi/remote_8.png
new file mode 100644
index 0000000..b73c7ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_8.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_80.png b/packages/SystemUI/res/drawable-xhdpi/remote_80.png
new file mode 100644
index 0000000..cbf883c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_81.png b/packages/SystemUI/res/drawable-xhdpi/remote_81.png
new file mode 100644
index 0000000..11a6add
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_81.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_82.png b/packages/SystemUI/res/drawable-xhdpi/remote_82.png
new file mode 100644
index 0000000..e05105d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_82.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_83.png b/packages/SystemUI/res/drawable-xhdpi/remote_83.png
new file mode 100644
index 0000000..57813aa
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_83.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_84.png b/packages/SystemUI/res/drawable-xhdpi/remote_84.png
new file mode 100644
index 0000000..0f6f0fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_84.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_85.png b/packages/SystemUI/res/drawable-xhdpi/remote_85.png
new file mode 100644
index 0000000..ada83ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_85.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_86.png b/packages/SystemUI/res/drawable-xhdpi/remote_86.png
new file mode 100644
index 0000000..442dd51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_86.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_87.png b/packages/SystemUI/res/drawable-xhdpi/remote_87.png
new file mode 100644
index 0000000..bdb4962
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_87.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_88.png b/packages/SystemUI/res/drawable-xhdpi/remote_88.png
new file mode 100644
index 0000000..b318002
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_88.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_89.png b/packages/SystemUI/res/drawable-xhdpi/remote_89.png
new file mode 100644
index 0000000..c4ed874
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_89.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_9.png b/packages/SystemUI/res/drawable-xhdpi/remote_9.png
new file mode 100644
index 0000000..ce5041f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_90.png b/packages/SystemUI/res/drawable-xhdpi/remote_90.png
new file mode 100644
index 0000000..6a662f9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_91.png b/packages/SystemUI/res/drawable-xhdpi/remote_91.png
new file mode 100644
index 0000000..21be887
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_91.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_92.png b/packages/SystemUI/res/drawable-xhdpi/remote_92.png
new file mode 100644
index 0000000..1bc5361
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_92.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_93.png b/packages/SystemUI/res/drawable-xhdpi/remote_93.png
new file mode 100644
index 0000000..76495ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_93.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_94.png b/packages/SystemUI/res/drawable-xhdpi/remote_94.png
new file mode 100644
index 0000000..081c84b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_94.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_95.png b/packages/SystemUI/res/drawable-xhdpi/remote_95.png
new file mode 100644
index 0000000..e9c27a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_95.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_96.png b/packages/SystemUI/res/drawable-xhdpi/remote_96.png
new file mode 100644
index 0000000..1369603
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_96.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_97.png b/packages/SystemUI/res/drawable-xhdpi/remote_97.png
new file mode 100644
index 0000000..fbd1458
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_97.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_98.png b/packages/SystemUI/res/drawable-xhdpi/remote_98.png
new file mode 100644
index 0000000..ccdd7a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_98.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_99.png b/packages/SystemUI/res/drawable-xhdpi/remote_99.png
new file mode 100644
index 0000000..f3cb4db
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/remote_99.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ksh_key_item_background.xml b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
new file mode 100644
index 0000000..75ff30d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shape="rectangle">
+    <solid android:color="@color/ksh_key_item_background" />
+    <corners android:radius="2dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/recents_info_dark.xml b/packages/SystemUI/res/drawable/recents_info_dark.xml
index b1a2242..555a69a 100644
--- a/packages/SystemUI/res/drawable/recents_info_dark.xml
+++ b/packages/SystemUI/res/drawable/recents_info_dark.xml
@@ -14,8 +14,8 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
+        android:width="24.0dp"
+        android:height="24.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
diff --git a/packages/SystemUI/res/drawable/recents_info_light.xml b/packages/SystemUI/res/drawable/recents_info_light.xml
index bc58c3b..65e7bf5 100644
--- a/packages/SystemUI/res/drawable/recents_info_light.xml
+++ b/packages/SystemUI/res/drawable/recents_info_light.xml
@@ -14,8 +14,8 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
+        android:width="24.0dp"
+        android:height="24.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
diff --git a/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
index e98d43f..1e52a91 100644
--- a/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
+++ b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
@@ -16,7 +16,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <gradient
-            android:startColor="#99000000"
-            android:endColor="#E6000000"
+            android:startColor="#4C000000"
+            android:endColor="#72000000"
             android:angle="90"/>
 </shape>
\ No newline at end of file
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_onboarding_remote.xml b/packages/SystemUI/res/drawable/tv_pip_onboarding_remote.xml
new file mode 100644
index 0000000..d46108a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_onboarding_remote.xml
@@ -0,0 +1,259 @@
+<?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.
+-->
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:oneshot="false">
+
+    <item android:drawable="@drawable/remote_0" android:duration="13" />
+    <item android:drawable="@drawable/remote_1" android:duration="13" />
+    <item android:drawable="@drawable/remote_2" android:duration="13" />
+    <item android:drawable="@drawable/remote_3" android:duration="13" />
+    <item android:drawable="@drawable/remote_4" android:duration="13" />
+    <item android:drawable="@drawable/remote_5" android:duration="13" />
+    <item android:drawable="@drawable/remote_6" android:duration="13" />
+    <item android:drawable="@drawable/remote_7" android:duration="13" />
+    <item android:drawable="@drawable/remote_8" android:duration="13" />
+    <item android:drawable="@drawable/remote_9" android:duration="13" />
+    <item android:drawable="@drawable/remote_10" android:duration="13" />
+    <item android:drawable="@drawable/remote_11" android:duration="13" />
+    <item android:drawable="@drawable/remote_12" android:duration="13" />
+    <item android:drawable="@drawable/remote_13" android:duration="13" />
+    <item android:drawable="@drawable/remote_14" android:duration="13" />
+    <item android:drawable="@drawable/remote_15" android:duration="13" />
+    <item android:drawable="@drawable/remote_16" android:duration="13" />
+    <item android:drawable="@drawable/remote_17" android:duration="13" />
+    <item android:drawable="@drawable/remote_18" android:duration="13" />
+    <item android:drawable="@drawable/remote_19" android:duration="13" />
+    <item android:drawable="@drawable/remote_20" android:duration="13" />
+    <item android:drawable="@drawable/remote_21" android:duration="13" />
+    <item android:drawable="@drawable/remote_22" android:duration="13" />
+    <item android:drawable="@drawable/remote_23" android:duration="13" />
+    <item android:drawable="@drawable/remote_24" android:duration="13" />
+    <item android:drawable="@drawable/remote_25" android:duration="13" />
+    <item android:drawable="@drawable/remote_26" android:duration="13" />
+    <item android:drawable="@drawable/remote_27" android:duration="13" />
+    <item android:drawable="@drawable/remote_28" android:duration="13" />
+    <item android:drawable="@drawable/remote_29" android:duration="13" />
+    <item android:drawable="@drawable/remote_30" android:duration="13" />
+    <item android:drawable="@drawable/remote_31" android:duration="13" />
+    <item android:drawable="@drawable/remote_32" android:duration="13" />
+    <item android:drawable="@drawable/remote_33" android:duration="13" />
+    <item android:drawable="@drawable/remote_34" android:duration="13" />
+    <item android:drawable="@drawable/remote_35" android:duration="13" />
+    <item android:drawable="@drawable/remote_36" android:duration="13" />
+    <item android:drawable="@drawable/remote_37" android:duration="13" />
+    <item android:drawable="@drawable/remote_38" android:duration="13" />
+    <item android:drawable="@drawable/remote_39" android:duration="13" />
+    <item android:drawable="@drawable/remote_40" android:duration="13" />
+    <item android:drawable="@drawable/remote_41" android:duration="13" />
+    <item android:drawable="@drawable/remote_42" android:duration="13" />
+    <item android:drawable="@drawable/remote_43" android:duration="13" />
+    <item android:drawable="@drawable/remote_44" android:duration="13" />
+    <item android:drawable="@drawable/remote_45" android:duration="13" />
+    <item android:drawable="@drawable/remote_46" android:duration="13" />
+    <item android:drawable="@drawable/remote_47" android:duration="13" />
+    <item android:drawable="@drawable/remote_48" android:duration="13" />
+    <item android:drawable="@drawable/remote_49" android:duration="13" />
+    <item android:drawable="@drawable/remote_50" android:duration="13" />
+    <item android:drawable="@drawable/remote_51" android:duration="13" />
+    <item android:drawable="@drawable/remote_52" android:duration="13" />
+    <item android:drawable="@drawable/remote_53" android:duration="13" />
+    <item android:drawable="@drawable/remote_54" android:duration="13" />
+    <item android:drawable="@drawable/remote_55" android:duration="13" />
+    <item android:drawable="@drawable/remote_56" android:duration="13" />
+    <item android:drawable="@drawable/remote_57" android:duration="13" />
+    <item android:drawable="@drawable/remote_58" android:duration="13" />
+    <item android:drawable="@drawable/remote_59" android:duration="13" />
+    <item android:drawable="@drawable/remote_60" android:duration="13" />
+    <item android:drawable="@drawable/remote_61" android:duration="13" />
+    <item android:drawable="@drawable/remote_62" android:duration="13" />
+    <item android:drawable="@drawable/remote_63" android:duration="13" />
+    <item android:drawable="@drawable/remote_64" android:duration="13" />
+    <item android:drawable="@drawable/remote_65" android:duration="13" />
+    <item android:drawable="@drawable/remote_66" android:duration="13" />
+    <item android:drawable="@drawable/remote_67" android:duration="13" />
+    <item android:drawable="@drawable/remote_68" android:duration="13" />
+    <item android:drawable="@drawable/remote_69" android:duration="13" />
+    <item android:drawable="@drawable/remote_70" android:duration="13" />
+    <item android:drawable="@drawable/remote_71" android:duration="13" />
+    <item android:drawable="@drawable/remote_72" android:duration="13" />
+    <item android:drawable="@drawable/remote_73" android:duration="13" />
+    <item android:drawable="@drawable/remote_74" android:duration="13" />
+    <item android:drawable="@drawable/remote_75" android:duration="13" />
+    <item android:drawable="@drawable/remote_76" android:duration="13" />
+    <item android:drawable="@drawable/remote_77" android:duration="13" />
+    <item android:drawable="@drawable/remote_78" android:duration="13" />
+    <item android:drawable="@drawable/remote_79" android:duration="13" />
+    <item android:drawable="@drawable/remote_80" android:duration="13" />
+    <item android:drawable="@drawable/remote_81" android:duration="13" />
+    <item android:drawable="@drawable/remote_82" android:duration="13" />
+    <item android:drawable="@drawable/remote_83" android:duration="13" />
+    <item android:drawable="@drawable/remote_84" android:duration="13" />
+    <item android:drawable="@drawable/remote_85" android:duration="13" />
+    <item android:drawable="@drawable/remote_86" android:duration="13" />
+    <item android:drawable="@drawable/remote_87" android:duration="13" />
+    <item android:drawable="@drawable/remote_88" android:duration="13" />
+    <item android:drawable="@drawable/remote_89" android:duration="13" />
+    <item android:drawable="@drawable/remote_90" android:duration="13" />
+    <item android:drawable="@drawable/remote_91" android:duration="13" />
+    <item android:drawable="@drawable/remote_92" android:duration="13" />
+    <item android:drawable="@drawable/remote_93" android:duration="13" />
+    <item android:drawable="@drawable/remote_94" android:duration="13" />
+    <item android:drawable="@drawable/remote_95" android:duration="13" />
+    <item android:drawable="@drawable/remote_96" android:duration="13" />
+    <item android:drawable="@drawable/remote_97" android:duration="13" />
+    <item android:drawable="@drawable/remote_98" android:duration="13" />
+    <item android:drawable="@drawable/remote_99" android:duration="13" />
+    <item android:drawable="@drawable/remote_100" android:duration="13" />
+    <item android:drawable="@drawable/remote_101" android:duration="13" />
+    <item android:drawable="@drawable/remote_102" android:duration="13" />
+    <item android:drawable="@drawable/remote_103" android:duration="13" />
+    <item android:drawable="@drawable/remote_104" android:duration="13" />
+    <item android:drawable="@drawable/remote_105" android:duration="13" />
+    <item android:drawable="@drawable/remote_106" android:duration="13" />
+    <item android:drawable="@drawable/remote_107" android:duration="13" />
+    <item android:drawable="@drawable/remote_108" android:duration="13" />
+    <item android:drawable="@drawable/remote_109" android:duration="13" />
+    <item android:drawable="@drawable/remote_110" android:duration="13" />
+    <item android:drawable="@drawable/remote_111" android:duration="13" />
+    <item android:drawable="@drawable/remote_112" android:duration="13" />
+    <item android:drawable="@drawable/remote_113" android:duration="13" />
+    <item android:drawable="@drawable/remote_114" android:duration="13" />
+    <item android:drawable="@drawable/remote_115" android:duration="13" />
+    <item android:drawable="@drawable/remote_116" android:duration="13" />
+    <item android:drawable="@drawable/remote_117" android:duration="13" />
+    <item android:drawable="@drawable/remote_118" android:duration="13" />
+    <item android:drawable="@drawable/remote_119" android:duration="13" />
+    <item android:drawable="@drawable/remote_120" android:duration="13" />
+    <item android:drawable="@drawable/remote_121" android:duration="13" />
+    <item android:drawable="@drawable/remote_122" android:duration="13" />
+    <item android:drawable="@drawable/remote_123" android:duration="13" />
+    <item android:drawable="@drawable/remote_124" android:duration="13" />
+    <item android:drawable="@drawable/remote_125" android:duration="13" />
+    <item android:drawable="@drawable/remote_126" android:duration="13" />
+    <item android:drawable="@drawable/remote_127" android:duration="13" />
+    <item android:drawable="@drawable/remote_128" android:duration="13" />
+    <item android:drawable="@drawable/remote_129" android:duration="13" />
+    <item android:drawable="@drawable/remote_130" android:duration="13" />
+    <item android:drawable="@drawable/remote_131" android:duration="13" />
+    <item android:drawable="@drawable/remote_132" android:duration="13" />
+    <item android:drawable="@drawable/remote_133" android:duration="13" />
+    <item android:drawable="@drawable/remote_134" android:duration="13" />
+    <item android:drawable="@drawable/remote_135" android:duration="13" />
+    <item android:drawable="@drawable/remote_136" android:duration="13" />
+    <item android:drawable="@drawable/remote_137" android:duration="13" />
+    <item android:drawable="@drawable/remote_138" android:duration="13" />
+    <item android:drawable="@drawable/remote_139" android:duration="13" />
+    <item android:drawable="@drawable/remote_140" android:duration="13" />
+    <item android:drawable="@drawable/remote_141" android:duration="13" />
+    <item android:drawable="@drawable/remote_142" android:duration="13" />
+    <item android:drawable="@drawable/remote_143" android:duration="13" />
+    <item android:drawable="@drawable/remote_144" android:duration="13" />
+    <item android:drawable="@drawable/remote_145" android:duration="13" />
+    <item android:drawable="@drawable/remote_146" android:duration="13" />
+    <item android:drawable="@drawable/remote_147" android:duration="13" />
+    <item android:drawable="@drawable/remote_148" android:duration="13" />
+    <item android:drawable="@drawable/remote_149" android:duration="13" />
+    <item android:drawable="@drawable/remote_150" android:duration="13" />
+    <item android:drawable="@drawable/remote_151" android:duration="13" />
+    <item android:drawable="@drawable/remote_152" android:duration="13" />
+    <item android:drawable="@drawable/remote_153" android:duration="13" />
+    <item android:drawable="@drawable/remote_154" android:duration="13" />
+    <item android:drawable="@drawable/remote_155" android:duration="13" />
+    <item android:drawable="@drawable/remote_156" android:duration="13" />
+    <item android:drawable="@drawable/remote_157" android:duration="13" />
+    <item android:drawable="@drawable/remote_158" android:duration="13" />
+    <item android:drawable="@drawable/remote_159" android:duration="13" />
+    <item android:drawable="@drawable/remote_160" android:duration="13" />
+    <item android:drawable="@drawable/remote_161" android:duration="13" />
+    <item android:drawable="@drawable/remote_162" android:duration="13" />
+    <item android:drawable="@drawable/remote_163" android:duration="13" />
+    <item android:drawable="@drawable/remote_164" android:duration="13" />
+    <item android:drawable="@drawable/remote_165" android:duration="13" />
+    <item android:drawable="@drawable/remote_166" android:duration="13" />
+    <item android:drawable="@drawable/remote_167" android:duration="13" />
+    <item android:drawable="@drawable/remote_168" android:duration="13" />
+    <item android:drawable="@drawable/remote_169" android:duration="13" />
+    <item android:drawable="@drawable/remote_170" android:duration="13" />
+    <item android:drawable="@drawable/remote_171" android:duration="13" />
+    <item android:drawable="@drawable/remote_172" android:duration="13" />
+    <item android:drawable="@drawable/remote_173" android:duration="13" />
+    <item android:drawable="@drawable/remote_174" android:duration="13" />
+    <item android:drawable="@drawable/remote_175" android:duration="13" />
+    <item android:drawable="@drawable/remote_176" android:duration="13" />
+    <item android:drawable="@drawable/remote_177" android:duration="13" />
+    <item android:drawable="@drawable/remote_178" android:duration="13" />
+    <item android:drawable="@drawable/remote_179" android:duration="13" />
+    <item android:drawable="@drawable/remote_180" android:duration="13" />
+    <item android:drawable="@drawable/remote_181" android:duration="13" />
+    <item android:drawable="@drawable/remote_182" android:duration="13" />
+    <item android:drawable="@drawable/remote_183" android:duration="13" />
+    <item android:drawable="@drawable/remote_184" android:duration="13" />
+    <item android:drawable="@drawable/remote_185" android:duration="13" />
+    <item android:drawable="@drawable/remote_186" android:duration="13" />
+    <item android:drawable="@drawable/remote_187" android:duration="13" />
+    <item android:drawable="@drawable/remote_188" android:duration="13" />
+    <item android:drawable="@drawable/remote_189" android:duration="13" />
+    <item android:drawable="@drawable/remote_190" android:duration="13" />
+    <item android:drawable="@drawable/remote_191" android:duration="13" />
+    <item android:drawable="@drawable/remote_192" android:duration="13" />
+    <item android:drawable="@drawable/remote_193" android:duration="13" />
+    <item android:drawable="@drawable/remote_194" android:duration="13" />
+    <item android:drawable="@drawable/remote_195" android:duration="13" />
+    <item android:drawable="@drawable/remote_196" android:duration="13" />
+    <item android:drawable="@drawable/remote_197" android:duration="13" />
+    <item android:drawable="@drawable/remote_198" android:duration="13" />
+    <item android:drawable="@drawable/remote_199" android:duration="13" />
+    <item android:drawable="@drawable/remote_200" android:duration="13" />
+    <item android:drawable="@drawable/remote_201" android:duration="13" />
+    <item android:drawable="@drawable/remote_202" android:duration="13" />
+    <item android:drawable="@drawable/remote_203" android:duration="13" />
+    <item android:drawable="@drawable/remote_204" android:duration="13" />
+    <item android:drawable="@drawable/remote_205" android:duration="13" />
+    <item android:drawable="@drawable/remote_206" android:duration="13" />
+    <item android:drawable="@drawable/remote_207" android:duration="13" />
+    <item android:drawable="@drawable/remote_208" android:duration="13" />
+    <item android:drawable="@drawable/remote_209" android:duration="13" />
+    <item android:drawable="@drawable/remote_210" android:duration="13" />
+    <item android:drawable="@drawable/remote_211" android:duration="13" />
+    <item android:drawable="@drawable/remote_212" android:duration="13" />
+    <item android:drawable="@drawable/remote_213" android:duration="13" />
+    <item android:drawable="@drawable/remote_214" android:duration="13" />
+    <item android:drawable="@drawable/remote_215" android:duration="13" />
+    <item android:drawable="@drawable/remote_216" android:duration="13" />
+    <item android:drawable="@drawable/remote_217" android:duration="13" />
+    <item android:drawable="@drawable/remote_218" android:duration="13" />
+    <item android:drawable="@drawable/remote_219" android:duration="13" />
+    <item android:drawable="@drawable/remote_220" android:duration="13" />
+    <item android:drawable="@drawable/remote_221" android:duration="13" />
+    <item android:drawable="@drawable/remote_222" android:duration="13" />
+    <item android:drawable="@drawable/remote_223" android:duration="13" />
+    <item android:drawable="@drawable/remote_224" android:duration="13" />
+    <item android:drawable="@drawable/remote_225" android:duration="13" />
+    <item android:drawable="@drawable/remote_226" android:duration="13" />
+    <item android:drawable="@drawable/remote_227" android:duration="13" />
+    <item android:drawable="@drawable/remote_228" android:duration="13" />
+    <item android:drawable="@drawable/remote_229" android:duration="13" />
+    <item android:drawable="@drawable/remote_230" android:duration="13" />
+    <item android:drawable="@drawable/remote_231" android:duration="13" />
+    <item android:drawable="@drawable/remote_232" android:duration="13" />
+    <item android:drawable="@drawable/remote_233" android:duration="13" />
+    <item android:drawable="@drawable/remote_234" android:duration="13" />
+    <item android:drawable="@drawable/remote_235" android:duration="13" />
+    <item android:drawable="@drawable/remote_236" android:duration="13" />
+    <item android:drawable="@drawable/remote_237" android:duration="13" />
+    <item android:drawable="@drawable/remote_238" android:duration="13" />
+    <item android:drawable="@drawable/remote_239" android:duration="13" />
+</animation-list>
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/recents_on_tv.xml b/packages/SystemUI/res/layout-television/recents_on_tv.xml
similarity index 88%
rename from packages/SystemUI/res/layout/recents_on_tv.xml
rename to packages/SystemUI/res/layout-television/recents_on_tv.xml
index 28ea66d..1dbd1b3 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout-television/recents_on_tv.xml
@@ -19,7 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="false"
-    android:clipToPadding="false">
+    android:clipToPadding="false"
+    android:background="@drawable/recents_tv_background_gradient">
     <com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
         android:id="@+id/task_list"
         android:layout_width="wrap_content"
@@ -34,9 +35,9 @@
     <!-- Placeholder view to give focus to the PIP menus. -->
     <View
         android:id="@+id/pip"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
         android:focusable="true"
-        android:visibility="gone" />
+        android:visibility="visible" />
 
 </com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout-television/recents_tv_card_dismiss.xml b/packages/SystemUI/res/layout-television/recents_tv_card_dismiss.xml
new file mode 100644
index 0000000..186a058
--- /dev/null
+++ b/packages/SystemUI/res/layout-television/recents_tv_card_dismiss.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:id="@+id/card_dismiss"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_gravity="center_horizontal"
+        android:alpha="0.0"
+        tools:showIn="@layout/recents_tv_task_card_view">
+    <ImageView
+            android:id="@+id/card_dismiss_icon"
+            android:layout_width="@dimen/recents_tv_dismiss_icon_size"
+            android:layout_height="@dimen/recents_tv_dismiss_icon_size"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
+            android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
+            android:src="@drawable/ic_cancel_white_24dp"/>
+    <TextView
+            android:id="@+id/card_dismiss_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/recents_tv_dismiss_text_size"
+            android:fontFamily="@string/font_roboto_light"
+            android:textColor="@color/recents_tv_dismiss_text_color"
+            android:text="@string/recents_tv_dismiss"
+            android:layout_gravity="center_horizontal"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-television/recents_tv_card_info_field.xml b/packages/SystemUI/res/layout-television/recents_tv_card_info_field.xml
new file mode 100644
index 0000000..20397c3
--- /dev/null
+++ b/packages/SystemUI/res/layout-television/recents_tv_card_info_field.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:id="@+id/card_info_field"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        tools:showIn="@layout/recents_tv_task_card_view_fallback_banner">
+    <ImageView
+            android:id="@+id/card_extra_badge"
+            android:layout_width="@dimen/recents_tv_card_extra_badge_size"
+            android:layout_height="@dimen/recents_tv_card_extra_badge_size"
+            android:layout_marginBottom="@dimen/recents_tv_icon_padding_bottom"
+            android:scaleType="fitCenter"
+            android:layout_centerVertical="true"
+            android:layout_alignParentEnd="true"/>
+    <TextView
+            android:id="@+id/card_title_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:includeFontPadding="true"
+            android:singleLine="true"
+            android:shadowColor="@color/recents_tv_text_shadow_color"
+            android:shadowRadius="5"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:textColor="@color/recents_tv_card_title_text_color"
+            android:fontFamily="@string/font_roboto_regular"
+            android:textSize="@dimen/recents_tv_title_text_size"
+            android:paddingStart="@dimen/recents_tv_text_padding_start"
+            android:layout_marginBottom="@dimen/recents_tv_text_padding_bottom"
+            android:ellipsize="end"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
new file mode 100644
index 0000000..9b89aa0
--- /dev/null
+++ b/packages/SystemUI/res/layout-television/recents_tv_task_card_view.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<com.android.systemui.recents.tv.views.TaskCardView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:layout_gravity="center"
+        android:layout_centerInParent="true"
+        android:clipToPadding="false"
+        android:orientation="vertical" >
+    <include layout="@layout/recents_tv_card_info_field"/>
+    <LinearLayout
+            android:id="@+id/card_view_thumbnail"
+            android:layout_width="@dimen/recents_tv_card_width"
+            android:layout_height="@dimen/recents_tv_screenshot_height"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:background="@color/recents_tv_card_background_color"
+            android:layout_centerHorizontal="true" >
+
+        <ImageView
+                android:id="@+id/card_view_banner_icon"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_centerHorizontal="true"
+                android:scaleType="centerCrop"
+                android:gravity="center" />
+
+    </LinearLayout>
+    <include layout="@layout/recents_tv_card_dismiss"/>
+</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
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/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 3865020..63b759b 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -15,10 +15,10 @@
   ~ limitations under the License
   -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/keyboard_shortcuts_keyword_wrapper"
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:minHeight="48dp"
         android:paddingStart="24dp"
         android:paddingEnd="24dp"
         android:paddingBottom="8dp">
@@ -29,7 +29,8 @@
             android:layout_marginEnd="32dp"
             android:layout_gravity="center_vertical"
             android:visibility="gone"
-            android:layout_alignParentStart="true"/>
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"/>
     <TextView
             android:id="@+id/keyboard_shortcuts_keyword"
             android:layout_toEndOf="@+id/keyboard_shortcuts_icon"
@@ -41,7 +42,8 @@
             android:maxLines="5"
             android:singleLine="false"
             android:scrollHorizontally="false"
-            android:layout_alignParentStart="true"/>
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"/>
     <com.android.systemui.statusbar.KeyboardShortcutKeysLayout
             android:id="@+id/keyboard_shortcuts_item_container"
             android:layout_toEndOf="@+id/keyboard_shortcuts_keyword"
@@ -50,5 +52,6 @@
             android:layout_height="wrap_content"
             android:layout_alignParentEnd="true"
             android:textSize="14sp"
-            android:scrollHorizontally="false"/>
+            android:scrollHorizontally="false"
+            android:layout_centerVertical="true"/>
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
index 0cecb96..5db6789 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -21,4 +21,4 @@
         android:padding="@dimen/ksh_item_padding"
         android:layout_marginStart="@dimen/ksh_item_margin_start"
         android:scaleType="fitXY"
-        android:background="@color/ksh_key_item_background"/>
+        android:background="@drawable/ksh_key_item_background"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 1215029..31a8773 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -19,7 +19,8 @@
           android:layout_height="wrap_content"
           android:padding="@dimen/ksh_item_padding"
           android:layout_marginStart="@dimen/ksh_item_margin_start"
-          android:background="@color/ksh_key_item_background"
+          android:background="@drawable/ksh_key_item_background"
           android:textColor="@color/ksh_key_item_color"
           android:singleLine="true"
+          android:gravity="center"
           android:textSize="@dimen/ksh_item_text_size"/>
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/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index b88846b..5d3b5ff 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -84,6 +84,7 @@
             android:clickable="true"
             android:focusable="true"
             android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:contentDescription="@string/accessibility_quick_settings_expand"
             android:padding="12dp" />
 
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 186aaf6..ae89631 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -25,6 +25,14 @@
         android:layout_height="match_parent">
     </com.android.systemui.recents.views.RecentsView>
 
+    <!-- Incompatible task overlay -->
+    <ViewStub android:id="@+id/incompatible_app_overlay_stub"
+        android:inflatedId="@+id/incompatible_app_overlay"
+        android:layout="@layout/recents_incompatible_app_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="128dp"
+        android:layout_gravity="center_horizontal|top" />
+
     <!-- Nav Bar Scrim View -->
     <ImageView
         android:id="@+id/nav_bar_scrim"
diff --git a/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml b/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
new file mode 100644
index 0000000..2b49dd3
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
@@ -0,0 +1,30 @@
+<?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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:alpha="0"
+    android:background="#88000000"
+    android:forceHasOverlappingRendering="false">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:drawableTop="@drawable/recents_info_light"
+        android:drawablePadding="8dp"
+        android:text="@string/recents_incompatible_app_message" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index c813818..1978a93 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -18,34 +18,43 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:focusable="true">
-    <FrameLayout
-        android:id="@+id/task_view_content"
+    <com.android.systemui.recents.views.TaskViewThumbnail
+        android:id="@+id/task_view_thumbnail"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <com.android.systemui.recents.views.TaskViewThumbnail
-            android:id="@+id/task_view_thumbnail"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" />
-        <include layout="@layout/recents_task_view_header" />
-        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-            android:id="@+id/lock_to_app_fab"
-            android:layout_width="@dimen/recents_lock_to_app_size"
-            android:layout_height="@dimen/recents_lock_to_app_size"
-            android:layout_gravity="bottom|right"
-            android:layout_marginRight="15dp"
-            android:layout_marginBottom="15dp"
-            android:translationZ="4dp"
-            android:contentDescription="@string/recents_lock_to_app_button_label"
-            android:background="@drawable/recents_lock_to_task_button_bg"
-            android:visibility="invisible"
-            android:alpha="0">
-            <ImageView
-                android:layout_width="@dimen/recents_lock_to_app_icon_size"
-                android:layout_height="@dimen/recents_lock_to_app_icon_size"
-                android:layout_gravity="center"
-                android:src="@drawable/recents_lock_to_app_pin" />
-        </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-    </FrameLayout>
+        android:layout_height="match_parent" />
+
+    <include layout="@layout/recents_task_view_header" />
+
+    <!-- TODO: Move this into a view stub -->
+    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+        android:id="@+id/lock_to_app_fab"
+        android:layout_width="@dimen/recents_lock_to_app_size"
+        android:layout_height="@dimen/recents_lock_to_app_size"
+        android:layout_gravity="bottom|right"
+        android:layout_marginRight="15dp"
+        android:layout_marginBottom="15dp"
+        android:translationZ="4dp"
+        android:contentDescription="@string/recents_lock_to_app_button_label"
+        android:background="@drawable/recents_lock_to_task_button_bg"
+        android:visibility="invisible"
+        android:alpha="0">
+        <ImageView
+            android:layout_width="@dimen/recents_lock_to_app_icon_size"
+            android:layout_height="@dimen/recents_lock_to_app_icon_size"
+            android:layout_gravity="center"
+            android:src="@drawable/recents_lock_to_app_pin" />
+    </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+    <!-- The incompatible app toast -->
+    <ViewStub android:id="@+id/incompatible_app_toast_stub"
+                android:inflatedId="@+id/incompatible_app_toast"
+                android:layout="@*android:layout/transient_notification"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top|center_horizontal"
+                android:layout_marginTop="48dp"
+                android:layout_marginLeft="16dp"
+                android:layout_marginRight="16dp" />
 </com.android.systemui.recents.views.TaskView>
 
 
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 2b3c5df..5ee242d 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -31,40 +31,20 @@
         android:paddingBottom="8dp"
         android:paddingStart="16dp"
         android:paddingEnd="12dp" />
-    <LinearLayout
-        android:id="@+id/title_container"
+    <TextView
+        android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
-        android:orientation="vertical">
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            android:textSize="16sp"
-            android:textColor="#ffffffff"
-            android:text="@string/recents_empty_message"
-            android:fontFamily="sans-serif-medium"
-            android:singleLine="true"
-            android:maxLines="1"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-        <TextView
-            android:id="@+id/sub_title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            android:textSize="11sp"
-            android:textColor="#ffffffff"
-            android:text="@string/recents_launch_non_dockable_task_label"
-            android:fontFamily="sans-serif-medium"
-            android:singleLine="true"
-            android:maxLines="1"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal"
-            android:visibility="gone" />
-    </LinearLayout>
+        android:textSize="16sp"
+        android:textColor="#ffffffff"
+        android:text="@string/recents_empty_message"
+        android:fontFamily="sans-serif-medium"
+        android:singleLine="true"
+        android:maxLines="1"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal"
+        android:forceHasOverlappingRendering="false" />
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/move_task"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
deleted file mode 100644
index 766ef60..0000000
--- a/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
+++ /dev/null
@@ -1,97 +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.
--->
-<com.android.systemui.recents.tv.views.TaskCardView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:focusable="true"
-    android:focusableInTouchMode="true"
-    android:layout_gravity="center"
-    android:layout_centerInParent="true"
-    android:orientation="vertical"
-    android:layoutDirection="ltr">
-
-    <LinearLayout
-            android:id="@+id/recents_tv_card"
-            android:layout_width="@dimen/recents_tv_card_width"
-            android:layout_height="wrap_content"
-            android:layout_centerInParent="true"
-            android:layout_gravity="center"
-            android:orientation="vertical" >
-        <LinearLayout
-                android:id="@+id/card_info_field"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-            <ImageView
-                    android:id="@+id/card_extra_badge"
-                    android:layout_width="@dimen/recents_tv_card_extra_badge_size"
-                    android:layout_height="@dimen/recents_tv_card_extra_badge_size"
-                    android:layout_marginBottom="@dimen/recents_tv_icon_padding_bottom"
-                    android:layout_marginEnd="@dimen/recents_tv_icon_padding_end"
-                    android:scaleType="fitCenter"
-                    android:layout_centerVertical="true"
-                    android:layout_alignParentRight="true" />
-            <TextView
-                    android:id="@+id/card_title_text"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_alignParentTop="false"
-                    android:includeFontPadding="true"
-                    android:minLines="1"
-                    android:maxLines="1"
-                    android:textColor="@color/recents_tv_card_title_text_color"
-                    android:fontFamily="@string/font_roboto_regular"
-                    android:textSize="@dimen/recents_tv_title_text_size"
-                    android:layout_marginBottom="@dimen/recents_tv_text_padding_bottom"
-                    android:ellipsize="end"/>
-        </LinearLayout>
-        <ImageView
-                android:id="@+id/card_view_thumbnail"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/recents_tv_screenshot_height"
-                android:scaleType="centerCrop"
-                android:gravity="center"
-                android:layout_alignParentTop="true"
-                android:layout_centerHorizontal="true"
-                android:layout_below="@id/card_title_text" />
-    </LinearLayout>
-    <LinearLayout
-            android:id="@+id/card_dismiss"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:layout_gravity="center_horizontal"
-            android:layout_below="@id/recents_tv_card"
-            android:alpha="0.0">
-        <ImageView
-                android:id="@+id/card_dismiss_icon"
-                android:layout_width="@dimen/recents_tv_dismiss_icon_size"
-                android:layout_height="@dimen/recents_tv_dismiss_icon_size"
-                android:layout_gravity="center_horizontal"
-                android:layout_marginTop="@dimen/recents_tv_dismiss_icon_top_margin"
-                android:layout_marginBottom="@dimen/recents_tv_dismiss_icon_bottom_margin"
-                android:src="@drawable/ic_cancel_white_24dp" />
-        <TextView
-                android:id="@+id/card_dismiss_text"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textSize="@dimen/recents_tv_dismiss_text_size"
-                android:fontFamily="@string/font_roboto_light"
-                android:textColor="@color/recents_tv_dismiss_text_color"
-                android:text="@string/recents_tv_dismiss"
-                android:layout_gravity="center_horizontal" />
-    </LinearLayout>
-</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
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..0a2f320 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.PipControlButtonView
+        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.PipControlButtonView
+        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.PipControlButtonView
+        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_onboarding.xml b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
index b0814cf..288ad1e 100644
--- a/packages/SystemUI/res/layout/tv_pip_onboarding.xml
+++ b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
@@ -17,47 +17,81 @@
 */
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/pip_onboarding"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="#C00288D1"
-    android:gravity="top|center_horizontal"
+    android:background="#B3000000"
     android:orientation="vertical">
 
-    <!-- A rectangle arounds the PIP.
-         Size and positions will be programatically set up
-         to comply with config_defaultPictureInPictureBounds. -->
     <ImageView
-        android:id="@+id/pip_outline"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:src="@drawable/tv_pip_outline" />
+        android:id="@+id/remote"
+        android:layout_width="72dp"
+        android:layout_height="273dp"
+        android:layout_marginTop="136dp"
+        android:layout_marginStart="304dp"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
+        android:adjustViewBounds="true"
+        android:src="@drawable/remote"
+        android:alpha="0" />
+    <ImageView
+        android:id="@+id/remote_button"
+        android:layout_width="50dp"
+        android:layout_height="50dp"
+        android:layout_marginTop="256dp"
+        android:layout_marginStart="315dp"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
+        android:scaleType="fitXY"
+        android:src="@drawable/tv_pip_onboarding_remote"
+        android:alpha="0" />
     <TextView
+        android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="24dp"
-        android:paddingStart="24dp"
-        android:paddingEnd="24dp"
+        android:layout_marginTop="188dp"
+        android:layout_marginStart="406dp"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
         android:fontFamily="sans-serif"
-        android:textSize="16sp"
+        android:textSize="24sp"
         android:textColor="#EEEEEE"
-        android:lineSpacingMultiplier="1.28"
-        android:gravity="top|center_horizontal"
-        android:text="@string/pip_onboarding_description" />
+        android:text="@string/pip_onboarding_title"
+        android:alpha="0" />
+    <TextView
+        android:id="@+id/description"
+        android:layout_width="200dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:layout_marginStart="408dp"
+        android:layout_below="@id/title"
+        android:layout_alignParentStart="true"
+        android:fontFamily="sans-serif"
+        android:textSize="14sp"
+        android:textColor="#EEEEEE"
+        android:lineSpacingMultiplier="1.46286"
+        android:text="@string/pip_onboarding_description"
+        android:alpha="0" />
     <Button
-        android:id="@+id/close"
+        android:id="@+id/button"
         android:layout_width="wrap_content"
-        android:layout_height="36dp"
-        android:layout_marginTop="24dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_marginStart="408dp"
+        android:layout_below="@id/description"
+        android:layout_alignParentStart="true"
         android:gravity="center"
+        android:paddingTop="10dp"
+        android:paddingBottom="10dp"
         android:paddingStart="24dp"
         android:paddingEnd="24dp"
         android:fontFamily="sans-serif-condensed"
         android:textSize="16sp"
-        android:textColor="#026089"
+        android:textColor="#FFFFFF"
         android:textAllCaps="true"
         android:text="@string/pip_onboarding_button"
-        android:background="#EEEEEE"
+        android:alpha="0"
+        android:background="#009688"
         android:elevation="4dp" />
-</LinearLayout>
+</RelativeLayout>
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 d9fcf42..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>
@@ -166,11 +168,11 @@
     <color name="switch_accent_color">#ff7fcac3</color>
 
     <!-- Keyboard shortcuts colors -->
-    <color name="ksh_system_group_color">#ff00bcd4</color>
+    <color name="ksh_system_group_color">@color/material_deep_teal_500</color>
     <color name="ksh_application_group_color">#fff44336</color>
     <color name="ksh_keyword_color">#d9000000</color>
     <color name="ksh_key_item_color">@color/material_grey_600</color>
-    <color name="ksh_key_item_background">#eeeeee</color>
+    <color name="ksh_key_item_background">@color/material_grey_100</color>
 
     <!-- Background color of edit overflow -->
     <color name="qs_edit_overflow_bg">#455A64</color>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 4126d3c..3817da0 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -17,7 +17,8 @@
  */
 -->
 <resources>
-    <color name="recents_tv_card_background_color">#FF37474F</color>
+    <color name="recents_tv_card_background_color">#FF263238</color>
     <color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
     <color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
+    <color name="recents_tv_text_shadow_color">#7F000000</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cf2e338..03b9837 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -606,8 +606,8 @@
     <dimen name="recents_layout_z_min">3dp</dimen>
     <dimen name="recents_layout_z_max">24dp</dimen>
 
-    <!-- The margin between the freeform and stack.  We also don't want this to change across 
-         configurations that Recents can be opened in, so we define them statically for all 
+    <!-- The margin between the freeform and stack.  We also don't want this to change across
+         configurations that Recents can be opened in, so we define them statically for all
          display sizes. -->
     <dimen name="recents_freeform_layout_bottom_margin">16dp</dimen>
 
@@ -651,4 +651,7 @@
 
     <!-- The amount to translate when animating the removal of a task. -->
     <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
+
+    <!-- The alpha to apply to the recents row when it doesn't have focus -->
+    <item name="recents_recents_row_dim_alpha" format="float" type="dimen">0.5</item>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index f536f86..367dd1d 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -21,11 +21,13 @@
     <dimen name="recents_tv_card_width">240dip</dimen>
     <dimen name="recents_tv_screenshot_height">135dip</dimen>
     <dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
-    <dimen name="recents_tv_banner_width">114dip</dimen>
-    <dimen name="recents_tv_banner_height">64dip</dimen>
+    <dimen name="recents_tv_banner_width">130dip</dimen>
+    <dimen name="recents_tv_banner_height">72dip</dimen>
+    <dimen name="recents_tv_fallback_icon_width">40dip</dimen>
+    <dimen name="recents_tv_fallback_icon_height">40dip</dimen>
     <dimen name="recents_tv_banner_margin_top">16dip</dimen>
     <dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
-    <dimen name="recents_tv_icon_padding_end">12dip</dimen>
+    <dimen name="recents_tv_text_padding_start">12dip</dimen>
     <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
 
     <!-- Padding for grid view in recents view on tv -->
@@ -54,8 +56,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/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index 6984d5a..ebfd8ab 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -24,4 +24,7 @@
     <integer name="recents_home_duration">400</integer>
     <!-- Delay between the start of slide in animation for each card. -->
     <integer name="recents_home_delay">40</integer>
+
+    <!-- Duration of the onboarding animation duration -->
+    <integer name="tv_pip_onboarding_anim_duration">1000</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a33b7a3..a4d7a18 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -735,10 +735,8 @@
     <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
     <!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
     <string name="recents_stack_action_button_label">Clear all</string>
-    <!-- Recents: Non-dockable task drag message. [CHAR LIMIT=NONE] -->
-    <string name="recents_drag_non_dockable_task_message">This app does not support multi-window</string>
-    <!-- Recents: Non-dockable task launch sub header. [CHAR LIMIT=NONE] -->
-    <string name="recents_launch_non_dockable_task_label">App does not support multi-window</string>
+    <!-- Recents: Incompatible task message. [CHAR LIMIT=NONE] -->
+    <string name="recents_incompatible_app_message">App doesn\'t support split screen</string>
 
     <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
     <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1594,4 +1592,10 @@
     <!-- Warning message when we try to dock a non-resizeble tasks and launch it in fullscreen instead. -->
     <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
 
+    <!-- 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/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 52aba0d..dcd1654 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -31,8 +31,10 @@
     <string name="pip_hold_home">Hold <b>HOME</b> to control PIP</string>
     <!-- Picture-in-Picture (PIP) onboarding screen -->
     <eat-comment />
+    <!-- Title for picture-in-picture (PIP) onboarding screen to indicate that an user is in PIP mode. [CHAR LIMIT=NONE] -->
+    <string name="pip_onboarding_title">PIP mode</string>
     <!-- Description for picture-in-picture (PIP) onboarding screen to indicate that longpress HOME key to control PIP. [CHAR LIMIT=NONE] -->
-    <string name="pip_onboarding_description">Press and hold the HOME button to control PIP</string>
+    <string name="pip_onboarding_description">To control PIP press and hold the <b>HOME</b> button on the remote</string>
     <!-- Button to close picture-in-picture (PIP) onboarding screen. -->
     <string name="pip_onboarding_button">Got it</string>
     <!-- Dismiss icon description -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f560a13..aeb484f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -33,6 +33,7 @@
         <item name="android:windowBackground">@color/transparent</item>
         <item name="android:colorBackgroundCacheHint">@null</item>
         <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowDisablePreview">true</item>
     </style>
 
     <!-- Performance optimized Recents theme (no wallpaper) -->
@@ -308,6 +309,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 41926e0..4d8e33d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -847,17 +847,16 @@
 
     private void doKeyguardLaterForChildProfilesLocked() {
         UserManager um = UserManager.get(mContext);
-        List<UserInfo> profiles = um.getEnabledProfiles(UserHandle.myUserId());
-        for (UserInfo info : profiles) {
-            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) {
-                long userTimeout = getLockTimeout(info.id);
+        for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) {
+            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) {
+                long userTimeout = getLockTimeout(profileId);
                 if (userTimeout == 0) {
                     doKeyguardForChildProfilesLocked();
                 } else {
                     long userWhen = SystemClock.elapsedRealtime() + userTimeout;
                     Intent lockIntent = new Intent(DELAYED_LOCK_PROFILE_ACTION);
                     lockIntent.putExtra("seq", mDelayedProfileShowingSequence);
-                    lockIntent.putExtra(Intent.EXTRA_USER_ID, info.id);
+                    lockIntent.putExtra(Intent.EXTRA_USER_ID, profileId);
                     PendingIntent lockSender = PendingIntent.getBroadcast(
                             mContext, 0, lockIntent, PendingIntent.FLAG_CANCEL_CURRENT);
                     mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
@@ -869,10 +868,9 @@
 
     private void doKeyguardForChildProfilesLocked() {
         UserManager um = UserManager.get(mContext);
-        List<UserInfo> profiles = um.getEnabledProfiles(UserHandle.myUserId());
-        for (UserInfo info : profiles) {
-            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) {
-                lockProfile(info.id);
+        for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) {
+            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) {
+                lockProfile(profileId);
             }
         }
     }
@@ -1478,13 +1476,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);
-                List <UserInfo> userHandles = um.getProfiles(currentUser.getIdentifier());
-                for (UserInfo ui : userHandles) {
-                    mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, ui.getUserHandle());
+                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 ba07bf4..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);
 
@@ -118,6 +120,10 @@
         }
         ImageView first = (ImageView) getChildAt(firstIndex);
         ImageView second = (ImageView) getChildAt(secondIndex);
+        if (second == null) {
+            // Weird state where number of pages must not have propagated yet.
+            return;
+        }
         // Lay the two views on top of each other.
         second.setTranslationX(first.getX() - second.getX());
 
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/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index 9e40cfd..44b38f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -93,6 +93,12 @@
         }
     }
 
+    @Override
+    public boolean hasOverlappingRendering() {
+        // Avoid layers for this layout - we don't need them.
+        return false;
+    }
+
     /**
      * Update the accessibility order for this view.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index e8e17b1..8925d45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -387,7 +387,7 @@
                 }
             }
         }
-        if (mTiles.get(mTiles.size() - 1) == null) {
+        if (mTiles.size() - 1 == mTileDividerIndex) {
             mTiles.remove(mTiles.size() - 1);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index ab21532..f34df75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -16,13 +16,18 @@
 package com.android.systemui.qs.external;
 
 import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
+import libcore.util.Objects;
 
 /**
  * Manages the priority which lets {@link TileServices} make decisions about which tiles
@@ -68,6 +73,12 @@
         mHandler = handler;
         mStateManager = tileLifecycleManager;
         mStateManager.setQSService(tileServices);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mServices.getContext().registerReceiverAsUser(mUninstallReceiver,
+                new UserHandle(ActivityManager.getCurrentUser()), filter, null, mHandler);
     }
 
     public boolean isActiveTile() {
@@ -106,6 +117,7 @@
     }
 
     public void handleDestroy() {
+        mServices.getContext().unregisterReceiver(mUninstallReceiver);
         mStateManager.handleDestroy();
     }
 
@@ -198,4 +210,23 @@
             mServices.recalculateBindAllowance();
         }
     };
+
+    private final BroadcastReceiver mUninstallReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                return;
+            }
+            if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                return;
+            }
+            Uri data = intent.getData();
+            String pkgName = data.getEncodedSchemeSpecificPart();
+            final ComponentName component = mStateManager.getComponent();
+            if (!Objects.equal(pkgName, component.getPackageName())) {
+                return;
+            }
+            mServices.getHost().removeTile(component);
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index f36d013..2ab6b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -72,6 +72,10 @@
         return mContext;
     }
 
+    public QSTileHost getHost() {
+        return mHost;
+    }
+
     public TileServiceManager getTileWrapper(CustomTile tile) {
         ComponentName component = tile.getComponent();
         TileServiceManager service = onCreateTileService(component);
@@ -89,6 +93,7 @@
     public void freeService(CustomTile tile, TileServiceManager service) {
         synchronized (mServices) {
             service.setBindAllowed(false);
+            service.handleDestroy();
             mServices.remove(tile);
             mTiles.remove(tile.getComponent());
             final String slot = tile.getComponent().getClassName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index e494fd8..77eaa3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -130,6 +130,8 @@
             }
         };
         state.label = percentage;
+        state.contentDescription = mContext.getString(R.string.accessibility_quick_settings_battery,
+                percentage);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index 9a00d95..1240e05 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -27,7 +27,7 @@
     void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);
 
     void updateRecentsVisibility(boolean visible);
-    void startScreenPinning();
+    void startScreenPinning(int taskId);
     void sendRecentsDrawnEvent();
     void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
     void sendLaunchRecentsEvent();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 287bb22..a227e1d 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 {
@@ -435,7 +449,7 @@
                 mDraggingInRecentsCurrentUser = currentUser;
                 return true;
             } else {
-                Toast.makeText(mContext, R.string.recents_drag_non_dockable_task_message,
+                Toast.makeText(mContext, R.string.recents_incompatible_app_message,
                         Toast.LENGTH_SHORT).show();
                 return false;
             }
@@ -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)) {
@@ -564,13 +598,13 @@
     public final void onBusEvent(final ScreenPinningRequestEvent event) {
         int processUser = sSystemServicesProxy.getProcessUser();
         if (sSystemServicesProxy.isSystemUser(processUser)) {
-            mImpl.onStartScreenPinning(event.applicationContext);
+            mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
         } else {
             postToSystemUser(new Runnable() {
                 @Override
                 public void run() {
                     try {
-                        mUserToSystemCallbacks.startScreenPinning();
+                        mUserToSystemCallbacks.startScreenPinning(event.taskId);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b1d9555..4d69280 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
@@ -58,22 +59,25 @@
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
 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;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.AnimationProps;
 import com.android.systemui.recents.views.RecentsView;
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.statusbar.BaseStatusBar;
@@ -90,10 +94,11 @@
     private final static boolean DEBUG = false;
 
     public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
+    public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150;
 
     private RecentsPackageMonitor mPackageMonitor;
     private long mLastTabKeyEventTime;
-    private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
+    private int mLastDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
     private boolean mFinishedOnStartup;
     private boolean mIgnoreAltTabRelease;
     private boolean mIsVisible;
@@ -101,6 +106,7 @@
     // Top level views
     private RecentsView mRecentsView;
     private SystemBarScrimViews mScrimViews;
+    private View mIncompatibleAppOverlay;
 
     // Runnables to finish the Recents activity
     private Intent mHomeIntent;
@@ -270,7 +276,7 @@
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
 
-        mLastOrientation = getResources().getConfiguration().orientation;
+        mLastDeviceOrientation = Utilities.getAppConfiguration(this).orientation;
         mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
         mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
             @Override
@@ -420,13 +426,12 @@
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-
         // Notify of the config change
-        int newOrientation = getResources().getConfiguration().orientation;
+        int newDeviceOrientation = Utilities.getAppConfiguration(this).orientation;
         int numStackTasks = mRecentsView.getStack().getStackTaskCount();
         EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
-                (mLastOrientation != newOrientation), numStackTasks > 0));
-        mLastOrientation = newOrientation;
+                (mLastDeviceOrientation != newDeviceOrientation), numStackTasks > 0));
+        mLastDeviceOrientation = newDeviceOrientation;
     }
 
     @Override
@@ -449,7 +454,7 @@
         int numStackTasks = stack.getStackTaskCount();
 
         EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
-                false /* fromOrientationChange */, numStackTasks > 0));
+                false /* fromDeviceOrientationChange */, numStackTasks > 0));
 
         if (mRecentsView != null) {
             mRecentsView.updateStack(stack);
@@ -674,6 +679,30 @@
         MetricsLogger.count(this, "overview_app_info", 1);
     }
 
+    public final void onBusEvent(ShowIncompatibleAppOverlayEvent event) {
+        if (mIncompatibleAppOverlay == null) {
+            mIncompatibleAppOverlay = Utilities.findViewStubById(this,
+                    R.id.incompatible_app_overlay_stub).inflate();
+            mIncompatibleAppOverlay.setWillNotDraw(false);
+            mIncompatibleAppOverlay.setVisibility(View.VISIBLE);
+        }
+        mIncompatibleAppOverlay.animate()
+                .alpha(1f)
+                .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
+                .setInterpolator(Interpolators.ALPHA_IN)
+                .start();
+    }
+
+    public final void onBusEvent(HideIncompatibleAppOverlayEvent event) {
+        if (mIncompatibleAppOverlay != null) {
+            mIncompatibleAppOverlay.animate()
+                    .alpha(0f)
+                    .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .start();
+        }
+    }
+
     public final void onBusEvent(DeleteTaskDataEvent event) {
         // Remove any stored data from the loader
         RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -723,6 +752,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);
@@ -740,6 +776,8 @@
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         super.dump(prefix, fd, writer, args);
+        EventBus.getDefault().dump(prefix, writer);
+
         String id = Integer.toHexString(System.identityHashCode(this));
 
         writer.print(prefix); writer.print(TAG);
@@ -750,6 +788,5 @@
         if (mRecentsView != null) {
             mRecentsView.dump(prefix, writer);
         }
-        EventBus.getDefault().dump(prefix, writer);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index fda340d..618a2e2 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();
     }
 
     /**
@@ -215,11 +221,11 @@
      * visibility change events through to the system user via
      * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
      */
-    public void onStartScreenPinning(Context context) {
+    public void onStartScreenPinning(Context context, int taskId) {
         SystemUIApplication app = (SystemUIApplication) context;
         PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
         if (statusBar != null) {
-            statusBar.showScreenPinningRequest(false);
+            statusBar.showScreenPinningRequest(taskId, false);
         }
     }
 
@@ -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);
     }
 
     /**
@@ -687,7 +689,7 @@
             TaskStackViewScroller stackScroller = stackView.getScroller();
 
             stackView.updateLayoutAlgorithm(true /* boundScroll */);
-            stackView.updateToInitialState(true /* scrollToInitialState */);
+            stackView.updateToInitialState();
 
             for (int i = tasks.size() - 1; i >= 0; i--) {
                 Task task = tasks.get(i);
@@ -740,7 +742,7 @@
 
         // Get the transform for the running task
         stackView.updateLayoutAlgorithm(true /* boundScroll */);
-        stackView.updateToInitialState(true /* scrollToInitialState */);
+        stackView.updateToInitialState();
         stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
                 stackView.getScroller().getStackScroll(), mTmpTransform, null);
         return mTmpTransform;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index ffeb4a1..913da18 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -82,8 +82,8 @@
     }
 
     @Override
-    public void startScreenPinning() {
-        mImpl.onStartScreenPinning(mContext);
+    public void startScreenPinning(int taskId) {
+        mImpl.onStartScreenPinning(mContext, taskId);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 13d4acb..7a604ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -52,6 +52,9 @@
 
     private RequestWindowView mRequestWindow;
 
+    // Id of task to be pinned or locked.
+    private int taskId;
+
     public ScreenPinningRequest(Context context) {
         mContext = context;
         mAccessibilityService = (AccessibilityManager)
@@ -67,9 +70,11 @@
         }
     }
 
-    public void showPrompt(boolean allowCancel) {
+    public void showPrompt(int taskId, boolean allowCancel) {
         clearPrompt();
 
+        this.taskId = taskId;
+
         mRequestWindow = new RequestWindowView(mContext, allowCancel);
 
         mRequestWindow.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
@@ -106,7 +111,7 @@
     public void onClick(View v) {
         if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
             try {
-                ActivityManagerNative.getDefault().startLockTaskModeOnCurrent();
+                ActivityManagerNative.getDefault().startSystemLockTaskMode(taskId);
             } catch (RemoteException e) {}
         }
         clearPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
index e3bc2a7..53b67cf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
@@ -24,13 +24,13 @@
 public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
 
     public final boolean fromMultiWindow;
-    public final boolean fromOrientationChange;
+    public final boolean fromDeviceOrientationChange;
     public final boolean hasStackTasks;
 
-    public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromOrientationChange,
+    public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromDeviceOrientationChange,
             boolean hasStackTasks) {
         this.fromMultiWindow = fromMultiWindow;
-        this.fromOrientationChange = fromOrientationChange;
+        this.fromDeviceOrientationChange = fromDeviceOrientationChange;
         this.hasStackTasks = hasStackTasks;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
index 75e459a..d460917 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
@@ -26,8 +26,10 @@
 public class ScreenPinningRequestEvent extends EventBus.Event {
 
     public final Context applicationContext;
+    public final int taskId;
 
-    public ScreenPinningRequestEvent(Context context) {
+    public ScreenPinningRequestEvent(Context context, int taskId) {
         this.applicationContext = context.getApplicationContext();
+        this.taskId = taskId;
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
similarity index 69%
rename from telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
rename to packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
index 1e74b31..d6ef636 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony;
+package com.android.systemui.recents.events.ui;
 
-parcelable TelephonyEvent;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when a user stops draggin an incompatible app task.
+ */
+public class HideIncompatibleAppOverlayEvent extends EventBus.Event {
+    // Simple event
+}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
similarity index 68%
copy from telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
copy to packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
index 1e74b31..3a4350e 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony;
+package com.android.systemui.recents.events.ui;
 
-parcelable TelephonyEvent;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when a user starts dragging an incompatible app task.
+ */
+public class ShowIncompatibleAppOverlayEvent extends EventBus.Event {
+    // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 2d5addb..63bcc2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -99,6 +99,7 @@
     static {
         sBitmapOptions = new BitmapFactory.Options();
         sBitmapOptions.inMutable = true;
+        sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
     }
 
     final static List<String> sRecentsBlacklist;
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 69d98af..f6cc12b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -18,18 +18,24 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.animation.RectEvaluator;
 import android.annotation.FloatRange;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 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;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewParent;
+import android.view.ViewStub;
 
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.views.TaskViewTransform;
@@ -68,7 +74,7 @@
             };
 
     public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
-
+    public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
     public static final Rect EMPTY_RECT = new Rect();
 
     /**
@@ -220,6 +226,20 @@
     }
 
     /**
+     * Returns a view stub for the given view id.
+     */
+    public static ViewStub findViewStubById(View v, int stubId) {
+        return (ViewStub) v.findViewById(stubId);
+    }
+
+    /**
+     * Returns a view stub for the given view id.
+     */
+    public static ViewStub findViewStubById(Activity a, int stubId) {
+        return (ViewStub) a.findViewById(stubId);
+    }
+
+    /**
      * Updates {@param transforms} to be the same size as {@param tasks}.
      */
     public static void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
@@ -245,6 +265,22 @@
     }
 
     /**
+     * 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 the application configuration, which is independent of the activity's current
+     * configuration in multiwindow.
+     */
+    public static Configuration getAppConfiguration(Context context) {
+        return context.getApplicationContext().getResources().getConfiguration();
+    }
+
+    /**
      * 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/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index fbb5987..95e276f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -299,7 +299,7 @@
                 if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
                     if (animateBounds) {
                         PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
-                                Utilities.DRAWABLE_RECT, new RectEvaluator(new Rect()),
+                                Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
                                 dockAreaOverlay.getBounds(), bounds);
                         animators.add(ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop));
                     } else {
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..7378102 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;
@@ -106,7 +107,9 @@
         public void onMoveToFullscreen() {
             // Recents should be dismissed when PIP moves to fullscreen. If not, Recents will
             // be unnecessarily shown in the scenario: PIP->Fullscreen->PIP.
-            dismissRecentsToLaunchTargetTaskOrHome();
+            // Do not show Recents close animation because PIP->Fullscreen animation will be shown
+            // instead.
+            dismissRecentsToLaunchTargetTaskOrHome(false);
         }
 
         @Override
@@ -117,7 +120,7 @@
             new PipRecentsOverlayManager.Callback() {
                 @Override
                 public void onClosed() {
-                    dismissRecentsToLaunchTargetTaskOrHome();
+                    dismissRecentsToLaunchTargetTaskOrHome(true);
                 }
 
                 @Override
@@ -210,13 +213,15 @@
         }
     }
 
-    boolean dismissRecentsToLaunchTargetTaskOrHome() {
+    boolean dismissRecentsToLaunchTargetTaskOrHome(boolean animate) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
             // If we have a focused Task, launch that Task now
-            if (mRecentsView.launchPreviousTask()) return true;
+            if (mRecentsView.launchPreviousTask(animate)) {
+              return true;
+            }
             // If none of the other cases apply, then just go Home
-            dismissRecentsToHome(true /* animateTaskViews */);
+            dismissRecentsToHome(animate /* animateTaskViews */);
         }
         return false;
     }
@@ -246,7 +251,7 @@
         dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
         dismissEvent.addPostAnimationCallback(closeSystemWindows);
 
-        if(mTaskStackHorizontalGridView.getChildCount() > 0) {
+        if (mTaskStackHorizontalGridView.getChildCount() > 0 && animateTaskViews) {
             mHomeRecentsEnterExitAnimationHolder.startExitAnimation(dismissEvent);
         } else {
             closeSystemWindows.run();
@@ -343,6 +348,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
@@ -366,7 +378,7 @@
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         if(mLaunchedFromHome) {
-            mHomeRecentsEnterExitAnimationHolder.startEnterAnimation();
+            mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown());
         }
         EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
     }
@@ -455,7 +467,7 @@
         if (launchState.launchedFromHome) {
             dismissRecentsToHome(true /* animateTaskViews */);
         } else {
-            dismissRecentsToLaunchTargetTaskOrHome();
+            dismissRecentsToLaunchTargetTaskOrHome(true);
         }
     }
 
@@ -494,12 +506,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);
         }
     }
 
@@ -555,6 +565,8 @@
             // as if it's the part of the Recents UI.
             mPipRecentsOverlayManager.requestFocus(
                     mTaskStackViewAdapter.getItemCount() > 0);
+        } else {
+            mPipRecentsOverlayManager.clearFocus();
         }
     }
 }
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..66d8576 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
@@ -18,6 +18,7 @@
 
 import android.animation.Animator;
 import android.content.res.Resources;
+import android.view.View;
 import android.widget.LinearLayout;
 import com.android.systemui.Interpolators;
 import com.android.systemui.recents.tv.views.TaskCardView;
@@ -26,17 +27,20 @@
 
 public class DismissAnimationsHolder {
     private LinearLayout mDismissArea;
-    private LinearLayout mTaskCardView;
-    private int mCardYDelta;
+    private LinearLayout mInfoField;
+    private View mThumbnailView;
+    private int mDismissEnterYDelta;
+    private int mDismissStartYDelta;
     private long mShortDuration;
     private long mLongDuration;
 
     public DismissAnimationsHolder(TaskCardView taskCardView) {
-        mTaskCardView = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
+        mInfoField = (LinearLayout) taskCardView.findViewById(R.id.card_info_field);
         mDismissArea = (LinearLayout) taskCardView.findViewById(R.id.card_dismiss);
-
+        mThumbnailView = taskCardView.findViewById(R.id.card_view_thumbnail);
         Resources res = taskCardView.getResources();
-        mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
+        mDismissEnterYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
+        mDismissStartYDelta = mDismissEnterYDelta * 2;
         mShortDuration =  res.getInteger(R.integer.dismiss_short_duration);
         mLongDuration =  res.getInteger(R.integer.dismiss_long_duration);
     }
@@ -47,10 +51,16 @@
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(1.0f);
 
-        mTaskCardView.animate()
+        mInfoField.animate()
                 .setDuration(mShortDuration)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .translationYBy(mCardYDelta)
+                .translationY(mDismissEnterYDelta)
+                .alpha(0.5f);
+
+        mThumbnailView.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationY(mDismissEnterYDelta)
                 .alpha(0.5f);
     }
 
@@ -60,10 +70,16 @@
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(0.0f);
 
-        mTaskCardView.animate()
+        mInfoField.animate()
                 .setDuration(mShortDuration)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .translationYBy(-mCardYDelta)
+                .translationY(0)
+                .alpha(1.0f);
+
+        mThumbnailView.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationY(0)
                 .alpha(1.0f);
     }
 
@@ -73,11 +89,25 @@
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(0.0f);
 
-        mTaskCardView.animate()
+        mInfoField.animate()
                 .setDuration(mLongDuration)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .translationYBy(mCardYDelta)
+                .translationY(mDismissStartYDelta)
                 .alpha(0.0f)
                 .setListener(listener);
+
+        mThumbnailView.animate()
+                .setDuration(mLongDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationY(mDismissStartYDelta)
+                .alpha(0.0f);
+    }
+
+    public void reset() {
+        mInfoField.setAlpha(1.0f);
+        mInfoField.setTranslationY(0);
+        mInfoField.animate().setListener(null);
+        mThumbnailView.setAlpha(1.0f);
+        mThumbnailView.setTranslationY(0);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
index 278de87..92718a3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
@@ -28,6 +28,7 @@
 
     private Context mContext;
     private TaskStackHorizontalGridView mGridView;
+    private float mDimAlpha;
     private long mDelay;
     private int mDuration;
     private int mTranslationX;
@@ -36,19 +37,19 @@
             TaskStackHorizontalGridView gridView) {
         mContext = context;
         mGridView = gridView;
+        mDimAlpha = mContext.getResources().getFloat(R.dimen.recents_recents_row_dim_alpha);
         mTranslationX = mContext.getResources()
                 .getDimensionPixelSize(R.dimen.recents_tv_home_recents_shift);
         mDelay = mContext.getResources().getInteger(R.integer.recents_home_delay);
         mDuration =  mContext.getResources().getInteger(R.integer.recents_home_duration);
     }
 
-    public void startEnterAnimation() {
+    public void startEnterAnimation(boolean isPipShown) {
         for(int i = 0; i < mGridView.getChildCount(); i++) {
             TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
             view.setTranslationX(-mTranslationX);
-            view.setAlpha(0.0f);
             view.animate()
-                    .alpha(1.0f)
+                    .alpha(isPipShown ? mDimAlpha : 1.0f)
                     .translationX(0)
                     .setDuration(mDuration)
                     .setStartDelay(mDelay * i)
@@ -76,7 +77,7 @@
     public void setEnterFromHomeStartingAnimationValues() {
         for(int i = 0; i < mGridView.getChildCount(); i++) {
             TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
-            view.setTranslationX(-mTranslationX);
+            view.setTranslationX(0);
             view.setAlpha(0.0f);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
index 28abc34..160835f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
@@ -29,8 +29,6 @@
  * Recents row's focus animation with PIP controls.
  */
 public class RecentsRowFocusAnimationHolder {
-    private static final float DIM_ALPHA = 0.5f;
-
     private View mView;
     private View mTitleView;
 
@@ -43,6 +41,7 @@
 
         Resources res = view.getResources();
         int duration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
+        float dimAlpha = res.getFloat(R.dimen.recents_recents_row_dim_alpha);
 
         mFocusGainAnimatorSet = new AnimatorSet();
         mFocusGainAnimatorSet.playTogether(
@@ -53,7 +52,7 @@
 
         mFocusLoseAnimatorSet = new AnimatorSet();
         mFocusLoseAnimatorSet.playTogether(
-                ObjectAnimator.ofFloat(mView, "alpha", DIM_ALPHA),
+                ObjectAnimator.ofFloat(mView, "alpha", dimAlpha),
                 ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
         mFocusLoseAnimatorSet.setDuration(duration);
         mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index 888561c..9edd5af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -26,6 +26,7 @@
 import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.tv.views.TaskCardView;
 
 public class ViewFocusAnimator implements View.OnFocusChangeListener {
     private final float mUnselectedScale;
@@ -94,10 +95,13 @@
 
         mTargetView.setScaleX(scale);
         mTargetView.setScaleY(scale);
-        mTargetView.setZ(z);
 
         mTargetView.setPadding((int) spacing, mTargetView.getPaddingTop(),
                 (int) spacing, mTargetView.getPaddingBottom());
+
+        if (mTargetView instanceof TaskCardView) {
+            ((TaskCardView) mTargetView).getThumbnailView().setZ(z);
+        }
     }
 
     public float getFocusProgress() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
index fb1127e..812bff1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
@@ -16,7 +16,6 @@
 package com.android.systemui.recents.tv.views;
 
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -122,11 +121,13 @@
         }
         try {
             Rect taskRect = taskView.getFocusedThumbnailRect();
-            Bitmap thumbnail = Bitmap.createScaledBitmap(task.thumbnail, taskRect.width(),
-                    taskRect.height(), false);
-            WindowManagerGlobal.getWindowManagerService()
-                    .overridePendingAppTransitionAspectScaledThumb(thumbnail, taskRect.left,
-                            taskRect.top, taskRect.width(), taskRect.height(), callback, true);
+            if (taskRect != null) {
+                Bitmap thumbnail = Bitmap.createScaledBitmap(task.thumbnail, taskRect.width(),
+                        taskRect.height(), false);
+                WindowManagerGlobal.getWindowManagerService()
+                        .overridePendingAppTransitionAspectScaledThumb(thumbnail, taskRect.left,
+                                taskRect.top, taskRect.width(), taskRect.height(), callback, true);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to override transition: " + e);
         }
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..4058c62 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
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -35,7 +34,6 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -114,7 +112,7 @@
         if (mTaskStackHorizontalView != null) {
             Task task = mTaskStackHorizontalView.getFocusedTask();
             if (task != null) {
-                launchTaskFomRecents(task);
+                launchTaskFomRecents(task, true);
                 return true;
             }
         }
@@ -122,12 +120,12 @@
     }
 
     /** Launches the task that recents was launched from if possible */
-    public boolean launchPreviousTask() {
+    public boolean launchPreviousTask(boolean animate) {
         if (mTaskStackHorizontalView != null) {
             TaskStack stack = mTaskStackHorizontalView.getStack();
             Task task = stack.getLaunchTarget();
             if (task != null) {
-                launchTaskFomRecents(task);
+                launchTaskFomRecents(task, animate);
                 return true;
             }
         }
@@ -139,18 +137,25 @@
      * attempt to scroll to focus the task before launching.
      * @param task
      */
-    private void launchTaskFomRecents(final Task task) {
-        if(task != mTaskStackHorizontalView.getFocusedTask()) {
-            if(mScrollListener != null) {
+    private void launchTaskFomRecents(final Task task, boolean animate) {
+        if (!animate) {
+            SystemServicesProxy ssp = Recents.getSystemServices();
+            ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
+            return;
+        }
+        mTaskStackHorizontalView.requestFocus();
+        Task focusedTask = mTaskStackHorizontalView.getFocusedTask();
+        if (focusedTask != null && task != focusedTask) {
+            if (mScrollListener != null) {
                 mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
             }
             mScrollListener = new OnScrollListener() {
                 @Override
                 public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                     super.onScrollStateChanged(recyclerView, newState);
-                    if(newState == RecyclerView.SCROLL_STATE_IDLE) {
+                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                         TaskCardView cardView = mTaskStackHorizontalView.getChildViewForTask(task);
-                        if(cardView != null) {
+                        if (cardView != null) {
                             mTransitionHelper.launchTaskFromRecents(mStack, task,
                                     mTaskStackHorizontalView, cardView, null, INVALID_STACK_ID);
                         } else {
@@ -250,8 +255,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..a72a7e6 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
@@ -17,10 +17,15 @@
 
 import android.animation.Animator;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -38,7 +43,8 @@
 
 public class TaskCardView extends LinearLayout {
 
-    private ImageView mThumbnailView;
+    private static final String TAG = "TaskCardView";
+    private View mThumbnailView;
     private TextView mTitleTextView;
     private ImageView mBadgeView;
     private Task mTask;
@@ -58,26 +64,28 @@
 
     public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mViewFocusAnimator = new ViewFocusAnimator(this);
         mDismissState = false;
+        Configuration config = getResources().getConfiguration();
+        setLayoutDirection(config.getLayoutDirection());
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
+        mThumbnailView = findViewById(R.id.card_view_thumbnail);
         mTitleTextView = (TextView) findViewById(R.id.card_title_text);
         mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
         mDismissAnimationsHolder = new DismissAnimationsHolder(this);
         View title = findViewById(R.id.card_info_field);
         mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
+        mViewFocusAnimator = new ViewFocusAnimator(this);
     }
 
     public void init(Task task) {
         mTask = task;
-        mThumbnailView.setImageBitmap(task.thumbnail);
         mTitleTextView.setText(task.title);
         mBadgeView.setImageDrawable(task.icon);
+        setThumbnailView();
     }
 
     public Task getTask() {
@@ -95,7 +103,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 +166,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 +227,7 @@
     }
 
     public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
+        mDismissState = false;
         mDismissAnimationsHolder.startDismissAnimation(listener);
     }
 
@@ -201,4 +240,70 @@
         super.onDetachedFromWindow();
         setDismissState(false);
     }
+
+    public void reset() {
+        mDismissState = false;
+        mRecentsRowFocusAnimationHolder.reset();
+        mDismissAnimationsHolder.reset();
+    }
+
+    private void setThumbnailView() {
+        ImageView screenshotView = (ImageView) findViewById(R.id.card_view_banner_icon);
+        PackageManager pm = getContext().getPackageManager();
+        if (mTask.thumbnail != null) {
+            setAsScreenShotView(mTask.thumbnail, screenshotView);
+        } else {
+            try {
+                Drawable banner = null;
+                if (mTask.key != null) {
+                    banner = pm.getActivityBanner(mTask.key.baseIntent);
+                }
+                if (banner != null) {
+                    setAsBannerView(banner, screenshotView);
+                } else {
+                    setAsIconView(mTask.icon, screenshotView);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Package not found : " + e);
+                setAsIconView(mTask.icon, screenshotView);
+            }
+        }
+    }
+
+    private void setAsScreenShotView(Bitmap screenshot, ImageView screenshotView) {
+        LayoutParams lp = (LayoutParams) screenshotView.getLayoutParams();
+        lp.width = getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_card_width);
+        lp.height = getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_screenshot_height);
+
+        screenshotView.setLayoutParams(lp);
+        screenshotView.setImageBitmap(screenshot);
+    }
+
+    private void setAsBannerView(Drawable banner, ImageView bannerView) {
+        LayoutParams lp = (LayoutParams) bannerView.getLayoutParams();
+        lp.width = getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_banner_width);
+        lp.height = getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_banner_height);
+
+        bannerView.setLayoutParams(lp);
+        bannerView.setImageDrawable(banner);
+    }
+
+    private void setAsIconView(Drawable icon, ImageView iconView) {
+        LayoutParams lp = (LayoutParams) iconView.getLayoutParams();
+        lp.width = getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_fallback_icon_width);
+        lp.height = getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_fallback_icon_height);
+
+        iconView.setLayoutParams(lp);
+        iconView.setImageDrawable(icon);
+    }
+
+    public View getThumbnailView() {
+        return mThumbnailView;
+    }
 }
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..9f52abd 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
@@ -33,9 +33,6 @@
 import com.android.systemui.recents.model.TaskStack.TaskStackCallbacks;
 import com.android.systemui.recents.views.AnimationProps;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Horizontal Grid View Implementation to show the Task Stack for TV.
  */
@@ -179,13 +176,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..eb3b02d 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
@@ -28,6 +28,7 @@
 import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.AnimationProps;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -40,10 +41,12 @@
     //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;
         private Task mTask;
+        private boolean mShouldReset;
         public ViewHolder(View v) {
             super(v);
             if(v instanceof TaskCardView) {
@@ -62,11 +65,10 @@
             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));
-                    ((Activity) (v.getContext())).finish();
                 }
             } catch (Exception e) {
                 Log.e(TAG, v.getContext()
@@ -74,6 +76,29 @@
             }
 
         }
+
+        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));
+                    mShouldReset = true;
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) { }
+
+                @Override
+                public void onAnimationRepeat(Animator animation) { }
+            };
+
+        }
     }
 
     public TaskStackHorizontalViewAdapter(List tasks) {
@@ -89,9 +114,9 @@
     @Override
     public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
             int viewType) {
-        View view = LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.recents_tv_task_card_view, parent, false);
-        ViewHolder viewHolder = new ViewHolder(view);
+        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+        ViewHolder viewHolder = new ViewHolder(
+                        inflater.inflate(R.layout.recents_tv_task_card_view, parent, false));
         return viewHolder;
     }
 
@@ -101,39 +126,49 @@
     }
 
     @Override
+    public void onViewDetachedFromWindow(ViewHolder holder) {
+        // We only want to reset on view detach if this is the last task being dismissed.
+        // This is so that we do not reset when shifting to apps etc, as it is not needed.
+        if (holder.mShouldReset) {
+            holder.mTaskCardView.reset();
+            holder.mShouldReset = false;
+        }
+    }
+
+    @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/FixedSizeFrameLayout.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
index 9f2b00a..471df6a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
@@ -88,6 +88,10 @@
      */
     protected void layoutContents(Rect bounds, boolean changed) {
         super.onLayout(changed, bounds.left, bounds.top, bounds.right, bounds.bottom);
+
+        int width = getMeasuredWidth();
+        int height = getMeasuredHeight();
+        onSizeChanged(width, height, width, height);
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index fd8df99..db5413f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -77,12 +77,17 @@
     private Handler mHandler;
     private TaskViewTransform mTmpTransform = new TaskViewTransform();
 
-    private Runnable mStartScreenPinningRunnable = new Runnable() {
+    private class StartScreenPinningRunnableRunnable implements Runnable {
+
+        private int taskId = -1;
+
         @Override
         public void run() {
-            EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext));
+            EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext, taskId));
         }
-    };
+    }
+    private StartScreenPinningRunnableRunnable mStartScreenPinningRunnable
+            = new StartScreenPinningRunnableRunnable();
 
     public RecentsTransitionHelper(Context context) {
         mContext = context;
@@ -120,6 +125,7 @@
 
                     if (screenPinningRequested) {
                         // Request screen pinning after the animation runs
+                        mStartScreenPinningRunnable.taskId = task.key.id;
                         mHandler.postDelayed(mStartScreenPinningRunnable, 350);
                     }
                 }
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..d55c7d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -519,7 +519,9 @@
                 @Override
                 public void onAnimationStarted() {
                     EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
-                    mTaskStackView.getStack().removeTask(event.task, AnimationProps.IMMEDIATE,
+                    // Remove the task and don't bother relaying out, as all the tasks will be
+                    // relaid out when the stack changes on the multiwindow change event
+                    mTaskStackView.getStack().removeTask(event.task, null,
                             true /* fromDockGesture */);
                 }
             };
@@ -540,7 +542,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 a867bde..70c4dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -20,19 +20,17 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.provider.Settings;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.view.ViewDebug;
-import android.widget.Toast;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsImpl;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
+import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
@@ -43,7 +41,6 @@
 
 import java.util.ArrayList;
 
-
 /**
  * Represents the dock regions for each orientation.
  */
@@ -165,9 +162,10 @@
         mVisibleDockStates.clear();
         if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
                 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
+            Recents.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
+                    event.task.resizeMode);
             if (!event.task.isDockable) {
-                Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message,
-                        Toast.LENGTH_SHORT).show();
+                EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
             } else {
                 // Add the dock state drop targets (these take priority)
                 TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
@@ -184,6 +182,9 @@
     }
 
     public final void onBusEvent(DragEndEvent event) {
+        if (!mDragTask.isDockable) {
+            EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
+        }
         mDragRequested = false;
         mDragTask = null;
         mTaskView = null;
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..34d6bce 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;
@@ -297,9 +297,6 @@
     private FreePathInterpolator mUnfocusedDimCurveInterpolator;
     private FreePathInterpolator mFocusedDimCurveInterpolator;
 
-    // Indexed from the front of the stack, the normalized x in the unfocused range for each task
-    private float[] mInitialNormX;
-
     // The state of the stack focus (0..1), which controls the transition of the stack from the
     // focused to non-focused state
     @ViewDebug.ExportedProperty(category="recents")
@@ -406,8 +403,10 @@
     /**
      * Sets the system insets.
      */
-    public void setSystemInsets(Rect systemInsets) {
+    public boolean setSystemInsets(Rect systemInsets) {
+        boolean changed = mSystemInsets.equals(systemInsets);
         mSystemInsets.set(systemInsets);
+        return changed;
     }
 
     /**
@@ -545,13 +544,11 @@
             } else {
                 mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
             }
-            mInitialNormX = null;
         } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
             // If there is one stack task, ignore the min/max/initial scroll positions
             mMinScrollP = 0;
             mMaxScrollP = 0;
             mInitialScrollP = 0;
-            mInitialNormX = null;
         } else {
             // Set the max scroll to be the point where the front most task is visible with the
             // stack bottom offset
@@ -565,42 +562,50 @@
                     launchState.launchedViaDockGesture;
             if (launchState.launchedWithAltTab) {
                 mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
-                mInitialNormX = null;
             } else if (scrollToFront) {
                 mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
-                mInitialNormX = null;
             } else {
                 // We are overriding the initial two task positions, so set the initial scroll
                 // position to match the second task (aka focused task) position
                 float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
                 mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2))
                         - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX)));
-
-                // Set the initial scroll to the predefined state (which differs from the stack)
-                mInitialNormX = new float[] {
-                        getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
-                                FROM_BOTTOM),
-                        initialTopNormX
-                };
             }
         }
     }
 
-    public void updateToInitialState(List<Task> tasks) {
-        if (mInitialNormX == null) {
-            return;
-        }
+    /**
+     * Creates task overrides to ensure the initial stack layout if necessary.
+     */
+    public void setTaskOverridesForInitialState(TaskStack stack) {
+        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
 
-        mUnfocusedRange.offset(0f);
-        int taskCount = tasks.size();
-        for (int i = taskCount - 1; i >= 0; i--) {
-            int indexFromFront = taskCount - i - 1;
-            if (indexFromFront >= mInitialNormX.length) {
-                break;
+        mTaskIndexOverrideMap.clear();
+
+        boolean scrollToFront = launchState.launchedFromHome ||
+                launchState.launchedViaDockGesture;
+        if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
+            if (!launchState.launchedWithAltTab && !scrollToFront) {
+                // Set the initial scroll to the predefined state (which differs from the stack)
+                float [] initialNormX = new float[] {
+                        getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
+                                FROM_BOTTOM),
+                        getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP)
+                };
+
+                mUnfocusedRange.offset(0f);
+                List<Task> tasks = stack.getStackTasks();
+                int taskCount = tasks.size();
+                for (int i = taskCount - 1; i >= 0; i--) {
+                    int indexFromFront = taskCount - i - 1;
+                    if (indexFromFront >= initialNormX.length) {
+                        break;
+                    }
+                    float newTaskProgress = mInitialScrollP +
+                            mUnfocusedRange.getAbsoluteX(initialNormX[indexFromFront]);
+                    mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
+                }
             }
-            float newTaskProgress = mInitialScrollP +
-                    mUnfocusedRange.getAbsoluteX(mInitialNormX[indexFromFront]);
-            mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
         }
     }
 
@@ -628,22 +633,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 +659,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..a75d1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -28,11 +28,9 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
-import android.os.Parcelable;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -105,12 +103,6 @@
 
     private static final String TAG = "TaskStackView";
 
-    private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
-    private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
-            "saved_instance_state_layout_focused_state";
-    private final static String KEY_SAVED_STATE_LAYOUT_STACK_SCROLL =
-            "saved_instance_state_layout_stack_scroll";
-
     // The thresholds at which to show/hide the stack action button.
     private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
     private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
@@ -350,11 +342,9 @@
     /**
      * Updates this TaskStackView to the initial state.
      */
-    public void updateToInitialState(boolean scrollToInitialState) {
-        if (scrollToInitialState) {
-            mStackScroller.setStackScrollToInitialState();
-        }
-        mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
+    public void updateToInitialState() {
+        mStackScroller.setStackScrollToInitialState();
+        mLayoutAlgorithm.setTaskOverridesForInitialState(mStack);
     }
 
     /** Updates the list of task views */
@@ -638,18 +628,11 @@
     /**
      * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
      */
-    void relayoutTaskViews(AnimationProps animation) {
+    public void relayoutTaskViews(AnimationProps animation) {
         relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */);
     }
 
     /**
-     * @see #relayoutTaskViews(AnimationProps, ArraySet<Task.TaskKey>, boolean)
-     */
-    void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet) {
-        relayoutTaskViews(animation, ignoreTasksSet, false /* ignoreTaskOverrides */);
-    }
-
-    /**
      * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
      * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
      * animations that are current running on those task views, and will ensure that the children
@@ -657,7 +640,7 @@
      *
      * @param ignoreTasksSet the set of tasks to ignore in the relayout
      */
-    void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
+    private void relayoutTaskViews(AnimationProps animation, ArraySet<Task.TaskKey> ignoreTasksSet,
             boolean ignoreTaskOverrides) {
         // If we had a deferred animation, cancel that
         mDeferredTaskViewLayoutAnimation = null;
@@ -841,7 +824,7 @@
      *
      * @param ignoreTasksSet the set of tasks to ignore in the relayout
      */
-    public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
+    private void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
             ArraySet<Task.TaskKey> ignoreTasksSet) {
         // Compute the min and max scroll values
         mLayoutAlgorithm.update(mStack, ignoreTasksSet);
@@ -1098,24 +1081,6 @@
     }
 
     @Override
-    protected Parcelable onSaveInstanceState() {
-        Bundle savedState = new Bundle();
-        savedState.putParcelable(KEY_SAVED_STATE_SUPER, super.onSaveInstanceState());
-        savedState.putInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE, mLayoutAlgorithm.getFocusState());
-        savedState.putFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL, mStackScroller.getStackScroll());
-        return super.onSaveInstanceState();
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        Bundle savedState = (Bundle) state;
-        super.onRestoreInstanceState(savedState.getParcelable(KEY_SAVED_STATE_SUPER));
-
-        mLayoutAlgorithm.setFocusState(savedState.getInt(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE));
-        mStackScroller.setStackScroll(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL));
-    }
-
-    @Override
     public CharSequence getAccessibilityClassName() {
         return TaskStackView.class.getName();
     }
@@ -1181,9 +1146,10 @@
      * Updates the system insets.
      */
     public void setSystemInsets(Rect systemInsets) {
-        if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
-            mStableLayoutAlgorithm.setSystemInsets(systemInsets);
-            mLayoutAlgorithm.setSystemInsets(systemInsets);
+        boolean changed = false;
+        changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
+        changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
+        if (changed) {
             requestLayout();
         }
     }
@@ -1215,13 +1181,17 @@
                 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
         mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
                 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
-        updateLayoutAlgorithm(false /* boundScroll */, mIgnoreTasks);
+        updateLayoutAlgorithm(false /* boundScroll */);
 
         // If this is the first layout, then scroll to the front of the stack, then update the
         // 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 (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY) {
+                updateToInitialState();
+            }
+            if (!mAwaitingFirstLayout) {
+                mInitialState = INITIAL_STATE_UPDATE_NONE;
+            }
         }
 
         // Rebind all the views, including the ignore ones
@@ -1245,16 +1215,16 @@
      * Measures a TaskView.
      */
     private void measureTaskView(TaskView tv) {
+        Rect padding = new Rect();
         if (tv.getBackground() != null) {
-            tv.getBackground().getPadding(mTmpRect);
-        } else {
-            mTmpRect.setEmpty();
+            tv.getBackground().getPadding(padding);
         }
-        Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
+        mTmpRect.set(mStableLayoutAlgorithm.mTaskRect);
+        mTmpRect.union(mLayoutAlgorithm.mTaskRect);
         tv.measure(
-                MeasureSpec.makeMeasureSpec(taskRect.width() + mTmpRect.left + mTmpRect.right,
+                MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
                         MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(taskRect.height() + mTmpRect.top + mTmpRect.bottom,
+                MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
                         MeasureSpec.EXACTLY));
     }
 
@@ -1276,11 +1246,12 @@
         }
 
         // Relayout all of the task views including the ignored ones
-        relayoutTaskViews(AnimationProps.IMMEDIATE, mIgnoreTasks);
+        relayoutTaskViews(AnimationProps.IMMEDIATE);
         clipTaskViews();
 
         if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
             mAwaitingFirstLayout = false;
+            mInitialState = INITIAL_STATE_UPDATE_NONE;
             onFirstLayout();
         }
     }
@@ -1290,15 +1261,15 @@
      */
     private void layoutTaskView(boolean changed, TaskView tv) {
         if (changed) {
+            Rect padding = new Rect();
             if (tv.getBackground() != null) {
-                tv.getBackground().getPadding(mTmpRect);
-            } else {
-                mTmpRect.setEmpty();
+                tv.getBackground().getPadding(padding);
             }
-            Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
+            mTmpRect.set(mStableLayoutAlgorithm.mTaskRect);
+            mTmpRect.union(mLayoutAlgorithm.mTaskRect);
             tv.cancelTransformAnimation();
-            tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
-                    taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
+            tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
+                    mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
         } else {
             // If the layout has not changed, then just lay it out again in-place
             tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
@@ -1618,7 +1589,6 @@
         if (animation != null) {
             relayoutTaskViewsOnNextFrame(animation);
         }
-        mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
 
         if (mEnterAnimationComplete) {
             if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
@@ -1837,9 +1807,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, systemInsets,
                     mLayoutAlgorithm, getResources(), mWindowRect));
+            mLayoutAlgorithm.setSystemInsets(systemInsets);
             mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
             updateLayoutAlgorithm(true /* boundScroll */);
@@ -1850,6 +1828,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 */);
@@ -1971,25 +1950,31 @@
 
     public final void onBusEvent(MultiWindowStateChangedEvent event) {
         if (!event.inMultiWindow) {
-            // Scroll the stack to the front to see the undocked task
-            mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
+            // Defer until the next frame to ensure that we have received all the system insets, and
+            // initial layout updates
+            post(new Runnable() {
                 @Override
                 public void run() {
-                    List<TaskView> taskViews = getTaskViews();
-                    int taskViewCount = taskViews.size();
-                    for (int i = 0; i < taskViewCount; i++) {
-                        TaskView tv = taskViews.get(i);
-                        tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
-                                tv.mIsDisabledInSafeMode);
-                    }
+                    // Scroll the stack to the front to see the undocked task
+                    mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
+                        @Override
+                        public void run() {
+                            List<TaskView> taskViews = getTaskViews();
+                            int taskViewCount = taskViews.size();
+                            for (int i = 0; i < taskViewCount; i++) {
+                                TaskView tv = taskViews.get(i);
+                                tv.getHeaderView().rebindToTask(tv.getTask(),
+                                        tv.mTouchExplorationEnabled, tv.mIsDisabledInSafeMode);
+                            }
+                        }
+                    });
                 }
             });
         }
     }
 
     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) {
@@ -2004,11 +1989,17 @@
 
         // Trigger a new layout and update to the initial state if necessary
         if (event.fromMultiWindow) {
-            mInitialState = INITIAL_STATE_UPDATE_ALL;
-        } else if (event.fromOrientationChange) {
             mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
+            requestLayout();
+        } else if (event.fromDeviceOrientationChange) {
+            mInitialState = INITIAL_STATE_UPDATE_ALL;
+            requestLayout();
         }
-        requestLayout();
+    }
+
+    public void reloadOnConfigurationChange() {
+        mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+        mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
     }
 
     /**
@@ -2097,7 +2088,7 @@
         if (mFocusedTask != null) {
             writer.print(innerPrefix);
             writer.print("Focused task: ");
-            mFocusedTask.dump(innerPrefix, writer);
+            mFocusedTask.dump("", writer);
         }
 
         mLayoutAlgorithm.dump(innerPrefix, writer);
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/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 6e585ae..37f5a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -39,6 +39,7 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewOutlineProvider;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
@@ -151,12 +152,12 @@
     private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
     private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
 
-    View mContent;
     @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
     TaskViewThumbnail mThumbnailView;
     @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
     TaskViewHeader mHeaderView;
     View mActionButtonView;
+    View mIncompatibleAppToastView;
     TaskViewCallbacks mCb;
 
     @ViewDebug.ExportedProperty(category="recents")
@@ -224,9 +225,9 @@
     @Override
     protected void onFinishInflate() {
         // Bind the views
-        mContent = findViewById(R.id.task_view_content);
         mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar);
         mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail);
+        mThumbnailView.updateClipToTaskBar(mHeaderView);
         mActionButtonView = findViewById(R.id.lock_to_app_fab);
         mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
             @Override
@@ -253,6 +254,9 @@
         if (w > 0 && h > 0) {
             mHeaderView.onTaskViewSizeChanged(w, h);
             mThumbnailView.onTaskViewSizeChanged(w, h);
+
+            mActionButtonView.setTranslationX(w - getMeasuredWidth());
+            mActionButtonView.setTranslationY(h - getMeasuredHeight());
         }
     }
 
@@ -274,13 +278,11 @@
     protected void measureContents(int width, int height) {
         int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
         int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
+        int widthSpec = MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY);
+        int heightSpec = MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY);
 
         // Measure the content
-        mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
-
-        // Optimization: Prevent overdraw of the thumbnail under the header view
-        mThumbnailView.updateClipToTaskBar(mHeaderView);
+        measureChildren(widthSpec, heightSpec);
 
         setMeasuredDimension(width, height);
     }
@@ -344,7 +346,12 @@
         mActionButtonView.setScaleX(1f);
         mActionButtonView.setScaleY(1f);
         mActionButtonView.setAlpha(0f);
+        mActionButtonView.setTranslationX(0f);
+        mActionButtonView.setTranslationY(0f);
         mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
+        if (mIncompatibleAppToastView != null) {
+            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+        }
     }
 
     /**
@@ -536,6 +543,10 @@
         // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
         setDimAlphaWithoutHeader(0);
         mActionButtonView.setAlpha(0f);
+        if (mIncompatibleAppToastView != null &&
+                mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
+            mIncompatibleAppToastView.setAlpha(0f);
+        }
     }
 
     @Override
@@ -554,6 +565,15 @@
         if (screenPinningEnabled) {
             showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
         }
+
+        if (mIncompatibleAppToastView != null &&
+                mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
+            mIncompatibleAppToastView.animate()
+                    .alpha(1f)
+                    .setDuration(duration)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .start();
+        }
     }
 
     @Override
@@ -587,6 +607,18 @@
         mTask = t;
         mTask.addCallback(this);
         mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
+
+        if (!t.isDockable && ssp.hasDockedTask()) {
+            if (mIncompatibleAppToastView == null) {
+                mIncompatibleAppToastView = Utilities.findViewStubById(this,
+                        R.id.incompatible_app_toast_stub).inflate();
+                TextView msg = (TextView) findViewById(com.android.internal.R.id.message);
+                msg.setText(R.string.recents_incompatible_app_message);
+            }
+            mIncompatibleAppToastView.setVisibility(View.VISIBLE);
+        } else if (mIncompatibleAppToastView != null) {
+            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+        }
     }
 
     @Override
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 570ff8a..fb0fc30 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -39,7 +39,6 @@
 import android.view.ViewAnimationUtils;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
-import android.view.ViewStub;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
@@ -141,15 +140,12 @@
     // Header views
     ImageView mIconView;
     TextView mTitleView;
-    TextView mSubTitleView;
     ImageView mMoveTaskButton;
     ImageView mDismissButton;
-    ViewStub mAppOverlayViewStub;
     FrameLayout mAppOverlayView;
     ImageView mAppIconView;
     ImageView mAppInfoView;
     TextView mAppTitleView;
-    ViewStub mFocusTimerIndicatorStub;
     ProgressBar mFocusTimerIndicator;
 
     // Header drawables
@@ -242,13 +238,10 @@
         mIconView.setClickable(false);
         mIconView.setOnLongClickListener(this);
         mTitleView = (TextView) findViewById(R.id.title);
-        mSubTitleView = (TextView) findViewById(R.id.sub_title);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
         if (ssp.hasFreeformWorkspaceSupport()) {
             mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
         }
-        mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub);
-        mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub);
 
         onConfigurationChanged();
     }
@@ -287,28 +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, findViewById(R.id.title_container), 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);
+            }
         }
     }
 
@@ -354,6 +350,8 @@
         }
         mDismissButton.setVisibility(showDismissIcon ? View.VISIBLE : View.INVISIBLE);
         mDismissButton.setTranslationX(rightInset);
+
+        setLeftTopRightBottom(0, 0, width, getMeasuredHeight());
     }
 
     @Override
@@ -442,7 +440,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,
@@ -462,13 +459,6 @@
         mTitleView.setContentDescription(t.titleDescription);
         mTitleView.setTextColor(t.useLightOnPrimaryColor ?
                 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
-        if (!t.isDockable && ssp.hasDockedTask()) {
-            mSubTitleView.setVisibility(View.VISIBLE);
-            mSubTitleView.setTextColor(t.useLightOnPrimaryColor ?
-                    mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
-        } else {
-            mSubTitleView.setVisibility(View.GONE);
-        }
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(t.dismissDescription);
@@ -491,7 +481,8 @@
 
         if (Recents.getDebugFlags().isFastToggleRecentsEnabled()) {
             if (mFocusTimerIndicator == null) {
-                mFocusTimerIndicator = (ProgressBar) mFocusTimerIndicatorStub.inflate();
+                mFocusTimerIndicator = (ProgressBar) Utilities.findViewStubById(this,
+                        R.id.focus_timer_indicator_stub).inflate();
             }
             mFocusTimerIndicator.getProgressDrawable()
                     .setColorFilter(
@@ -637,7 +628,8 @@
 
         // Inflate the overlay if necessary
         if (mAppOverlayView == null) {
-            mAppOverlayView = (FrameLayout) mAppOverlayViewStub.inflate();
+            mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
+                    R.id.app_overlay_stub).inflate();
             mAppOverlayView.setBackground(mOverlayBackground);
             mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
             mAppIconView.setOnClickListener(this);
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..4de7713 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -123,6 +123,7 @@
         }
 
         mTaskViewRect.set(0, 0, width, height);
+        setLeftTopRightBottom(0, 0, width, height);
         updateThumbnailScale();
     }
 
@@ -143,43 +144,32 @@
 
         int viewWidth = mTaskViewRect.width();
         int viewHeight = mTaskViewRect.height();
-        if (mBitmapShader != null) {
+        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) {
+            int topOffset = mTaskBar != null
+                    ? mTaskBar.getHeight() - mCornerRadius
+                    : 0;
 
-            // 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);
+            // 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), topOffset,
+                        viewWidth, viewHeight,
+                        mCornerRadius, mCornerRadius, mBgFillPaint);
             }
+            if (thumbnailHeight < viewHeight) {
+                // Landscape thumbnail on a portrait task view
+                canvas.drawRoundRect(0, Math.max(topOffset, thumbnailHeight - mCornerRadius),
+                        viewWidth, viewHeight,
+                        mCornerRadius, mCornerRadius, mBgFillPaint);
+            }
+
+            // Draw the thumbnail
+            canvas.drawRoundRect(0, topOffset, thumbnailWidth, thumbnailHeight,
+                    mCornerRadius, mCornerRadius, mDrawPaint);
         } else {
             canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
                     mBgFillPaint);
@@ -289,10 +279,7 @@
     /** Updates the clip rect based on the given task bar. */
     void updateClipToTaskBar(View taskBar) {
         mTaskBar = taskBar;
-        int top = (int) Math.max(0, taskBar.getTranslationY() +
-                taskBar.getMeasuredHeight() - 1);
-        mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
-        setClipBounds(mClipRect);
+        invalidate();
     }
 
     /** Updates the visibility of the the thumbnail. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index b512393..397f24e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -19,11 +19,14 @@
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.IntProperty;
 import android.util.Property;
 import android.view.View;
 
+import com.android.systemui.recents.misc.Utilities;
+
 import java.util.ArrayList;
 
 /**
@@ -31,55 +34,20 @@
  */
 public class TaskViewTransform {
 
-    public static final Property<View, Integer> LEFT =
-            new IntProperty<View>("left") {
+    public static final Property<View, Rect> LTRB =
+            new Property<View, Rect>(Rect.class, "leftTopRightBottom") {
+
+                private Rect mTmpRect = new Rect();
+
                 @Override
-                public void setValue(View object, int v) {
-                    object.setLeft(v);
+                public void set(View v, Rect ltrb) {
+                    v.setLeftTopRightBottom(ltrb.left, ltrb.top, ltrb.right, ltrb.bottom);
                 }
 
                 @Override
-                public Integer get(View object) {
-                    return object.getLeft();
-                }
-            };
-
-    public static final Property<View, Integer> TOP =
-            new IntProperty<View>("top") {
-                @Override
-                public void setValue(View object, int v) {
-                    object.setTop(v);
-                }
-
-                @Override
-                public Integer get(View object) {
-                    return object.getTop();
-                }
-            };
-
-    public static final Property<View, Integer> RIGHT =
-            new IntProperty<View>("right") {
-                @Override
-                public void setValue(View object, int v) {
-                    object.setRight(v);
-                }
-
-                @Override
-                public Integer get(View object) {
-                    return object.getRight();
-                }
-            };
-
-    public static final Property<View, Integer> BOTTOM =
-            new IntProperty<View>("bottom") {
-                @Override
-                public void setValue(View object, int v) {
-                    object.setBottom(v);
-                }
-
-                @Override
-                public Integer get(View object) {
-                    return object.getBottom();
+                public Rect get(View v) {
+                    mTmpRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+                    return mTmpRect;
                 }
             };
 
@@ -205,11 +173,12 @@
                 animators.add(animation.apply(AnimationProps.ALPHA, anim));
             }
             if (hasRectChangedFrom(v)) {
+                Rect fromViewRect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+                Rect toViewRect = new Rect();
+                rect.round(toViewRect);
                 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
-                        PropertyValuesHolder.ofInt(LEFT, v.getLeft(), (int) rect.left),
-                        PropertyValuesHolder.ofInt(TOP, v.getTop(), (int) rect.top),
-                        PropertyValuesHolder.ofInt(RIGHT, v.getRight(), (int) rect.right),
-                        PropertyValuesHolder.ofInt(BOTTOM, v.getBottom(), (int) rect.bottom));
+                        PropertyValuesHolder.ofObject(LTRB, Utilities.RECT_EVALUATOR,
+                                fromViewRect, toViewRect));
                 animators.add(animation.apply(AnimationProps.BOUNDS, anim));
             }
         }
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..5d1a61d 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -39,6 +39,7 @@
     private DockDividerVisibilityListener mDockDividerVisibilityListener;
     private boolean mVisible = false;
     private boolean mMinimized = false;
+    private boolean mAdjustedForIme = false;
     private ForcedResizableInfoActivityController mForcedResizableController;
 
     @Override
@@ -84,7 +85,7 @@
         addDivider(configuration);
         if (mMinimized) {
             mView.setMinimizedDockStack(true);
-            mWindowManager.setTouchable(false);
+            updateTouchable();
         }
     }
 
@@ -109,7 +110,7 @@
             public void run() {
                 if (mMinimized != minimized) {
                     mMinimized = minimized;
-                    mWindowManager.setTouchable(!minimized);
+                    updateTouchable();
                     if (animDuration > 0) {
                         mView.setMinimizedDockStack(minimized, animDuration);
                     } else {
@@ -129,6 +130,10 @@
         });
     }
 
+    private void updateTouchable() {
+        mWindowManager.setTouchable(!mMinimized && !mAdjustedForIme);
+    }
+
     class DockDividerVisibilityListener extends IDockedStackListener.Stub {
 
         @Override
@@ -146,5 +151,26 @@
                 throws RemoteException {
             updateMinimizedDockedStack(minimized, animDuration);
         }
+
+        @Override
+        public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
+                throws RemoteException {
+            mView.post(() -> {
+                if (mAdjustedForIme != adjustedForIme) {
+                    mAdjustedForIme = adjustedForIme;
+                    updateTouchable();
+                    if (animDuration > 0) {
+                        mView.setAdjustedForIme(adjustedForIme, animDuration);
+                    } else {
+                        mView.setAdjustedForIme(adjustedForIme);
+                    }
+                }
+            });
+        }
+
+        @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 3005535..4d1c6ba 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -35,8 +35,8 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.GestureDetector;
-import android.view.GestureDetector.OnDoubleTapListener;
 import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.VelocityTracker;
@@ -45,7 +45,6 @@
 import android.view.ViewConfiguration;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -54,6 +53,8 @@
 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;
@@ -80,7 +81,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 +95,8 @@
     /**
      * 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 float ADJUSTED_FOR_IME_SCALE = 0.5f;
 
     private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
             new PathInterpolator(0.5f, 1f, 0.5f, 1f);
@@ -97,6 +105,7 @@
 
     private DividerHandleView mHandle;
     private View mBackground;
+    private MinimizedDockShadow mMinimizedShadow;
     private int mStartX;
     private int mStartY;
     private int mStartPosition;
@@ -133,8 +142,11 @@
     private boolean mGrowRecents;
     private ValueAnimator mCurrentAnimator;
     private boolean mEntranceAnimationRunning;
+    private boolean mExitAnimationRunning;
+    private int mExitStartPosition;
     private GestureDetector mGestureDetector;
     private boolean mDockedStackMinimized;
+    private boolean mAdjustedForIme;
 
     private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
         @Override
@@ -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)
@@ -570,11 +657,46 @@
         mDockedStackMinimized = minimized;
     }
 
+    public void setAdjustedForIme(boolean adjustedForIme) {
+        updateDockSide();
+        mHandle.setAlpha(adjustedForIme ? 0f : 1f);
+        if (!adjustedForIme) {
+            resetBackground();
+        } else if (mDockSide == WindowManager.DOCKED_TOP) {
+            mBackground.setPivotY(0);
+            mBackground.setScaleY(MINIMIZE_DOCK_SCALE);
+        }
+        mAdjustedForIme = adjustedForIme;
+    }
+
+    public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
+        updateDockSide();
+        mHandle.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setDuration(animDuration)
+                .alpha(adjustedForIme ? 0f : 1f)
+                .start();
+        if (mDockSide == WindowManager.DOCKED_TOP) {
+            mBackground.setPivotY(0);
+            mBackground.animate()
+                    .scaleY(adjustedForIme ? MINIMIZE_DOCK_SCALE : 1f);
+        }
+        if (!adjustedForIme) {
+            mBackground.animate().withEndAction(mResetBackgroundRunnable);
+        }
+        mBackground.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setDuration(animDuration)
+                .start();
+        mAdjustedForIme = adjustedForIme;
+    }
+
     private void resetBackground() {
         mBackground.setPivotX(mBackground.getWidth() / 2);
         mBackground.setPivotY(mBackground.getHeight() / 2);
         mBackground.setScaleX(1f);
         mBackground.setScaleY(1f);
+        mMinimizedShadow.setAlpha(0f);
     }
 
     @Override
@@ -583,6 +705,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 +783,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 +830,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;
@@ -866,20 +1014,26 @@
         mEntranceAnimationRunning = true;
         resizeStack(position, mSnapAlgorithm.getMiddleTarget().position,
                 mSnapAlgorithm.getMiddleTarget());
+
+        // Vibrate after docking
+        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
     public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
         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);
         }
     }
 
@@ -892,8 +1046,13 @@
                     : 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/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
index d4922c3..177296b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
@@ -48,6 +48,7 @@
         setContentView(R.layout.forced_resizable_activity);
         TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
         tv.setText(R.string.dock_forced_resizable);
+        getWindow().setTitle(getString(R.string.dock_forced_resizable));
         getWindow().getDecorView().setOnTouchListener(this);
     }
 
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..237ca5e 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 */
 
@@ -1315,7 +1341,7 @@
     }
 
     protected void toggleKeyboardShortcuts(int deviceId) {
-        getKeyboardShortcuts().toggleKeyboardShortcuts(deviceId);
+        KeyboardShortcuts.toggle(mContext, deviceId);
     }
 
     protected void cancelPreloadingRecents() {
@@ -1549,7 +1575,11 @@
             row.setRemoteInputController(mRemoteInputController);
             row.setOnExpandClickListener(this);
 
-            // Get the app name
+            // Get the app name.
+            // Note that Notification.Builder#bindHeaderAppName has similar logic
+            // but since this field is used in the guts, it must be accurate.
+            // Therefore we will only show the application label, or, failing that, the
+            // package name. No substitutions.
             final String pkg = sbn.getPackageName();
             String appname = pkg;
             try {
@@ -1712,14 +1742,6 @@
         }
     }
 
-    protected KeyboardShortcuts getKeyboardShortcuts() {
-        if (mKeyboardShortcuts == null) {
-            mKeyboardShortcuts = new KeyboardShortcuts(mContext);
-        }
-
-        return mKeyboardShortcuts;
-    }
-
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
         if (!isDeviceProvisioned()) return;
 
@@ -2353,6 +2375,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/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 99b6397..cc8e3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -114,7 +114,7 @@
         void buzzBeepBlinked();
         void notificationLightOff();
         void notificationLightPulse(int argb, int onMillis, int offMillis);
-        void showScreenPinningRequest();
+        void showScreenPinningRequest(int taskId);
         void appTransitionPending();
         void appTransitionCancelled();
         void appTransitionStarting(long startTime, long duration);
@@ -298,9 +298,10 @@
         }
     }
 
-    public void showScreenPinningRequest() {
+    public void showScreenPinningRequest(int taskId) {
         synchronized (mLock) {
-            mHandler.sendEmptyMessage(MSG_SHOW_SCREEN_PIN_REQUEST);
+            mHandler.obtainMessage(MSG_SHOW_SCREEN_PIN_REQUEST, taskId, 0, null)
+                    .sendToTarget();
         }
     }
 
@@ -450,7 +451,7 @@
                     mCallbacks.notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
                     break;
                 case MSG_SHOW_SCREEN_PIN_REQUEST:
-                    mCallbacks.showScreenPinningRequest();
+                    mCallbacks.showScreenPinningRequest(msg.arg1);
                     break;
                 case MSG_APP_TRANSITION_PENDING:
                     mCallbacks.appTransitionPending();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 11aaedf..79e06c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -58,6 +58,8 @@
 import com.android.systemui.recents.Recents;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import static android.content.Context.LAYOUT_INFLATER_SERVICE;
@@ -68,6 +70,10 @@
  */
 public final class KeyboardShortcuts {
     private static final String TAG = KeyboardShortcuts.class.getSimpleName();
+    private static final Object sLock = new Object();
+    private static KeyboardShortcuts sInstance;
+    private static boolean sIsShowing;
+
     private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
     private final SparseArray<String> mModifierNames = new SparseArray<>();
     private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
@@ -76,21 +82,79 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Context mContext;
     private final IPackageManager mPackageManager;
-    private final OnClickListener dialogCloseListener =  new DialogInterface.OnClickListener() {
+    private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int id) {
-            dismissKeyboardShortcutsDialog();
+            dismissKeyboardShortcuts();
         }
     };
+    private final Comparator<KeyboardShortcutInfo> mApplicationItemsComparator =
+            new Comparator<KeyboardShortcutInfo>() {
+                @Override
+                public int compare(KeyboardShortcutInfo ksh1, KeyboardShortcutInfo ksh2) {
+                    boolean ksh1ShouldBeLast = ksh1.getLabel() == null
+                            || ksh1.getLabel().toString().isEmpty();
+                    boolean ksh2ShouldBeLast = ksh2.getLabel() == null
+                            || ksh2.getLabel().toString().isEmpty();
+                    if (ksh1ShouldBeLast && ksh2ShouldBeLast) {
+                        return 0;
+                    }
+                    if (ksh1ShouldBeLast) {
+                        return 1;
+                    }
+                    if (ksh2ShouldBeLast) {
+                        return -1;
+                    }
+                    return (ksh1.getLabel().toString()).compareToIgnoreCase(
+                            ksh2.getLabel().toString());
+                }
+            };
 
     private Dialog mKeyboardShortcutsDialog;
     private KeyCharacterMap mKeyCharacterMap;
 
-    public KeyboardShortcuts(Context context) {
+    private KeyboardShortcuts(Context context) {
         this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
         this.mPackageManager = AppGlobals.getPackageManager();
         loadResources(context);
     }
 
+    private static KeyboardShortcuts getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new KeyboardShortcuts(context);
+        }
+        return sInstance;
+    }
+
+    public static void show(Context context, int deviceId) {
+        synchronized (sLock) {
+            if (sInstance != null && !sInstance.mContext.equals(context)) {
+                dismiss();
+            }
+            getInstance(context).showKeyboardShortcuts(deviceId);
+            sIsShowing = true;
+        }
+    }
+
+    public static void toggle(Context context, int deviceId) {
+        synchronized (sLock) {
+            if (sIsShowing) {
+                dismiss();
+            } else {
+                show(context, deviceId);
+            }
+        }
+    }
+
+    public static void dismiss() {
+        synchronized (sLock) {
+            if (sInstance != null) {
+                sInstance.dismissKeyboardShortcuts();
+                sInstance = null;
+            }
+            sIsShowing = false;
+        }
+    }
+
     private void loadResources(Context context) {
         mSpecialCharacterNames.put(
                 KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
@@ -254,27 +318,6 @@
                 KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
     }
 
-    public void toggleKeyboardShortcuts(int deviceId) {
-        retrieveKeyCharacterMap(deviceId);
-        if (mKeyboardShortcutsDialog == null) {
-            Recents.getSystemServices().requestKeyboardShortcuts(mContext,
-                new KeyboardShortcutsReceiver() {
-                    @Override
-                    public void onKeyboardShortcutsReceived(
-                            final List<KeyboardShortcutGroup> result) {
-                        result.add(getSystemShortcuts());
-                        final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
-                        if (appShortcuts != null) {
-                            result.add(appShortcuts);
-                        }
-                        showKeyboardShortcutsDialog(result);
-                    }
-                }, deviceId);
-        } else {
-            dismissKeyboardShortcutsDialog();
-        }
-    }
-
     /**
      * Retrieves a {@link KeyCharacterMap} and assigns it to mKeyCharacterMap. If the given id is an
      * existing device, that device's map is used. Otherwise, it checks first all available devices
@@ -304,7 +347,24 @@
         mKeyCharacterMap = inputDevice.getKeyCharacterMap();
     }
 
-    public void dismissKeyboardShortcutsDialog() {
+    private void showKeyboardShortcuts(int deviceId) {
+        retrieveKeyCharacterMap(deviceId);
+        Recents.getSystemServices().requestKeyboardShortcuts(mContext,
+                new KeyboardShortcutsReceiver() {
+                    @Override
+                    public void onKeyboardShortcutsReceived(
+                            final List<KeyboardShortcutGroup> result) {
+                        result.add(getSystemShortcuts());
+                        final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
+                        if (appShortcuts != null) {
+                            result.add(appShortcuts);
+                        }
+                        showKeyboardShortcutsDialog(result);
+                    }
+                }, deviceId);
+    }
+
+    private void dismissKeyboardShortcuts() {
         if (mKeyboardShortcutsDialog != null) {
             mKeyboardShortcutsDialog.dismiss();
             mKeyboardShortcutsDialog = null;
@@ -346,9 +406,7 @@
 
     private KeyboardShortcutGroup getDefaultApplicationShortcuts() {
         final int userId = mContext.getUserId();
-        final KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
-                mContext.getString(R.string.keyboard_shortcut_group_applications),
-                true);
+        List<KeyboardShortcutInfo> keyboardShortcutInfoAppItems = new ArrayList<>();
 
         // Assist.
         final AssistUtils assistUtils = new AssistUtils(mContext);
@@ -366,7 +424,7 @@
                     assistPackageInfo.applicationInfo.packageName,
                     assistPackageInfo.applicationInfo.icon);
 
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
                     assistIcon,
                     KeyEvent.KEYCODE_UNKNOWN,
@@ -376,7 +434,7 @@
         // Browser.
         final Icon browserIcon = getIconForIntentCategory(Intent.CATEGORY_APP_BROWSER, userId);
         if (browserIcon != null) {
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
                     browserIcon,
                     KeyEvent.KEYCODE_B,
@@ -387,7 +445,7 @@
         // Contacts.
         final Icon contactsIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CONTACTS, userId);
         if (contactsIcon != null) {
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_contacts),
                     contactsIcon,
                     KeyEvent.KEYCODE_C,
@@ -397,7 +455,7 @@
         // Email.
         final Icon emailIcon = getIconForIntentCategory(Intent.CATEGORY_APP_EMAIL, userId);
         if (emailIcon != null) {
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_email),
                     emailIcon,
                     KeyEvent.KEYCODE_E,
@@ -407,7 +465,7 @@
         // Messaging.
         final Icon messagingIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MESSAGING, userId);
         if (messagingIcon != null) {
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_im),
                     messagingIcon,
                     KeyEvent.KEYCODE_T,
@@ -417,7 +475,7 @@
         // Music.
         final Icon musicIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MUSIC, userId);
         if (musicIcon != null) {
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_music),
                     musicIcon,
                     KeyEvent.KEYCODE_P,
@@ -427,14 +485,24 @@
         // Calendar.
         final Icon calendarIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CALENDAR, userId);
         if (calendarIcon != null) {
-            applicationGroup.addItem(new KeyboardShortcutInfo(
+            keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
                     calendarIcon,
                     KeyEvent.KEYCODE_L,
                     KeyEvent.META_META_ON));
         }
 
-        return applicationGroup.getItems().size() == 0 ? null : applicationGroup;
+        final int itemsSize = keyboardShortcutInfoAppItems.size();
+        if (itemsSize == 0) {
+            return null;
+        }
+
+        // Sorts by label, case insensitive with nulls and/or empty labels last.
+        Collections.sort(keyboardShortcutInfoAppItems, mApplicationItemsComparator);
+        return new KeyboardShortcutGroup(
+                mContext.getString(R.string.keyboard_shortcut_group_applications),
+                keyboardShortcutInfoAppItems,
+                true);
     }
 
     private Icon getIconForIntentCategory(String intentCategory, int userId) {
@@ -485,7 +553,7 @@
         populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById(
                 R.id.keyboard_shortcuts_container), keyboardShortcutGroups);
         dialogBuilder.setView(keyboardShortcutsView);
-        dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener);
+        dialogBuilder.setPositiveButton(R.string.quick_settings_done, mDialogCloseListener);
         mKeyboardShortcutsDialog = dialogBuilder.create();
         mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
         Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
@@ -497,8 +565,15 @@
             List<KeyboardShortcutGroup> keyboardShortcutGroups) {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size();
+        TextView shortcutsKeyView = (TextView) inflater.inflate(
+                R.layout.keyboard_shortcuts_key_view, null, false);
+        shortcutsKeyView.measure(
+                View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        final int shortcutKeyTextItemMinWidth = shortcutsKeyView.getMeasuredHeight();
         // Needed to be able to scale the image items to the same height as the text items.
-        final int shortcutTextItemHeight = getShortcutTextItemHeight(inflater);
+        final int shortcutKeyIconItemHeightWidth = shortcutsKeyView.getMeasuredHeight()
+                - shortcutsKeyView.getPaddingTop()
+                - shortcutsKeyView.getPaddingBottom();
         for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
             KeyboardShortcutGroup group = keyboardShortcutGroups.get(i);
             TextView categoryTitle = (TextView) inflater.inflate(
@@ -549,8 +624,8 @@
                         ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
                                 R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
                                 false);
-                        Bitmap bitmap = Bitmap.createBitmap(shortcutTextItemHeight,
-                                shortcutTextItemHeight, Bitmap.Config.ARGB_8888);
+                        Bitmap bitmap = Bitmap.createBitmap(shortcutKeyIconItemHeightWidth,
+                                shortcutKeyIconItemHeightWidth, Bitmap.Config.ARGB_8888);
                         Canvas canvas = new Canvas(bitmap);
                         shortcutRepresentation.drawable.setBounds(0, 0, canvas.getWidth(),
                                 canvas.getHeight());
@@ -561,6 +636,7 @@
                         TextView shortcutKeyTextView = (TextView) inflater.inflate(
                                 R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
                                 false);
+                        shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
                         shortcutKeyTextView.setText(shortcutRepresentation.string);
                         shortcutItemsContainer.addView(shortcutKeyTextView);
                     }
@@ -577,16 +653,6 @@
         }
     }
 
-    private int getShortcutTextItemHeight(LayoutInflater inflater) {
-        TextView shortcutKeyTextView = (TextView) inflater.inflate(
-                R.layout.keyboard_shortcuts_key_view, null, false);
-        shortcutKeyTextView.measure(
-                View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-        return shortcutKeyTextView.getMeasuredHeight()
-                - shortcutKeyTextView.getPaddingTop()
-                - shortcutKeyTextView.getPaddingBottom();
-    }
-
     private List<StringOrDrawable> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
         List<StringOrDrawable> shortcutKeys = getHumanReadableModifiers(info);
         if (shortcutKeys == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
index 5d22faf..5f4ebd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -26,8 +26,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
-            final KeyboardShortcuts keyboardShortcuts = new KeyboardShortcuts(context);
-            keyboardShortcuts.toggleKeyboardShortcuts(-1 /* deviceId unknown */);
+            KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
         }
     }
 }
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/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 3812429..92f3585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -21,8 +21,12 @@
 import android.app.IWallpaperManagerCallback;
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.DrawableWrapper;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
@@ -151,4 +155,57 @@
         getBitmap();
         mBar.updateMediaMetaData(true /* metaDataChanged */, true /* allowEnterAnimation */);
     }
+
+    /**
+     * Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
+     */
+    public static class WallpaperDrawable extends DrawableWrapper {
+
+        private Bitmap mBackground;
+        private Rect mTmpRect = new Rect();
+
+        public WallpaperDrawable(Resources r, Bitmap b) {
+            super(new BitmapDrawable(r, b));
+            mBackground = b;
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            return -1;
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return -1;
+        }
+
+        @Override
+        protected void onBoundsChange(Rect bounds) {
+            int vwidth = getBounds().width();
+            int vheight = getBounds().height();
+            int dwidth = mBackground.getWidth();
+            int dheight = mBackground.getHeight();
+            float scale;
+            float dx = 0, dy = 0;
+
+            if (dwidth * vheight > vwidth * dheight) {
+                scale = (float) vheight / (float) dheight;
+            } else {
+                scale = (float) vwidth / (float) dwidth;
+            }
+
+            if (scale <= 1f) {
+                scale = 1f;
+            }
+            dy = (vheight - dheight * scale) * 0.5f;
+
+            mTmpRect.set(
+                    bounds.left,
+                    bounds.top + Math.round(dy),
+                    bounds.left + Math.round(dwidth * scale),
+                    bounds.top + Math.round(dheight * scale + dy));
+
+            super.onBoundsChange(mTmpRect);
+        }
+    }
 }
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..4b7d56b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -372,7 +372,10 @@
         mDisabledFlags = disabledFlags;
 
         final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
-        boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
+
+        // Disable recents always in car mode.
+        boolean disableRecent = (
+                mCarMode || (disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
         final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
                 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
         final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
@@ -526,6 +529,15 @@
                 public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
                         throws RemoteException {
                 }
+
+                @Override
+                public void onAdjustedForImeChanged(boolean adjustedForIme, 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 e52a401..82806fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -46,6 +46,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
@@ -133,6 +134,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
@@ -893,6 +895,7 @@
                     qsContainer.setHost(qsh);
                     mQSPanel = qsContainer.getQsPanel();
                     mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
+                    mKeyguardStatusBar.setQSPanel(mQSPanel);
                     mHeader = qsContainer.getHeader();
                     initSignalCluster(mHeader);
                     mHeader.setActivityStarter(PhoneStatusBar.this);
@@ -1177,31 +1180,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 +1372,10 @@
     }
 
     private boolean shouldSuppressFullScreenIntent(String key) {
+        if (isDeviceInVrMode()) {
+            return true;
+        }
+
         if (mPowerManager.isInteractive()) {
             return mNotificationData.shouldSuppressScreenOn(key);
         } else {
@@ -1927,19 +1929,27 @@
                     + " state=" + mState);
         }
 
-        Bitmap artworkBitmap = null;
+        Drawable artworkDrawable = null;
         if (mMediaMetadata != null) {
+            Bitmap artworkBitmap = null;
             artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
             if (artworkBitmap == null) {
                 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
                 // might still be null
             }
+            if (artworkBitmap != null) {
+                artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
+            }
         }
-        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkBitmap == null) {
-            artworkBitmap = mLockscreenWallpaper.getBitmap();
+        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
+            Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap();
+            if (lockWallpaper != null) {
+                artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
+                        mBackdropBack.getResources(), lockWallpaper);
+            }
         }
 
-        final boolean hasArtwork = artworkBitmap != null;
+        final boolean hasArtwork = artworkDrawable != null;
 
         if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE
                 && mFingerprintUnlockController.getMode()
@@ -1984,7 +1994,7 @@
                     mBackdropBack.setBackgroundColor(0xFFFFFFFF);
                     mBackdropBack.setImageDrawable(new ColorDrawable(c));
                 } else {
-                    mBackdropBack.setImageBitmap(artworkBitmap);
+                    mBackdropBack.setImageDrawable(artworkDrawable);
                 }
                 if (mScrimSrcModeEnabled) {
                     mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
@@ -3161,7 +3171,7 @@
             if (DEBUG) Log.v(TAG, "onReceive: " + intent);
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                getKeyboardShortcuts().dismissKeyboardShortcutsDialog();
+                KeyboardShortcuts.dismiss();
                 if (isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");
@@ -3470,7 +3480,7 @@
             @Override
             public void run() {
                 mLeaveOpenOnKeyguardHide = true;
-                executeRunnableDismissingKeyguard(runnable, null, false, true);
+                executeRunnableDismissingKeyguard(runnable, null, false, false);
             }
         });
     }
@@ -3591,11 +3601,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);
@@ -4314,7 +4323,7 @@
         try {
             IActivityManager activityManager = ActivityManagerNative.getDefault();
             if (activityManager.isInLockTaskMode()) {
-                activityManager.stopLockTaskModeOnCurrent();
+                activityManager.stopSystemLockTaskMode();
 
                 // When exiting refresh disabled flags.
                 mNavigationBarView.setDisabledFlags(mDisabled1, true);
@@ -4337,17 +4346,17 @@
     }
 
     @Override
-    public void showScreenPinningRequest() {
+    public void showScreenPinningRequest(int taskId) {
         if (mKeyguardMonitor.isShowing()) {
             // Don't allow apps to trigger this from keyguard.
             return;
         }
         // Show screen pinning request, since this comes from an app, show 'no thanks', button.
-        showScreenPinningRequest(true);
+        showScreenPinningRequest(taskId, true);
     }
 
-    public void showScreenPinningRequest(boolean allowCancel) {
-        mScreenPinningRequest.showPrompt(allowCancel);
+    public void showScreenPinningRequest(int taskId, boolean allowCancel) {
+        mScreenPinningRequest.showPrompt(taskId, allowCancel);
     }
 
     public boolean hasActiveNotifications() {
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/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index ea9a5a5..2b03dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -125,6 +125,7 @@
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
         ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+        ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
 
         updateResources();
     }
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/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index a85fe0d..5046456 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -114,8 +114,8 @@
 
     @Override
     public String getProfileOwnerName() {
-        for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
-            String name = mDevicePolicyManager.getProfileOwnerNameAsUser(profile.id);
+        for (int profileId : mUserManager.getProfileIdsWithDisabled(mCurrentUserId)) {
+            String name = mDevicePolicyManager.getProfileOwnerNameAsUser(profileId);
             if (name != null) {
                 return name;
             }
@@ -135,13 +135,13 @@
 
     @Override
     public String getProfileVpnName() {
-        for (UserInfo profile : mUserManager.getProfiles(mVpnUserId)) {
-            if (profile.id == mVpnUserId) {
+        for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
+            if (profileId == mVpnUserId) {
                 continue;
             }
-            VpnConfig cfg = mCurrentVpns.get(profile.id);
+            VpnConfig cfg = mCurrentVpns.get(profileId);
             if (cfg != null) {
-                return getNameForVpnConfig(cfg, profile.getUserHandle());
+                return getNameForVpnConfig(cfg, UserHandle.of(profileId));
             }
         }
         return null;
@@ -149,8 +149,8 @@
 
     @Override
     public boolean isVpnEnabled() {
-        for (UserInfo profile : mUserManager.getProfiles(mVpnUserId)) {
-            if (mCurrentVpns.get(profile.id) != null) {
+        for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
+            if (mCurrentVpns.get(profileId) != null) {
                 return true;
             }
         }
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..f9202c4 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
@@ -166,7 +165,7 @@
     }
 
     @Override
-    public void showScreenPinningRequest() {
+    public void showScreenPinningRequest(int taskId) {
     }
 
     @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/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index a445e77..5febb9b 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -35,6 +35,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
@@ -144,6 +145,7 @@
     private ComponentName mPipComponentName;
     private MediaController mPipMediaController;
     private boolean mOnboardingShown;
+    private String[] mLastPackagesResourceGranted;
 
     private final Runnable mResizePinnedStackRunnable = new Runnable() {
         @Override
@@ -166,7 +168,7 @@
                 String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
                 int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
                         INVALID_RESOURCE_TYPE);
-                if (mState != STATE_NO_PIP && packageNames != null && packageNames.length > 0
+                if (packageNames != null && packageNames.length > 0
                         && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
                     handleMediaResourceGranted(packageNames);
                 }
@@ -446,41 +448,24 @@
     }
 
     private void handleMediaResourceGranted(String[] packageNames) {
-        StackInfo fullscreenStack = null;
-        try {
-            fullscreenStack = mActivityManager.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
-        } catch (RemoteException e) {
-            Log.e(TAG, "getStackInfo failed", e);
-        }
-        if (fullscreenStack == null) {
-            return;
-        }
-        int fullscreenTopTaskId = fullscreenStack.taskIds[fullscreenStack.taskIds.length - 1];
-        List<RunningTaskInfo> tasks = null;
-        try {
-            tasks = mActivityManager.getTasks(MAX_RUNNING_TASKS_COUNT, 0);
-        } catch (RemoteException e) {
-            Log.e(TAG, "getTasks failed", e);
-        }
-        if (tasks == null) {
-            return;
-        }
-        boolean wasGrantedInFullscreen = false;
-        boolean wasGrantedInPip = false;
-        for (int i = tasks.size() - 1; i >= 0; --i) {
-            RunningTaskInfo task = tasks.get(i);
-            for (int j = packageNames.length - 1; j >= 0; --j) {
-                if (task.topActivity.getPackageName().equals(packageNames[j])) {
-                    if (task.id == fullscreenTopTaskId) {
-                        wasGrantedInFullscreen = true;
-                    } else if (task.id == mPipTaskId) {
-                        wasGrantedInPip= true;
+        if (mState == STATE_NO_PIP) {
+            mLastPackagesResourceGranted = packageNames;
+        } else {
+            boolean requestedFromLastPackages = false;
+            if (mLastPackagesResourceGranted != null) {
+                for (String packageName : mLastPackagesResourceGranted) {
+                    for (String newPackageName : packageNames) {
+                        if (TextUtils.equals(newPackageName, packageName)) {
+                            requestedFromLastPackages = true;
+                            break;
+                        }
                     }
                 }
             }
-        }
-        if (wasGrantedInFullscreen && !wasGrantedInPip) {
-            closePip();
+            mLastPackagesResourceGranted = packageNames;
+            if (!requestedFromLastPackages) {
+                closePip();
+            }
         }
     }
 
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/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index 86ceff4..398572a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.tv.pip;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.app.Activity;
-import android.graphics.Rect;
+import android.graphics.drawable.AnimationDrawable;
 import android.os.Bundle;
-import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
 
 import com.android.systemui.R;
 
@@ -35,29 +38,47 @@
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_onboarding);
-        View pipOnboardingView = findViewById(R.id.pip_onboarding);
-        View pipOutlineView = findViewById(R.id.pip_outline);
-        mPipManager.addListener(this);
-        findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
+        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 finish();
             }
         });
 
-        int pipOutlineSpace = getResources().getDimensionPixelSize(R.dimen.tv_pip_bounds_space);
-        int screenWidth = getResources().getDisplayMetrics().widthPixels;
-        Rect pipBounds = mPipManager.getPipBounds();
-        pipOnboardingView.setPadding(
-                pipBounds.left - pipOutlineSpace,
-                pipBounds.top - pipOutlineSpace,
-                screenWidth - pipBounds.right - pipOutlineSpace, 0);
+        mPipManager.addListener(this);
+    }
 
-        // Set width and height for outline view to enclose the PIP.
-        LayoutParams lp = pipOutlineView.getLayoutParams();
-        lp.width = pipBounds.width() + pipOutlineSpace * 2;
-        lp.height = pipBounds.height() + pipOutlineSpace * 2;
-        pipOutlineView.setLayoutParams(lp);
+    @Override
+    public void onResume() {
+        super.onResume();
+        AnimatorSet enterAnimator = new AnimatorSet();
+        enterAnimator.playTogether(
+                loadAnimator(R.id.remote, R.anim.tv_pip_onboarding_image_enter_animation),
+                loadAnimator(R.id.remote_button, R.anim.tv_pip_onboarding_image_enter_animation),
+                loadAnimator(R.id.title, R.anim.tv_pip_onboarding_title_enter_animation),
+                loadAnimator(R.id.description,
+                        R.anim.tv_pip_onboarding_description_enter_animation),
+                loadAnimator(R.id.button, R.anim.tv_pip_onboarding_button_enter_animation));
+        enterAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                ImageView button = (ImageView) findViewById(R.id.remote_button);
+                ((AnimationDrawable) button.getDrawable()).start();
+            }
+        });
+        enterAnimator.start();
+    }
+
+    private Animator loadAnimator(int viewResId, int animResId) {
+        Animator animator = AnimatorInflater.loadAnimator(this, animResId);
+        animator.setTarget(findViewById(viewResId));
+        return animator;
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        finish();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index e205ff5..011e159 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.tv.pip;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -50,9 +52,11 @@
     private ImageView mGuideButtonPlayPauseImageView;
     private final Runnable mHideGuideOverlayRunnable = new Runnable() {
         public void run() {
-            mGuideOverlayView.setVisibility(View.GONE);
+            mFadeOutAnimation.start();
         }
     };
+    private Animator mFadeInAnimation;
+    private Animator mFadeOutAnimation;
 
     /**
      * Shows PIP overlay UI only if it's not there.
@@ -74,11 +78,18 @@
         setContentView(R.layout.tv_pip_overlay);
         mGuideOverlayView = findViewById(R.id.guide_overlay);
         mPipManager.addListener(this);
+        mFadeInAnimation = AnimatorInflater.loadAnimator(
+                this, R.anim.tv_pip_overlay_fade_in_animation);
+        mFadeInAnimation.setTarget(mGuideOverlayView);
+        mFadeOutAnimation = AnimatorInflater.loadAnimator(
+                this, R.anim.tv_pip_overlay_fade_out_animation);
+        mFadeOutAnimation.setTarget(mGuideOverlayView);
     }
 
     @Override
     protected void onResume() {
         super.onResume();
+        mFadeInAnimation.start();
         mHandler.removeCallbacks(mHideGuideOverlayRunnable);
         mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
     }
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..d75561b 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;
@@ -134,6 +132,7 @@
      * @param allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
      */
     public void requestFocus(boolean allowRecentsFocusable) {
+        mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
         if (!mIsRecentsShown || mIsPipFocusedInRecent) {
             return;
         }
@@ -143,7 +142,6 @@
         mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
         mPipControlsView.requestFocus();
         mPipControlsView.startFocusGainAnimation();
-        mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
     }
 
     /**
@@ -152,10 +150,14 @@
      * is focused.
      * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
      */
-    private void clearFocus() {
+    public void clearFocus() {
         if (!mIsRecentsShown || !mIsPipFocusedInRecent) {
             return;
         }
+        if (!mRecentsView.hasFocus()) {
+            // Let mRecentsView's focus listener handle clearFocus().
+            mRecentsView.requestFocus();
+        }
         mIsPipFocusedInRecent = false;
         mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
         mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 180d918..2c53e29 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.usb;
 
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.NotificationManager;
@@ -97,6 +98,11 @@
         public void onDiskScanned(DiskInfo disk, int volumeCount) {
             onDiskScannedInternal(disk, volumeCount);
         }
+
+        @Override
+        public void onDiskDestroyed(DiskInfo disk) {
+            onDiskDestroyedInternal(disk);
+        }
     };
 
     private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
@@ -238,6 +244,15 @@
         }
     }
 
+    /**
+     * Remove all notifications for a disk when it goes away.
+     *
+     * @param disk The disk that went away.
+     */
+    private void onDiskDestroyedInternal(@NonNull DiskInfo disk) {
+        mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
+    }
+
     private void onVolumeStateChangedInternal(VolumeInfo vol) {
         switch (vol.getType()) {
             case VolumeInfo.TYPE_PRIVATE:
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
index 04640a2..d7c4bbf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
@@ -47,6 +47,7 @@
         mAudioManager = audioManager;
 
         getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+        setShowForAllUsers(true);
         setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
         setButton(DialogInterface.BUTTON_POSITIVE,
                 mContext.getString(com.android.internal.R.string.yes), this);
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 f93fb1b..0428ecf 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -460,12 +460,9 @@
         }
         synchronized (mLock) {
             reloadWidgetsMaskedState(userId);
-            List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
-            if (profiles != null) {
-                for (int i = 0; i < profiles.size(); i++) {
-                    UserInfo user  = profiles.get(i);
-                    reloadWidgetsMaskedState(user.id);
-                }
+            int[] profileIds = mUserManager.getEnabledProfileIds(userId);
+            for (int profileId : profileIds) {
+                reloadWidgetsMaskedState(profileId);
             }
         }
     }
@@ -616,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) {
@@ -1065,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,
@@ -1075,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.
@@ -3458,33 +3454,12 @@
         public int[] getEnabledGroupProfileIds(int userId) {
             final int parentId = getGroupParent(userId);
 
-            final List<UserInfo> profiles;
             final long identity = Binder.clearCallingIdentity();
             try {
-                profiles = mUserManager.getProfiles(parentId);
+                return mUserManager.getEnabledProfileIds(parentId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-
-            int enabledProfileCount = 0;
-            final int profileCount = profiles.size();
-            for (int i = 0; i < profileCount; i++) {
-                if (profiles.get(i).isEnabled()) {
-                    enabledProfileCount++;
-                }
-            }
-
-            int enabledProfileIndex = 0;
-            final int[] profileIds = new int[enabledProfileCount];
-            for (int i = 0; i < profileCount; i++) {
-                UserInfo profile = profiles.get(i);
-                if (profile.isEnabled()) {
-                    profileIds[enabledProfileIndex] = profile.getUserHandle().getIdentifier();
-                    enabledProfileIndex++;
-                }
-            }
-
-            return profileIds;
         }
 
         public void enforceServiceExistsAndRequiresBindRemoteViewsPermission(
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 966deb6..e5fe03a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -116,6 +116,7 @@
 import com.android.internal.net.VpnProfile;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
@@ -164,7 +165,7 @@
         implements PendingIntent.OnFinished {
     private static final String TAG = "ConnectivityService";
 
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
     private static final boolean LOGD_RULES = false;
@@ -224,6 +225,9 @@
     private static final int ENABLED  = 1;
     private static final int DISABLED = 0;
 
+    private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
+            new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class });
+
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
         // the highest scoring network satisfying a NetworkRequest.  This should be passed when
@@ -446,9 +450,8 @@
      */
     private class LegacyTypeTracker {
 
-        private static final boolean DBG = false;
+        private static final boolean DBG = true;
         private static final boolean VDBG = false;
-        private static final String TAG = "CSLegacyTypeTracker";
 
         /**
          * Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS).
@@ -601,12 +604,6 @@
             pw.decreaseIndent();
             pw.println();
         }
-
-        // This class needs its own log method because it has a different TAG.
-        private void log(String s) {
-            Slog.d(TAG, s);
-        }
-
     }
     private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
 
@@ -1528,7 +1525,7 @@
                 mInitialBroadcast = new Intent(intent);
             }
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            if (DBG) {
+            if (VDBG) {
                 log("sendStickyBroadcast: action=" + intent.getAction());
             }
 
@@ -1656,7 +1653,7 @@
         }
 
         if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address()) == false) {
-            loge("Unexpected mtu value: " + mtu + ", " + iface);
+            if (mtu != 0) loge("Unexpected mtu value: " + mtu + ", " + iface);
             return;
         }
 
@@ -1667,7 +1664,7 @@
         }
 
         try {
-            if (DBG) log("Setting MTU size: " + iface + ", " + mtu);
+            if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
             mNetd.setMtu(iface, mtu);
         } catch (Exception e) {
             Slog.e(TAG, "exception in setMtu()" + e);
@@ -1703,7 +1700,7 @@
         if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
 
         try {
-            if (DBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+            if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
 
             final String prefix = "/sys/kernel/ipv4/tcp_";
             FileUtils.stringToFile(prefix + "rmem_min", values[0]);
@@ -1900,11 +1897,12 @@
         }
     }
 
-    private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
+    private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) {
         if (nai.network == null) return false;
         final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network);
         if (officialNai != null && officialNai.equals(nai)) return true;
         if (officialNai != null || VDBG) {
+            final String msg = sMagicDecoderRing.get(what, Integer.toString(what));
             loge(msg + " - isLiveNetworkAgent found mismatched netId: " + officialNai +
                 " - " + nai);
         }
@@ -1921,10 +1919,10 @@
             super(looper);
         }
 
-        @Override
-        public void handleMessage(Message msg) {
-            NetworkInfo info;
+        private boolean maybeHandleAsyncChannelMessage(Message msg) {
             switch (msg.what) {
+                default:
+                    return false;
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
                     handleAsyncChannelHalfConnect(msg);
                     break;
@@ -1938,69 +1936,58 @@
                     handleAsyncChannelDisconnected(msg);
                     break;
                 }
+            }
+            return true;
+        }
+
+        private void maybeHandleNetworkAgentMessage(Message msg) {
+            NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+            if (nai == null) {
+                if (VDBG) {
+                    final String what = sMagicDecoderRing.get(msg.what, Integer.toString(msg.what));
+                    log(String.format("%s from unknown NetworkAgent", what));
+                }
+                return;
+            }
+
+            switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent");
-                    } else {
-                        final NetworkCapabilities networkCapabilities =
-                                (NetworkCapabilities)msg.obj;
-                        if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
-                                networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
-                            Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
-                        }
-                        if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
-                                networkCapabilities)) {
-                            Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
-                                    + nai.networkCapabilities + " -> " + networkCapabilities);
-                        }
-                        updateCapabilities(nai, networkCapabilities);
+                    final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+                    if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
+                            networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                        Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
+                    if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
+                            networkCapabilities)) {
+                        Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
+                                + nai.networkCapabilities + " -> " + networkCapabilities);
+                    }
+                    updateCapabilities(nai, networkCapabilities);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED");
-                    } else {
-                        if (VDBG) {
-                            log("Update of LinkProperties for " + nai.name() +
-                                    "; created=" + nai.created);
-                        }
-                        LinkProperties oldLp = nai.linkProperties;
-                        synchronized (nai) {
-                            nai.linkProperties = (LinkProperties)msg.obj;
-                        }
-                        if (nai.created) updateLinkProperties(nai, oldLp);
+                    if (VDBG) {
+                        log("Update of LinkProperties for " + nai.name() +
+                                "; created=" + nai.created);
                     }
+                    LinkProperties oldLp = nai.linkProperties;
+                    synchronized (nai) {
+                        nai.linkProperties = (LinkProperties)msg.obj;
+                    }
+                    if (nai.created) updateLinkProperties(nai, oldLp);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_NETWORK_INFO_CHANGED from unknown NetworkAgent");
-                        break;
-                    }
-                    info = (NetworkInfo) msg.obj;
+                    NetworkInfo info = (NetworkInfo) msg.obj;
                     updateNetworkInfo(nai, info);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent");
-                        break;
-                    }
                     Integer score = (Integer) msg.obj;
                     if (score != null) updateNetworkScore(nai, score.intValue());
                     break;
                 }
                 case NetworkAgent.EVENT_UID_RANGES_ADDED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_UID_RANGES_ADDED from unknown NetworkAgent");
-                        break;
-                    }
                     try {
                         mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
                     } catch (Exception e) {
@@ -2010,11 +1997,6 @@
                     break;
                 }
                 case NetworkAgent.EVENT_UID_RANGES_REMOVED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_UID_RANGES_REMOVED from unknown NetworkAgent");
-                        break;
-                    }
                     try {
                         mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
                     } catch (Exception e) {
@@ -2024,11 +2006,6 @@
                     break;
                 }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_SET_EXPLICITLY_SELECTED from unknown NetworkAgent");
-                        break;
-                    }
                     if (nai.created && !nai.networkMisc.explicitlySelected) {
                         loge("ERROR: created network explicitly selected.");
                     }
@@ -2037,20 +2014,26 @@
                     break;
                 }
                 case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_PACKET_KEEPALIVE from unknown NetworkAgent");
-                        break;
-                    }
                     mKeepaliveTracker.handleEventPacketKeepalive(nai, msg);
                     break;
                 }
+            }
+        }
+
+        private boolean maybeHandleNetworkMonitorMessage(Message msg) {
+            switch (msg.what) {
+                default:
+                    return false;
                 case NetworkMonitor.EVENT_NETWORK_TESTED: {
-                    NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
+                    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;
@@ -2061,16 +2044,18 @@
                         }
                         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;
                 }
                 case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    if (isLiveNetworkAgent(nai, "EVENT_NETWORK_LINGER_COMPLETE")) {
+                    if (isLiveNetworkAgent(nai, msg.what)) {
                         handleLingerComplete(nai);
                     }
                     break;
@@ -2102,6 +2087,14 @@
                     break;
                 }
             }
+            return true;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
+                maybeHandleNetworkAgentMessage(msg);
+            }
         }
     }
 
@@ -2213,6 +2206,10 @@
             }
             mLegacyTypeTracker.remove(nai, wasDefault);
             rematchAllNetworksAndRequests(null, 0);
+            if (wasDefault && getDefaultNetwork() == null) {
+                // Log that we lost the default network and there is no replacement.
+                logConnectivityServiceChangeEvent(null, nai);
+            }
             if (nai.created) {
                 // Tell netd to clean up the configuration for this network
                 // (routing rules, DNS, etc).
@@ -2321,7 +2318,7 @@
                 if (DBG) log("Attempt to release unowned NetworkRequest " + request);
                 return;
             }
-            if (DBG) log("releasing NetworkRequest " + request);
+            if (VDBG || (DBG && nri.isRequest())) log("releasing NetworkRequest " + request);
             nri.unlinkDeathRecipient();
             mNetworkRequests.remove(request);
             mNetworkRequestInfoLogs.log("RELEASE " + nri);
@@ -2334,7 +2331,7 @@
                 for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                     if (nai.networkRequests.get(nri.request.requestId) != null) {
                         nai.networkRequests.remove(nri.request.requestId);
-                        if (DBG) {
+                        if (VDBG) {
                             log(" Removing from current network " + nai.name() +
                                     ", leaving " + nai.networkRequests.size() +
                                     " requests.");
@@ -2445,14 +2442,14 @@
     }
 
     private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
-        if (DBG) log("scheduleUnvalidatedPrompt " + nai.network);
+        if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
         mHandler.sendMessageDelayed(
                 mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
                 PROMPT_UNVALIDATED_DELAY_MS);
     }
 
     private void handlePromptUnvalidated(Network network) {
-        if (DBG) log("handlePromptUnvalidated " + network);
+        if (VDBG) log("handlePromptUnvalidated " + network);
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
 
         // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
@@ -2495,11 +2492,14 @@
                             break;
                         }
                     }
-                    if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
-                        log("Failed to find a new network - expiring NetTransition Wakelock");
-                    } else {
-                        log("NetTransition Wakelock (" + (causedBy == null ? "unknown" : causedBy) +
-                                " cleared because we found a replacement network");
+                    if (VDBG) {
+                        if (msg.what == EVENT_EXPIRE_NET_TRANSITION_WAKELOCK) {
+                            log("Failed to find a new network - expiring NetTransition Wakelock");
+                        } else {
+                            log("NetTransition Wakelock (" +
+                                    (causedBy == null ? "unknown" : causedBy) +
+                                    " cleared because we found a replacement network");
+                        }
                     }
                     break;
                 }
@@ -3353,10 +3353,6 @@
     private static enum NotificationType { SIGN_IN, NO_INTERNET; };
 
     private void setProvNotificationVisible(boolean visible, int networkType, String action) {
-        if (DBG) {
-            log("setProvNotificationVisible: E visible=" + visible + " networkType=" + networkType
-                + " action=" + action);
-        }
         Intent intent = new Intent(action);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
         // Concatenate the range of types onto the range of NetIDs.
@@ -3383,7 +3379,7 @@
     private void setProvNotificationVisibleIntent(boolean visible, int id,
             NotificationType notifyType, int networkType, String extraInfo, PendingIntent intent,
             boolean highPriority) {
-        if (DBG) {
+        if (VDBG || (DBG && visible)) {
             log("setProvNotificationVisibleIntent " + notifyType + " visible=" + visible
                     + " networkType=" + getNetworkTypeName(networkType)
                     + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
@@ -3826,8 +3822,7 @@
         Bundle thresholds = new Bundle();
         thresholds.putIntegerArrayList("thresholds", thresholdsArray);
 
-        // TODO: Switch to VDBG.
-        if (DBG) {
+        if (VDBG || (DBG && !"CONNECT".equals(reason))) {
             String detail;
             if (request != null && request.networkCapabilities.hasSignalStrength()) {
                 detail = reason + " " + request.networkCapabilities.getSignalStrength();
@@ -3992,7 +3987,7 @@
                 new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
         NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
                 NetworkRequestType.LISTEN);
-        if (DBG) log("listenForNetwork for " + nri);
+        if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
         return networkRequest;
@@ -4010,7 +4005,7 @@
                 new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId());
         NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
                 NetworkRequestType.LISTEN);
-        if (DBG) log("pendingListenForNetwork for " + nri);
+        if (VDBG) log("pendingListenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
     }
@@ -4215,7 +4210,7 @@
         // do this twice, adding non-nexthop routes first, then routes they are dependent on
         for (RouteInfo route : routeDiff.added) {
             if (route.hasGateway()) continue;
-            if (DBG) log("Adding Route [" + route + "] to network " + netId);
+            if (VDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
                 mNetd.addRoute(netId, route);
             } catch (Exception e) {
@@ -4226,7 +4221,7 @@
         }
         for (RouteInfo route : routeDiff.added) {
             if (route.hasGateway() == false) continue;
-            if (DBG) log("Adding Route [" + route + "] to network " + netId);
+            if (VDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
                 mNetd.addRoute(netId, route);
             } catch (Exception e) {
@@ -4237,7 +4232,7 @@
         }
 
         for (RouteInfo route : routeDiff.removed) {
-            if (DBG) log("Removing Route [" + route + "] from network " + netId);
+            if (VDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
                 mNetd.removeRoute(netId, route);
             } catch (Exception e) {
@@ -4253,7 +4248,7 @@
         }
 
         Collection<InetAddress> dnses = newLp.getDnsServers();
-        if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
+        if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
         try {
             mNetd.setDnsServersForNetwork(
                     netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
@@ -4436,9 +4431,8 @@
         teardownUnneededNetwork(oldNetwork);
     }
 
-    private void makeDefault(NetworkAgentInfo newNetwork) {
+    private void makeDefault(NetworkAgentInfo newNetwork, NetworkAgentInfo prevNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
-        ConnectivityServiceChangeEvent.logEvent(newNetwork.network.netId);
         setupDataActivityTracking(newNetwork);
         try {
             mNetd.setDefaultNetId(newNetwork.network.netId);
@@ -4449,6 +4443,8 @@
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
         updateTcpBufferSizes(newNetwork);
         setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+
+        logConnectivityServiceChangeEvent(newNetwork, prevNetwork);
     }
 
     // Handles a network appearing or improving its score.
@@ -4521,14 +4517,14 @@
                 }
                 if (currentNetwork == null ||
                         currentNetwork.getCurrentScore() < newNetwork.getCurrentScore()) {
-                    if (DBG) log("rematch for " + newNetwork.name());
+                    if (VDBG) log("rematch for " + newNetwork.name());
                     if (currentNetwork != null) {
-                        if (DBG) log("   accepting network in place of " + currentNetwork.name());
+                        if (VDBG) log("   accepting network in place of " + currentNetwork.name());
                         currentNetwork.networkRequests.remove(nri.request.requestId);
                         currentNetwork.networkLingered.add(nri.request);
                         affectedNetworks.add(currentNetwork);
                     } else {
-                        if (DBG) log("   accepting network in place of null");
+                        if (VDBG) log("   accepting network in place of null");
                     }
                     unlinger(newNetwork);
                     mNetworkForRequestId.put(nri.request.requestId, newNetwork);
@@ -4599,7 +4595,7 @@
         }
         if (isNewDefault) {
             // Notify system services that this network is up.
-            makeDefault(newNetwork);
+            makeDefault(newNetwork, oldDefaultNetwork);
             synchronized (ConnectivityService.this) {
                 // have a new default network, release the transition wakelock in
                 // a second if it's held.  The second pause is to allow apps
@@ -4858,7 +4854,7 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, int score) {
-        if (DBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+        if (VDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
         if (score < 0) {
             loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
                     ").  Bumping score to min of 0");
@@ -4936,7 +4932,7 @@
     }
 
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
-        if (DBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
+        if (VDBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
         for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
             NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
@@ -5069,4 +5065,22 @@
             NetworkAgentInfo nai, NetworkRequest defaultRequest) {
         return new NetworkMonitor(context, handler, nai, defaultRequest);
     }
+
+    private static void logConnectivityServiceChangeEvent(
+            NetworkAgentInfo next, NetworkAgentInfo prev) {
+        final int newNetId = (next == null) ? NETID_UNSET : next.network.netId;
+        final int[] newTransportTypes = (next == null)
+                ? new int[0]
+                : next.networkCapabilities.getTransportTypes();
+
+        final int oldNetId = (prev == null) ? NETID_UNSET : prev.network.netId;
+        final boolean hadIPv4 = (prev != null) &&
+                prev.linkProperties.hasIPv4Address() &&
+                prev.linkProperties.hasIPv4DefaultRoute();
+        final boolean hadIPv6 = (prev != null) &&
+                prev.linkProperties.hasGlobalIPv6Address() &&
+                prev.linkProperties.hasIPv6DefaultRoute();
+        ConnectivityServiceChangeEvent.logEvent(newNetId, newTransportTypes,
+                oldNetId, hadIPv4, hadIPv6);
+    }
 }
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index aa98648..5a90488 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -58,7 +58,6 @@
      * as a camera launch.
      */
     private static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
-    private static final long CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS = 120;
 
     /** The listener that receives the gesture event. */
     private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -260,8 +259,7 @@
         synchronized (this) {
             doubleTapInterval = event.getEventTime() - mLastPowerDown;
             if (mCameraDoubleTapPowerEnabled
-                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
-                    && doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) {
+                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
                 launched = true;
                 intercept = interactive;
             }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 58e3674..544e645 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -66,7 +66,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -1050,12 +1049,8 @@
     }
 
     void updateCurrentProfileIds() {
-        List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId());
-        int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
-        for (int i = 0; i < currentProfileIds.length; i++) {
-            currentProfileIds[i] = profiles.get(i).id;
-        }
-        mSettings.setCurrentProfileIds(currentProfileIds);
+        mSettings.setCurrentProfileIds(
+                mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
     }
 
     @Override
@@ -2971,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;
@@ -2988,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();
             }
         }
@@ -3649,6 +3638,7 @@
         private static final String ATTR_ID = "id";
         private static final String ATTR_LABEL = "label";
         private static final String ATTR_ICON = "icon";
+        private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
         private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
         private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
         private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
@@ -3742,6 +3732,10 @@
                     for (int i = 0; i < N; ++i) {
                         final InputMethodSubtype subtype = subtypesList.get(i);
                         out.startTag(null, NODE_SUBTYPE);
+                        if (subtype.hasSubtypeId()) {
+                            out.attribute(null, ATTR_IME_SUBTYPE_ID,
+                                    String.valueOf(subtype.getSubtypeId()));
+                        }
                         out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
                         out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
                         out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
@@ -3820,7 +3814,7 @@
                                 parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
                         final boolean isAsciiCapable = "1".equals(String.valueOf(
                                 parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
-                        final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
+                        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder()
                                 .setSubtypeNameResId(label)
                                 .setSubtypeIconResId(icon)
                                 .setSubtypeLocale(imeSubtypeLocale)
@@ -3828,9 +3822,13 @@
                                 .setSubtypeMode(imeSubtypeMode)
                                 .setSubtypeExtraValue(imeSubtypeExtraValue)
                                 .setIsAuxiliary(isAuxiliary)
-                                .setIsAsciiCapable(isAsciiCapable)
-                                .build();
-                        tempSubtypesArray.add(subtype);
+                                .setIsAsciiCapable(isAsciiCapable);
+                        final String subtypeIdString =
+                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
+                        if (subtypeIdString != null) {
+                            builder.setSubtypeId(Integer.valueOf(subtypeIdString));
+                        }
+                        tempSubtypesArray.add(builder.build());
                     }
                 }
             } catch (XmlPullParserException | IOException | NumberFormatException e) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9884a70..7c48634 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -21,6 +21,7 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.FlpHardwareProvider;
 import com.android.server.location.FusedProxy;
@@ -53,7 +54,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
-import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.location.ActivityRecognitionHardware;
@@ -359,12 +359,9 @@
      * @param currentUserId the current user, who might have an alter-ego.
      */
     void updateUserProfiles(int currentUserId) {
-        List<UserInfo> profiles = mUserManager.getProfiles(currentUserId);
+        int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
         synchronized (mLock) {
-            mCurrentUserProfiles = new int[profiles.size()];
-            for (int i = 0; i < mCurrentUserProfiles.length; i++) {
-                mCurrentUserProfiles[i] = profiles.get(i).id;
-            }
+            mCurrentUserProfiles = profileIds;
         }
     }
 
@@ -374,12 +371,7 @@
      */
     private boolean isCurrentProfile(int userId) {
         synchronized (mLock) {
-            for (int i = 0; i < mCurrentUserProfiles.length; i++) {
-                if (mCurrentUserProfiles[i] == userId) {
-                    return true;
-                }
-            }
-            return false;
+            return ArrayUtils.contains(mCurrentUserProfiles, userId);
         }
     }
 
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 d6dbad8..6009984 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -110,6 +110,14 @@
     }
 
     /**
+     * Like SystemClock.uptimeMillis, except truncated to an int so it will fit in a message arg.
+     * Inaccurate across 49.7 days of uptime, but only used for debugging.
+     */
+    private int uptimeMillisInt() {
+        return (int) SystemClock.uptimeMillis() & Integer.MAX_VALUE;
+    }
+
+    /**
      * Yell loudly if someone tries making future {@link #execute(Command)}
      * calls while holding a lock on the given object.
      */
@@ -134,7 +142,9 @@
 
     @Override
     public boolean handleMessage(Message msg) {
-        String event = (String) msg.obj;
+        final String event = (String) msg.obj;
+        final int start = uptimeMillisInt();
+        final int sent = msg.arg1;
         try {
             if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
                 log(String.format("Unhandled event '%s'", event));
@@ -145,6 +155,13 @@
             if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
                 mWakeLock.release();
             }
+            final int end = uptimeMillisInt();
+            if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
+                loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
+            }
+            if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
+                loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
+            }
         }
         return true;
     }
@@ -214,8 +231,9 @@
                                     mWakeLock.acquire();
                                     releaseWl = true;
                                 }
-                                if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
-                                        event.getCode(), event.getRawEvent()))) {
+                                Message msg = mCallbackHandler.obtainMessage(
+                                        event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
+                                if (mCallbackHandler.sendMessage(msg)) {
                                     releaseWl = false;
                                 }
                             } else {
@@ -225,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/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 3eb20a0..801d6e0 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -43,7 +43,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -195,12 +194,8 @@
     }
 
     void updateCurrentProfileIds() {
-        final List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId());
-        int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
-        for (int i = 0; i < currentProfileIds.length; i++) {
-            currentProfileIds[i] = profiles.get(i).id;
-        }
-        mSettings.setCurrentProfileIds(currentProfileIds);
+        mSettings.setCurrentProfileIds(
+                mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
     }
 
     private class TextServicesMonitor extends PackageMonitor {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 2924cef..5ba07cf 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -454,6 +454,12 @@
                 return;
             }
 
+            if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE
+                    && Settings.System.getInt(
+                    mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) == 0) {
+                return;
+            }
+
             int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
                     vib.mUsageHint, vib.mUid, vib.mOpPkg);
             if (mode == AppOpsManager.MODE_ALLOWED) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 4198af9..be53cfc 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -65,10 +65,14 @@
     // Which native processes to dump into dropbox's stack traces
     public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
         "/system/bin/audioserver",
+        "/system/bin/cameraserver",
+        "/system/bin/drmserver",
+        "/system/bin/mediadrmserver",
         "/system/bin/mediaserver",
         "/system/bin/sdcard",
         "/system/bin/surfaceflinger",
-        "media.log"
+        "media.codec",     // system/bin/mediacodec
+        "media.extractor", // system/bin/mediaextractor
     };
 
     static Watchdog sWatchdog;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 810270d..4791818 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -85,10 +85,14 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
+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;
 
@@ -125,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;
@@ -266,10 +295,10 @@
         private int debugDbInsertionPoint = -1;
         private SQLiteStatement statementForLogging;
 
-        UserAccounts(Context context, int userId) {
+        UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
             this.userId = userId;
             synchronized (cacheLock) {
-                openHelper = DeDatabaseHelper.create(context, userId);
+                openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
             }
         }
     }
@@ -338,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);
@@ -539,7 +565,9 @@
             UserAccounts accounts = mUsers.get(userId);
             boolean validateAccounts = false;
             if (accounts == null) {
-                accounts = new UserAccounts(mContext, userId);
+                File preNDbFile = new File(getPreNDatabaseName(userId));
+                File deDbFile = new File(getDeDatabaseName(userId));
+                accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
                 initializeDebugDbSizeAndCompileSqlStatementForLogging(
                         accounts.openHelper.getWritableDatabase(), accounts);
                 mUsers.append(userId, accounts);
@@ -550,10 +578,12 @@
             if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
                 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
                 synchronized (accounts.cacheLock) {
-                    CeDatabaseHelper.create(mContext, userId);
-                    accounts.openHelper.attachCeDatabase();
+                    File preNDatabaseFile = new File(getPreNDatabaseName(userId));
+                    File ceDatabaseFile = new File(getCeDatabaseName(userId));
+                    CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
+                    accounts.openHelper.attachCeDatabase(ceDatabaseFile);
                 }
-                // TODO Synchronize accounts by removing CE account not available in DE
+                syncDeCeAccountsLocked(accounts);
             }
             if (validateAccounts) {
                 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
@@ -562,6 +592,21 @@
         }
     }
 
+    private void syncDeCeAccountsLocked(UserAccounts accounts) {
+        Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
+        final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
+        List<Account> accountsToRemove = CeDatabaseHelper.findCeAccountsNotInDe(db);
+        if (!accountsToRemove.isEmpty()) {
+            Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
+                    + accounts.userId + " was locked. Removing accounts from CE tables");
+            logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
+
+            for (Account account : accountsToRemove) {
+                removeAccountInternal(accounts, account, Process.myUid());
+            }
+        }
+    }
+
     private void purgeOldGrantsAll() {
         synchronized (mUsers) {
             for (int i = 0; i < mUsers.size(); i++) {
@@ -631,8 +676,12 @@
         }
     }
 
-    private void onUserUnlocked(Intent intent) {
-        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+    @VisibleForTesting
+    void onUserUnlocked(Intent intent) {
+        onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+    }
+
+    void onUnlockUser(int userId) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "onUserUnlocked " + userId);
         }
@@ -799,7 +848,7 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "getAuthenticatorTypes: "
                     + "for user id " + userId
-                    + "caller's uid " + callingUid
+                    + " caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         // Only allow the system process to read accounts of other users
@@ -1537,27 +1586,41 @@
         }
     }
 
-    /* For testing */
+    @VisibleForTesting
     protected void removeAccountInternal(Account account) {
         removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
     }
 
     private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
-        // For now user is required to be unlocked. TODO: Handle both cases in the future
         int deleted;
+        boolean userUnlocked = isUserUnlocked(accounts.userId);
+        if (!userUnlocked) {
+            Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
+                    + " is still locked. CE data will be removed later");
+        }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
+            final SQLiteDatabase db = userUnlocked
+                    ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
+                    : accounts.openHelper.getWritableDatabase();
             final long accountId = getAccountIdLocked(db, account);
-            deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
-                    + "=?",
-                    new String[]{account.name, account.type});
-            // Delete from CE table
-            db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
-                    new String[]{account.name, account.type});
+            db.beginTransaction();
+            try {
+                deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
+                                + "=?", new String[]{account.name, account.type});
+                if (userUnlocked) {
+                    // Delete from CE table
+                    deleted = db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
+                            + "=?", new String[]{account.name, account.type});
+                }
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
             removeAccountFromCacheLocked(accounts, account);
             sendAccountsChangedBroadcast(accounts.userId);
-
-            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
+            String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
+                    : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
+            logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
         }
         long id = Binder.clearCallingIdentity();
         try {
@@ -4014,7 +4077,8 @@
         }
     }
 
-    static String getPreNDatabaseName(int userId) {
+    @VisibleForTesting
+    String getPreNDatabaseName(int userId) {
         File systemDir = Environment.getDataSystemDirectory();
         File databaseFile = new File(Environment.getUserSystemDirectory(userId),
                 PRE_N_DATABASE_NAME);
@@ -4040,13 +4104,15 @@
         return databaseFile.getPath();
     }
 
-    static String getDeDatabaseName(int userId) {
+    @VisibleForTesting
+    String getDeDatabaseName(int userId) {
         File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
                 DE_DATABASE_NAME);
         return databaseFile.getPath();
     }
 
-    static String getCeDatabaseName(int userId) {
+    @VisibleForTesting
+    String getCeDatabaseName(int userId) {
         File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
                 CE_DATABASE_NAME);
         return databaseFile.getPath();
@@ -4072,6 +4138,7 @@
         private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
         private static String ACTION_ACCOUNT_ADD = "action_account_add";
         private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
+        private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
         private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
         private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
 
@@ -4082,6 +4149,7 @@
         // who called.
         private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
         private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
+        private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
 
         //This action doesn't add account to accountdb. Account is only
         // added in finishSession which may be in a different user profile.
@@ -4185,13 +4253,11 @@
     }
 
     static class PreNDatabaseHelper extends SQLiteOpenHelper {
-
         private final Context mContext;
         private final int mUserId;
 
-        public PreNDatabaseHelper(Context context, int userId) {
-            super(context, AccountManagerService.getPreNDatabaseName(userId), null,
-                    PRE_N_DATABASE_VERSION);
+        public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
+            super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
             mContext = context;
             mUserId = userId;
         }
@@ -4328,8 +4394,8 @@
         private final int mUserId;
         private volatile boolean mCeAttached;
 
-        private DeDatabaseHelper(Context context, int userId) {
-            super(context, getDeDatabaseName(userId), null, DE_DATABASE_VERSION);
+        private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
+            super(context, deDatabaseName, null, DE_DATABASE_VERSION);
             mUserId = userId;
         }
 
@@ -4394,8 +4460,7 @@
             }
         }
 
-        public void attachCeDatabase() {
-            File ceDbFile = new File(getCeDatabaseName(mUserId));
+        public void attachCeDatabase(File ceDbFile) {
             SQLiteDatabase db = getWritableDatabase();
             db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
             mCeAttached = true;
@@ -4408,8 +4473,8 @@
 
         public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
             if(!mCeAttached) {
-                Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user "
-                        + mUserId + " is still locked ", new Throwable());
+                Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
+                        + " is still locked. CE database is not yet available.", new Throwable());
             }
             return super.getReadableDatabase();
         }
@@ -4417,7 +4482,7 @@
         public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
             if(!mCeAttached) {
                 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
-                        + " is still locked ", new Throwable());
+                        + " is still locked. CE database is not yet available.", new Throwable());
             }
             return super.getWritableDatabase();
         }
@@ -4470,20 +4535,24 @@
             db.execSQL("DETACH DATABASE preNDb");
         }
 
-        static DeDatabaseHelper create(Context context, int userId) {
-            File oldDb = new File(getPreNDatabaseName(userId));
-            File newDb = new File(getDeDatabaseName(userId));
-            boolean newDbExists = newDb.exists();
-            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId);
+        static DeDatabaseHelper create(
+                Context context,
+                int userId,
+                File preNDatabaseFile,
+                File deDatabaseFile) {
+            boolean newDbExists = deDatabaseFile.exists();
+            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
+                    deDatabaseFile.getPath());
             // If the db just created, and there is a legacy db, migrate it
-            if (!newDbExists && oldDb.exists()) {
+            if (!newDbExists && preNDatabaseFile.exists()) {
                 // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
-                PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId);
+                PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
+                        preNDatabaseFile.getPath());
                 // Open the database to force upgrade if required
                 preNDatabaseHelper.getWritableDatabase();
                 preNDatabaseHelper.close();
                 // Move data without SPII to DE
-                deDatabaseHelper.migratePreNDbToDe(oldDb);
+                deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
             }
             return deDatabaseHelper;
         }
@@ -4491,8 +4560,8 @@
 
     static class CeDatabaseHelper extends SQLiteOpenHelper {
 
-        public CeDatabaseHelper(Context context, int userId) {
-            super(context, getCeDatabaseName(userId), null, CE_DATABASE_VERSION);
+        public CeDatabaseHelper(Context context, String ceDatabaseName) {
+            super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
         }
 
         /**
@@ -4580,33 +4649,56 @@
             }
         }
 
+        static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
+            // Select accounts from CE that do not exist in DE
+            Cursor cursor = db.rawQuery(
+                    "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
+                            + " FROM " + CE_TABLE_ACCOUNTS
+                            + " WHERE NOT EXISTS "
+                            + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
+                            + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+                            + " )", null);
+            try {
+                List<Account> accounts = new ArrayList<>(cursor.getCount());
+                while (cursor.moveToNext()) {
+                    String accountName = cursor.getString(0);
+                    String accountType = cursor.getString(1);
+                    accounts.add(new Account(accountName, accountType));
+                }
+                return accounts;
+            } finally {
+                cursor.close();
+            }
+        }
+
         /**
          * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
          * it also performs migration to the new CE database.
          * @param context
          * @param userId id of the user where the database is located
          */
-        static CeDatabaseHelper create(Context context, int userId) {
-
-            File oldDatabaseFile = new File(getPreNDatabaseName(userId));
-            File ceDatabaseFile = new File(getCeDatabaseName(userId));
+        static CeDatabaseHelper create(
+                Context context,
+                int userId,
+                File preNDatabaseFile,
+                File ceDatabaseFile) {
             boolean newDbExists = ceDatabaseFile.exists();
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
-                        + oldDatabaseFile.exists() + " newDbExists=" + newDbExists);
+                        + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
             }
             boolean removeOldDb = false;
-            if (!newDbExists && oldDatabaseFile.exists()) {
-                removeOldDb = migratePreNDbToCe(oldDatabaseFile, ceDatabaseFile);
+            if (!newDbExists && preNDatabaseFile.exists()) {
+                removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
             }
             // Try to open and upgrade if necessary
-            CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, userId);
+            CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
             ceHelper.getWritableDatabase();
             ceHelper.close();
             if (removeOldDb) {
                 // TODO STOPSHIP - backup file during testing. Remove file before the release
-                Log.i(TAG, "Migration complete - creating backup of old db " + oldDatabaseFile);
-                renameToBakFile(oldDatabaseFile);
+                Log.i(TAG, "Migration complete - creating backup of old db " + preNDatabaseFile);
+                renameToBakFile(preNDatabaseFile);
             }
             return ceHelper;
         }
@@ -4771,12 +4863,14 @@
         }
     }
 
+    @VisibleForTesting
     protected void installNotification(final int notificationId, final Notification n,
             UserHandle user) {
         ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
                 .notifyAsUser(null, notificationId, n, user);
     }
 
+    @VisibleForTesting
     protected void cancelNotification(int id, UserHandle user) {
         long identityToken = clearCallingIdentity();
         try {
@@ -5314,7 +5408,7 @@
         HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
         if (userDataForAccount == null) {
             // need to populate the cache for this account
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
             userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
             accounts.userDataCache.put(account, userDataForAccount);
         }
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 0f48d21..c971262 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -628,13 +628,16 @@
         public Bundle result = null;
         public AssistStructure structure = null;
         public AssistContent content = null;
+        public Bundle receiverExtras;
+
         public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
-                String _hint, IResultReceiver _receiver, int _userHandle) {
+                String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _userHandle) {
             activity = _activity;
             extras = _extras;
             intent = _intent;
             hint = _hint;
             receiver = _receiver;
+            receiverExtras = _receiverExtras;
             userHandle = _userHandle;
         }
         @Override
@@ -8792,6 +8795,7 @@
             rti.bounds = new Rect(tr.mBounds);
         }
         rti.isDockable = tr.canGoInDockedStack();
+        rti.resizeMode = tr.mResizeMode;
 
         ActivityRecord base = null;
         ActivityRecord top = null;
@@ -9424,7 +9428,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);
         }
@@ -9868,7 +9873,6 @@
         boolean isSystemInitiated = callingUid == Process.SYSTEM_UID;
         long ident = Binder.clearCallingIdentity();
         try {
-            final ActivityStack stack = mStackSupervisor.getFocusedStack();
             if (!isSystemInitiated) {
                 task.mLockTaskUid = callingUid;
                 if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
@@ -9877,11 +9881,12 @@
                     StatusBarManagerInternal statusBarManager =
                             LocalServices.getService(StatusBarManagerInternal.class);
                     if (statusBarManager != null) {
-                        statusBarManager.showScreenPinningRequest();
+                        statusBarManager.showScreenPinningRequest(task.taskId);
                     }
                     return;
                 }
 
+                final ActivityStack stack = mStackSupervisor.getFocusedStack();
                 if (stack == null || task != stack.topTask()) {
                     throw new IllegalArgumentException("Invalid task, not in foreground");
                 }
@@ -9922,15 +9927,13 @@
     }
 
     @Override
-    public void startLockTaskModeOnCurrent() throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startLockTaskModeOnCurrent");
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                ActivityRecord r = mStackSupervisor.topRunningActivityLocked();
-                if (r != null) {
-                    startLockTaskModeLocked(r.task);
-                }
+                startLockTaskMode(taskId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -9974,8 +9977,9 @@
     }
 
     @Override
-    public void stopLockTaskModeOnCurrent() throws RemoteException {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopLockTaskModeOnCurrent");
+    public void stopSystemLockTaskMode() throws RemoteException {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
         long ident = Binder.clearCallingIdentity();
         try {
             stopLockTaskMode();
@@ -11804,7 +11808,7 @@
     @Override
     public Bundle getAssistContextExtras(int requestType) {
         PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
-                null, UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT);
+                null, null, true, UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT);
         if (pae == null) {
             return null;
         }
@@ -11868,14 +11872,17 @@
 
     @Override
     public boolean requestAssistContextExtras(int requestType, IResultReceiver receiver,
-            IBinder activityToken) {
-        return enqueueAssistContext(requestType, null, null, receiver, activityToken,
-                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT) != null;
+            Bundle receiverExtras,
+            IBinder activityToken, boolean focused) {
+        return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
+                activityToken, focused,
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT)
+                != null;
     }
 
     private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
-            IResultReceiver receiver, IBinder activityToken, int userHandle, Bundle args,
-            long timeout) {
+            IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken, boolean focused,
+            int userHandle, Bundle args, long timeout) {
         enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
                 "enqueueAssistContext()");
         synchronized (this) {
@@ -11888,14 +11895,24 @@
                 Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
                 return null;
             }
-            if (activityToken != null) {
-                ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
-                if (activity != caller) {
-                    Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
-                            + " is not current top " + activity);
+            if (focused) {
+                if (activityToken != null) {
+                    ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
+                    if (activity != caller) {
+                        Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
+                                + " is not current top " + activity);
+                        return null;
+                    }
+                }
+            } else {
+                activity = ActivityRecord.forTokenLocked(activityToken);
+                if (activity == null) {
+                    Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
+                            + " couldn't be found");
                     return null;
                 }
             }
+
             PendingAssistExtras pae;
             Bundle extras = new Bundle();
             if (args != null) {
@@ -11903,7 +11920,8 @@
             }
             extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
             extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
-            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, userHandle);
+            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
+                    userHandle);
             try {
                 activity.app.thread.requestAssistContextExtras(activity.appToken, pae,
                         requestType);
@@ -11973,9 +11991,11 @@
             if ((sendReceiver=pae.receiver) != null) {
                 // Caller wants result sent back to them.
                 sendBundle = new Bundle();
-                sendBundle.putBundle("data", pae.extras);
-                sendBundle.putParcelable("structure", pae.structure);
-                sendBundle.putParcelable("content", pae.content);
+                sendBundle.putBundle(VoiceInteractionSession.KEY_DATA, pae.extras);
+                sendBundle.putParcelable(VoiceInteractionSession.KEY_STRUCTURE, pae.structure);
+                sendBundle.putParcelable(VoiceInteractionSession.KEY_CONTENT, pae.content);
+                sendBundle.putBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS,
+                        pae.receiverExtras);
             }
         }
         if (sendReceiver != null) {
@@ -12005,8 +12025,8 @@
 
     public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
             Bundle args) {
-        return enqueueAssistContext(requestType, intent, hint, null, null, userHandle, args,
-                PENDING_ASSIST_EXTRAS_TIMEOUT) != null;
+        return enqueueAssistContext(requestType, intent, hint, null, null, null, true,
+                userHandle, args, PENDING_ASSIST_EXTRAS_TIMEOUT) != null;
     }
 
     public void registerProcessObserver(IProcessObserver observer) {
@@ -13164,6 +13184,9 @@
         }
     }
 
+    private volatile long mWtfClusterStart;
+    private volatile int mWtfClusterCount;
+
     /**
      * Write a description of an error (crash, WTF, ANR) to the drop box.
      * @param eventType to include in the drop box tag ("crash", "wtf", etc.)
@@ -13190,6 +13213,16 @@
         // Exit early if the dropbox isn't configured to accept this report type.
         if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
 
+        // Rate-limit how often we're willing to do the heavy lifting below to
+        // collect and record logs; currently 5 logs per 10 second period.
+        final long now = SystemClock.elapsedRealtime();
+        if (now - mWtfClusterStart > 10 * DateUtils.SECOND_IN_MILLIS) {
+            mWtfClusterStart = now;
+            mWtfClusterCount = 1;
+        } else {
+            if (mWtfClusterCount++ >= 5) return;
+        }
+
         final StringBuilder sb = new StringBuilder(1024);
         appendDropBoxProcessHeaders(process, processName, sb);
         if (process != null) {
@@ -17131,7 +17164,7 @@
                 // but historically it has not been protected and apps may be using it
                 // to poke their own app widget.  So, instead of making it protected,
                 // just limit it to the caller.
-                if (callerApp == null) {
+                if (callerPackage == null) {
                     String msg = "Permission Denial: not allowed to send broadcast "
                             + action + " from unknown caller.";
                     Slog.w(TAG, msg);
@@ -17140,17 +17173,17 @@
                     // They are good enough to send to an explicit component...  verify
                     // it is being sent to the calling app.
                     if (!intent.getComponent().getPackageName().equals(
-                            callerApp.info.packageName)) {
+                            callerPackage)) {
                         String msg = "Permission Denial: not allowed to send broadcast "
                                 + action + " to "
                                 + intent.getComponent().getPackageName() + " from "
-                                + callerApp.info.packageName;
+                                + callerPackage;
                         Slog.w(TAG, msg);
                         throw new SecurityException(msg);
                     }
                 } else {
                     // Limit broadcast to their own package.
-                    intent.setPackage(callerApp.info.packageName);
+                    intent.setPackage(callerPackage);
                 }
             }
         }
@@ -17607,7 +17640,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;
@@ -20947,6 +20980,13 @@
                 mStackSupervisor.notifyAppTransitionDone();
             }
         }
+
+        @Override
+        public List<IBinder> getTopVisibleActivities() {
+            synchronized (ActivityManagerService.this) {
+                return mStackSupervisor.getTopVisibleActivities();
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 43e1bdf..0331470 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -98,13 +98,16 @@
      *                   launch
      * @param componentName the component name of the activity being launched
      * @param processRunning whether the process that will contains the activity is already running
+     * @param processSwitch whether the process that will contain the activity didn't have any
+     *                      activity that was stopped, i.e. the started activity is "switching"
+     *                      processes
      */
     void notifyActivityLaunched(int resultCode, @Nullable String componentName,
-            boolean processRunning) {
+            boolean processRunning, boolean processSwitch) {
 
-        if (resultCode < 0 || componentName == null) {
+        if (resultCode < 0 || componentName == null || !processSwitch) {
 
-            // Failed to launch, don't track anything.
+            // Failed to launch or it was not a process switch, so we don't care about the timing.
             reset();
             return;
         }
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..ab3a0b3 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -98,6 +98,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -1759,8 +1760,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 +1812,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 +3362,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 +3451,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 +4260,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
@@ -4270,4 +4283,31 @@
             }
             return result;
     }
+
+    /**
+     * @return a list of activities which are the top ones in each visible stack. The first
+     * entry will be the focused activity.
+     */
+    public List<IBinder> getTopVisibleActivities() {
+        final ActivityDisplay display = mActivityDisplays.get(Display.DEFAULT_DISPLAY);
+        if (display == null) {
+            return Collections.EMPTY_LIST;
+        }
+        ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+        final ArrayList<ActivityStack> stacks = display.mStacks;
+        for (int i = stacks.size() - 1; i >= 0; i--) {
+            ActivityStack stack = stacks.get(i);
+            if (stack.getStackVisibilityLocked(null) == ActivityStack.STACK_VISIBLE) {
+                ActivityRecord top = stack.topActivity();
+                if (top != null) {
+                    if (stack == mFocusedStack) {
+                        topActivityTokens.add(0, top.appToken);
+                    } else {
+                        topActivityTokens.add(top.appToken);
+                    }
+                }
+            }
+        }
+        return topActivityTokens;
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 6fba8c8..a96f23a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -137,6 +137,7 @@
 
     // Share state variable among methods when starting an activity.
     private ActivityRecord mStartActivity;
+    private ActivityRecord mReusedActivity;
     private Intent mIntent;
     private int mCallingUid;
     private ActivityOptions mOptions;
@@ -785,15 +786,40 @@
 
             final String componentName = outRecord[0] != null ? outRecord[0].shortComponentName
                     : null;
-            final boolean processRunning = outRecord[0] != null &&
-                    mService.mProcessNames.get(outRecord[0].processName,
-                            outRecord[0].appInfo.uid) != null;
+            final ActivityRecord launchedActivity = mReusedActivity != null
+                    ? mReusedActivity : outRecord[0];
+            final ProcessRecord processRecord = launchedActivity != null
+                    ? mService.mProcessNames.get(launchedActivity.processName,
+                            launchedActivity.appInfo.uid)
+                    : null;
+            final boolean processRunning = processRecord != null;
+
+            // We consider this a "process switch" if the process of the activity that gets launched
+            // didn't have an activity that was in started state. In this case, we assume that lot
+            // of caches might be purged so the time until it produces the first frame is very
+            // interesting.
+            final boolean processSwitch = processRecord == null
+                    || !hasStartedActivity(processRecord, launchedActivity);
             mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, componentName,
-                    processRunning);
+                    processRunning, processSwitch);
             return res;
         }
     }
 
+    final boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
+        final ArrayList<ActivityRecord> activities = record.activities;
+        for (int i = activities.size() - 1; i >= 0; i--) {
+            final ActivityRecord activity = activities.get(i);
+            if (launchedActivity == activity) {
+                continue;
+            }
+            if (!activity.stopped) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo,
             Bundle bOptions, int userId) {
@@ -883,16 +909,16 @@
 
         mIntent.setFlags(mLaunchFlags);
 
-        ActivityRecord intentActivity = getReusableIntentActivity();
+        mReusedActivity = getReusableIntentActivity();
 
         final int preferredLaunchStackId =
                 (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
 
-        if (intentActivity != null) {
+        if (mReusedActivity != null) {
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
             // the device would otherwise leave the locked task.
-            if (mSupervisor.isLockTaskModeViolation(intentActivity.task,
+            if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task,
                     (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                             == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                 mSupervisor.showLockTaskToast();
@@ -901,12 +927,12 @@
             }
 
             if (mStartActivity.task == null) {
-                mStartActivity.task = intentActivity.task;
+                mStartActivity.task = mReusedActivity.task;
             }
-            if (intentActivity.task.intent == null) {
+            if (mReusedActivity.task.intent == null) {
                 // This task was started because of movement of the activity based on affinity...
                 // Now that we are actually launching it, we can assign the base intent.
-                intentActivity.task.setIntent(mStartActivity);
+                mReusedActivity.task.setIntent(mStartActivity);
             }
 
             // This code path leads to delivering a new intent, we want to make sure we schedule it
@@ -917,7 +943,7 @@
                 // In this situation we want to remove all activities from the task up to the one
                 // being started. In most cases this means we are resetting the task to its initial
                 // state.
-                final ActivityRecord top = intentActivity.task.performClearTaskForReuseLocked(
+                final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
                         mStartActivity, mLaunchFlags);
                 if (top != null) {
                     if (top.frontOfTask) {
@@ -931,7 +957,7 @@
                 }
             }
 
-            intentActivity = setTargetStackAndMoveToFrontIfNeeded(intentActivity);
+            mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
 
             if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                 // We don't need to start a new activity, and the client said not to do anything
@@ -941,7 +967,7 @@
                 return START_RETURN_INTENT_TO_CALLER;
             }
 
-            setTaskFromIntentActivity(intentActivity);
+            setTaskFromIntentActivity(mReusedActivity);
 
             if (!mAddingToTask && mReuseTask == null) {
                 // We didn't do anything...  but it was needed (a.k.a., client don't use that
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 1825c88..0e192ea 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -69,6 +69,12 @@
             return;
         }
 
+        if (!mService.isUserRunning(mUserId, 0)) {
+            Slog.i(TAG, "User " + mUserId + " is no longer running; skipping remaining receivers");
+            onFinished();
+            return;
+        }
+
         final ResolveInfo ri = mTargets.get(mIndex++);
         final ComponentName componentName = ri.activityInfo.getComponentName();
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index e69c662..08d2399 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1501,22 +1501,22 @@
                 Math.min((int)(mTmpStableBounds.width() / density), serviceConfig.screenWidthDp);
         config.screenHeightDp =
                 Math.min((int)(mTmpStableBounds.height() / density), serviceConfig.screenHeightDp);
-        config.smallestScreenWidthDp = Math.min(config.screenWidthDp, config.screenHeightDp);
 
         // TODO: Orientation?
         config.orientation = (config.screenWidthDp <= config.screenHeightDp)
                 ? Configuration.ORIENTATION_PORTRAIT
                 : Configuration.ORIENTATION_LANDSCAPE;
 
-        // For calculating screen layout, we need to use the non-decor inset screen area for the
-        // calculation for compatibility reasons, i.e. screen area without system bars that could
-        // never go away in Honeycomb.
+        // For calculating screen layout and smallest screen width, we need to use the non-decor
+        // inset screen area for the calculation for compatibility reasons, i.e. screen area without
+        // system bars that could never go away in Honeycomb.
         final int compatScreenWidthDp = (int)(mTmpNonDecorBounds.width() / density);
         final int compatScreenHeightDp = (int)(mTmpNonDecorBounds.height() / density);
         final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout);
         final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
-        final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
-        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
+        config.smallestScreenWidthDp = Math.min(compatScreenHeightDp, compatScreenWidthDp);
+        config.screenLayout = Configuration.reduceScreenLayout(sl, longSize,
+                config.smallestScreenWidthDp);
         return config;
     }
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index bea26c7..5ebb9a7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -278,6 +278,31 @@
                 // Dispatch unlocked to system services
                 mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
 
+                // Dispatch unlocked to external apps
+                final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+                unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                unlockedIntent.addFlags(
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+                mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
+                        null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                        userId);
+
+                if (getUserInfo(userId).isManagedProfile()) {
+                    UserInfo parent = getUserManager().getProfileParent(userId);
+                    if (parent != null) {
+                        final Intent profileUnlockedIntent = new Intent(
+                                Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+                        profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+                        profileUnlockedIntent.addFlags(
+                                Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                                | Intent.FLAG_RECEIVER_FOREGROUND);
+                        mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
+                                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                                null, false, false, MY_PID, SYSTEM_UID,
+                                parent.id);
+                    }
+                }
+
                 // Send PRE_BOOT broadcasts if fingerprint changed
                 final UserInfo info = getUserInfo(userId);
                 if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
@@ -320,31 +345,6 @@
                 // Remember that we logged in
                 mUserManager.onUserLoggedIn(userId);
 
-                // Dispatch unlocked to external apps
-                final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
-                unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                unlockedIntent.addFlags(
-                        Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
-                mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
-                        null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                        userId);
-
-                if (getUserInfo(userId).isManagedProfile()) {
-                    UserInfo parent = getUserManager().getProfileParent(userId);
-                    if (parent != null) {
-                        final Intent profileUnlockedIntent = new Intent(
-                                Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
-                        profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
-                        profileUnlockedIntent.addFlags(
-                                Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                                | Intent.FLAG_RECEIVER_FOREGROUND);
-                        mService.broadcastIntentLocked(null, null, profileUnlockedIntent,
-                                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                                null, false, false, MY_PID, SYSTEM_UID,
-                                parent.id);
-                    }
-                }
-
                 final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                 bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -893,8 +893,9 @@
     boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
             ProgressReporter progress) {
         synchronized (mService) {
-            // Bail if already running unlocked
+            // Bail if already running unlocked, or if not running at all
             final UserState uss = mStartedUsers.get(userId);
+            if (uss == null) return false;
             switch (uss.state) {
                 case STATE_RUNNING_UNLOCKING:
                 case STATE_RUNNING_UNLOCKED:
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 58db985..a6dfab0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1290,7 +1290,7 @@
             // Check if the ringer mode handles this adjustment. If it does we don't
             // need to adjust the volume further.
             final int result = checkForRingerModeChange(aliasIndex, direction, step,
-                    streamState.mIsMuted);
+                    streamState.mIsMuted, callingPackage, flags);
             adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
             // If suppressing a volume adjustment in silent mode, display the UI hint
             if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
@@ -1302,8 +1302,7 @@
             }
         }
         // If the ringermode is suppressing media, prevent changes
-        if (streamTypeAlias == AudioSystem.STREAM_MUSIC
-                && (mRingerModeMutedStreams & (1 << AudioSystem.STREAM_MUSIC)) != 0) {
+        if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
             adjustVolume = false;
         }
         int oldIndex = mStreamStates[streamType].getIndex(device);
@@ -1551,6 +1550,10 @@
             throw new SecurityException("Not allowed to change Do Not Disturb state");
         }
 
+        if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
+            return;
+        }
+
         synchronized (mSafeMediaVolumeState) {
             // reset any pending volume command
             mPendingVolumeCommand = null;
@@ -1601,6 +1604,19 @@
         sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
+    // No ringer affected streams can be changed in total silence mode except those that
+    // will cause the device to exit total silence mode.
+    private boolean volumeAdjustmentAllowedByDnd(int streamTypeAlias, int flags) {
+        if (mNm.getZenMode() == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
+                && isStreamMutedByRingerMode(streamTypeAlias)) {
+            if (!(((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+                    (streamTypeAlias == getUiSoundsStreamType()))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /** @see AudioManager#forceVolumeControlStream(int) */
     public void forceVolumeControlStream(int streamType, IBinder cb) {
         synchronized(mForceControlStreamLock) {
@@ -3366,7 +3382,8 @@
      * adjusting volume. If so, this will set the proper ringer mode and volume
      * indices on the stream states.
      */
-    private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted) {
+    private int checkForRingerModeChange(int oldIndex, int direction, int step, boolean isMuted,
+            String caller, int flags) {
         final boolean isTv = mPlatformType == AudioSystem.PLATFORM_TELEVISION;
         int result = FLAG_ADJUST_VOLUME;
         int ringerMode = getRingerModeInternal();
@@ -3455,6 +3472,12 @@
             break;
         }
 
+        if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
+                && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)
+                && (flags & AudioManager.FLAG_FROM_KEY) == 0) {
+            throw new SecurityException("Not allowed to change Do Not Disturb state");
+        }
+
         setRingerMode(ringerMode, TAG + ".checkForRingerModeChange", false /*external*/);
 
         mPrevVolDirection = direction;
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
index cd8eb4e..7d9adf2 100644
--- a/services/core/java/com/android/server/camera/CameraService.java
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -15,32 +15,28 @@
  */
 package com.android.server.camera;
 
-import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.UserInfo;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
 import android.nfc.INfcAdapter;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Binder;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserManager;
 import android.os.SystemProperties;
-import android.util.Slog;
+import android.os.UserManager;
 import android.util.ArraySet;
+import android.util.Slog;
 
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -225,11 +221,11 @@
     }
 
     private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
-        List<UserInfo> userProfiles = mUserManager.getEnabledProfiles(currentUserHandle);
-        Set<Integer> handles = new HashSet<>(userProfiles.size());
+        int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
+        Set<Integer> handles = new ArraySet<>(userProfiles.length);
 
-        for (UserInfo i : userProfiles) {
-            handles.add(i.id);
+        for (int id : userProfiles) {
+            handles.add(id);
         }
 
         return handles;
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/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 2cba93fd..79b5978 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -57,12 +57,16 @@
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseArray;
 
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.IState;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.IoThread;
@@ -95,6 +99,12 @@
     private final static boolean DBG = false;
     private final static boolean VDBG = false;
 
+    private static final Class[] messageClasses = {
+            Tethering.class, TetherMasterSM.class, TetherInterfaceSM.class
+    };
+    private static final SparseArray<String> sMagicDecoderRing =
+            MessageUtils.findMessageNames(messageClasses);
+
     // TODO - remove both of these - should be part of interface inspection/selection stuff
     private String[] mTetherableUsbRegexs;
     private String[] mTetherableWifiRegexs;
@@ -235,6 +245,8 @@
 
     @Override
     public void interfaceStatusChanged(String iface, boolean up) {
+        // Never called directly: only called from interfaceLinkStateChanged.
+        // See NetlinkHandler.cpp:71.
         if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
         boolean found = false;
         boolean usb = false;
@@ -274,7 +286,6 @@
 
     @Override
     public void interfaceLinkStateChanged(String iface, boolean up) {
-        if (VDBG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up);
         interfaceStatusChanged(iface, up);
     }
 
@@ -660,8 +671,11 @@
                 erroredList);
         mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
         if (DBG) {
-            Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
-                    activeList.size() + ", " + erroredList.size());
+            Log.d(TAG, String.format(
+                    "sendTetherStateChangedBroadcast avail=[%s] active=[%s] error=[%s]",
+                    TextUtils.join(",", availableList),
+                    TextUtils.join(",", activeList),
+                    TextUtils.join(",", erroredList)));
         }
 
         if (usbTethered) {
@@ -989,31 +1003,39 @@
         return retVal;
     }
 
+    private void maybeLogMessage(State state, int what) {
+        if (DBG) {
+            Log.d(TAG, state.getName() + " got " +
+                    sMagicDecoderRing.get(what, Integer.toString(what)));
+        }
+    }
+
     class TetherInterfaceSM extends StateMachine {
+        private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
         // notification from the master SM that it's not in tether mode
-        static final int CMD_TETHER_MODE_DEAD            =  1;
+        static final int CMD_TETHER_MODE_DEAD            = BASE_IFACE + 1;
         // request from the user that it wants to tether
-        static final int CMD_TETHER_REQUESTED            =  2;
+        static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
         // request from the user that it wants to untether
-        static final int CMD_TETHER_UNREQUESTED          =  3;
+        static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
         // notification that this interface is down
-        static final int CMD_INTERFACE_DOWN              =  4;
+        static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
         // notification that this interface is up
-        static final int CMD_INTERFACE_UP                =  5;
+        static final int CMD_INTERFACE_UP                = BASE_IFACE + 5;
         // notification from the master SM that it had an error turning on cellular dun
-        static final int CMD_CELL_DUN_ERROR              =  6;
+        static final int CMD_CELL_DUN_ERROR              = BASE_IFACE + 6;
         // notification from the master SM that it had trouble enabling IP Forwarding
-        static final int CMD_IP_FORWARDING_ENABLE_ERROR  =  7;
+        static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
         // notification from the master SM that it had trouble disabling IP Forwarding
-        static final int CMD_IP_FORWARDING_DISABLE_ERROR =  8;
+        static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
         // notification from the master SM that it had trouble starting tethering
-        static final int CMD_START_TETHERING_ERROR       =  9;
+        static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
         // notification from the master SM that it had trouble stopping tethering
-        static final int CMD_STOP_TETHERING_ERROR        = 10;
+        static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
         // notification from the master SM that it had trouble setting the DNS forwarders
-        static final int CMD_SET_DNS_FORWARDERS_ERROR    = 11;
+        static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
         // the upstream connection has changed
-        static final int CMD_TETHER_CONNECTION_CHANGED   = 12;
+        static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
 
         private State mDefaultState;
 
@@ -1124,7 +1146,7 @@
 
             @Override
             public boolean processMessage(Message message) {
-                if (DBG) Log.d(TAG, "InitialState.processMessage what=" + message.what);
+                maybeLogMessage(this, message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_REQUESTED:
@@ -1165,7 +1187,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                if (DBG) Log.d(TAG, "StartingState.processMessage what=" + message.what);
+                maybeLogMessage(this, message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     // maybe a parent class?
@@ -1255,7 +1277,7 @@
 
             @Override
             public boolean processMessage(Message message) {
-                if (DBG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
+                maybeLogMessage(this, message.what);
                 boolean retValue = true;
                 boolean error = false;
                 switch (message.what) {
@@ -1480,18 +1502,19 @@
     }
 
     class TetherMasterSM extends StateMachine {
+        private static final int BASE_MASTER                    = Protocol.BASE_TETHERING;
         // an interface SM has requested Tethering
-        static final int CMD_TETHER_MODE_REQUESTED              = 1;
+        static final int CMD_TETHER_MODE_REQUESTED              = BASE_MASTER + 1;
         // an interface SM has unrequested Tethering
-        static final int CMD_TETHER_MODE_UNREQUESTED            = 2;
+        static final int CMD_TETHER_MODE_UNREQUESTED            = BASE_MASTER + 2;
         // upstream connection change - do the right thing
-        static final int CMD_UPSTREAM_CHANGED                   = 3;
+        static final int CMD_UPSTREAM_CHANGED                   = BASE_MASTER + 3;
         // we don't have a valid upstream conn, check again after a delay
-        static final int CMD_RETRY_UPSTREAM                     = 4;
+        static final int CMD_RETRY_UPSTREAM                     = BASE_MASTER + 4;
         // Events from NetworkCallbacks that we process on the master state
         // machine thread on behalf of the UpstreamNetworkMonitor.
-        static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED  = 5;
-        static final int EVENT_UPSTREAM_LOST                    = 6;
+        static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED  = BASE_MASTER + 5;
+        static final int EVENT_UPSTREAM_LOST                    = BASE_MASTER + 6;
 
         // This indicates what a timeout event relates to.  A state that
         // sends itself a delayed timeout event and handles incoming timeout events
@@ -1748,7 +1771,7 @@
             }
 
             protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
-                if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
+                if (DBG) Log.d(TAG, "Notifying tethered with upstream=" + ifaceName);
                 mCurrentUpstreamIface = ifaceName;
                 for (TetherInterfaceSM sm : mNotifyList) {
                     sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
@@ -1862,7 +1885,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                if (DBG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
+                maybeLogMessage(this, message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
@@ -1910,7 +1933,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                if (DBG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
+                maybeLogMessage(this, message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
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/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
new file mode 100644
index 0000000..d2f015f
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -0,0 +1,156 @@
+/**
+ * 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.fingerprint;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+
+import android.content.Context;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the authentication state for a given client.
+ */
+public abstract class AuthenticationClient extends ClientMonitor {
+    private long mOpId;
+
+    public abstract boolean handleFailedAttempt();
+    public abstract void resetFailedAttempts();
+
+    public AuthenticationClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId, long opId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+        mOpId = opId;
+    }
+
+    @Override
+    public boolean onAuthenticated(int fingerId, int groupId) {
+        boolean result = false;
+        boolean authenticated = fingerId != 0;
+
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver != null) {
+            try {
+                MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
+                        authenticated);
+                if (!authenticated) {
+                    receiver.onAuthenticationFailed(getHalDeviceId());
+                } else {
+                    if (DEBUG) {
+                        Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
+                                + ", id=" + fingerId + ", gp=" + groupId + ")");
+                    }
+                    Fingerprint fp = !getIsRestricted()
+                            ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
+                            : null;
+                    receiver.onAuthenticationSucceeded(getHalDeviceId(), fp);
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify Authenticated:", e);
+                result = true; // client failed
+            }
+        } else {
+            result = true; // client not listening
+        }
+        if (fingerId == 0) {
+            if (receiver != null) {
+                FingerprintUtils.vibrateFingerprintError(getContext());
+            }
+            // allow system-defined limit of number of attempts before giving up
+            result |= handleFailedAttempt();
+        } else {
+            if (receiver != null) {
+                FingerprintUtils.vibrateFingerprintSuccess(getContext());
+            }
+            result |= true; // we have a valid fingerprint, done
+            resetFailedAttempts();
+        }
+        return result;
+    }
+
+    /**
+     * Start authentication
+     */
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "start authentication: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.authenticate(mOpId, getGroupId());
+            if (result != 0) {
+                Slog.w(TAG, "startAuthentication failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancelAuthentication();
+            if (result != 0) {
+                Slog.w(TAG, "stopAuthentication failed, result=" + result);
+                return result;
+            }
+            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopAuthentication failed", e);
+            return ERROR_ESRCH;
+        }
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
+        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onRemoved(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
+        return true; // Invalid for Authenticate
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
new file mode 100644
index 0000000..90998ed
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -0,0 +1,211 @@
+/**
+ * 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.fingerprint;
+
+import android.Manifest;
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Abstract base class for keeping track and dispatching events from fingerprintd to the
+ * the current client.  Subclasses are responsible for coordinating the interaction with
+ * fingerprintd for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ */
+public abstract class ClientMonitor implements IBinder.DeathRecipient {
+    protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
+    protected static final int ERROR_ESRCH = 3; // Likely fingerprintd is dead. See errno.h.
+    protected static final boolean DEBUG = FingerprintService.DEBUG;
+    private IBinder mToken;
+    private IFingerprintServiceReceiver mReceiver;
+    private int mUserId;
+    private int mGroupId;
+    private boolean mIsRestricted; // True if client does not have MANAGE_FINGERPRINT permission
+    private String mOwner;
+    private Context mContext;
+    private long mHalDeviceId;
+
+    /**
+     * @param context context of FingerprintService
+     * @param halDeviceId the HAL device ID of the associated fingerprint hardware
+     * @param token a unique token for the client
+     * @param receiver recipient of related events (e.g. authentication)
+     * @param userId userId for the fingerprint set
+     * @param groupId groupId for the fingerprint set
+     * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
+     * permission
+     * @param owner name of the client that owns this
+     */
+    public ClientMonitor(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
+            String owner) {
+        mContext = context;
+        mHalDeviceId = halDeviceId;
+        mToken = token;
+        mReceiver = receiver;
+        mUserId = userId;
+        mGroupId = groupId;
+        mIsRestricted = restricted;
+        mOwner = owner;
+        try {
+            token.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+        }
+    }
+
+    /**
+     * Contacts fingerprintd to start the client.
+     * @return 0 on succes, errno from driver on failure
+     */
+    public abstract int start();
+
+    /**
+     * Contacts fingerprintd to stop the client.
+     * @param initiatedByClient whether the operation is at the request of a client
+     */
+    public abstract int stop(boolean initiatedByClient);
+
+    /**
+     * Method to explicitly poke powermanager on events
+     */
+    public abstract void notifyUserActivity();
+
+    /**
+     * Gets the fingerprint daemon from the cached state in the container class.
+     */
+    public abstract IFingerprintDaemon getFingerprintDaemon();
+
+    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
+    // respective client (e.g. enrolling shouldn't get authenticate events).
+    // All of these return 'true' if the operation is completed and it's ok to move
+    // to the next client (e.g. authentication accepts or rejects a fingerprint).
+    public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
+    public abstract boolean onAuthenticated(int fingerId, int groupId);
+    public abstract boolean onRemoved(int fingerId, int groupId);
+    public abstract boolean onEnumerationResult(int fingerId, int groupId);
+
+    /**
+     * Called when we get notification from fingerprintd that an image has been acquired.
+     * Common to authenticate and enroll.
+     * @param acquiredInfo info about the current image acquisition
+     * @return true if client should be removed
+     */
+    public boolean onAcquired(int acquiredInfo) {
+        if (mReceiver == null)
+            return true; // client not connected
+        try {
+            mReceiver.onAcquired(getHalDeviceId(), acquiredInfo);
+            return false; // acquisition continues...
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to invoke sendAcquired:", e);
+            return true; // client failed
+        } finally {
+            // Good scans will keep the device awake
+            if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+                notifyUserActivity();
+            }
+        }
+    }
+
+    /**
+     * Called when we get notification from fingerprintd that an error has occurred with the
+     * current operation. Common to authenticate, enroll, enumerate and remove.
+     * @param error
+     * @return true if client should be removed
+     */
+    public boolean onError(int error) {
+        if (mReceiver != null) {
+            try {
+                mReceiver.onError(getHalDeviceId(), error);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke sendError:", e);
+            }
+        }
+        return true; // errors always remove current client
+    }
+
+    public void destroy() {
+        if (mToken != null) {
+            try {
+                mToken.unlinkToDeath(this, 0);
+            } catch (NoSuchElementException e) {
+                // TODO: remove when duplicate call bug is found
+                Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+            }
+            mToken = null;
+        }
+        mReceiver = null;
+    }
+
+    @Override
+    public void binderDied() {
+        mToken = null;
+        mReceiver = null;
+        onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mToken != null) {
+                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public final Context getContext() {
+        return mContext;
+    }
+
+    public final long getHalDeviceId() {
+        return mHalDeviceId;
+    }
+
+    public final String getOwnerString() {
+        return mOwner;
+    }
+
+    public final IFingerprintServiceReceiver getReceiver() {
+        return mReceiver;
+    }
+
+    public final boolean getIsRestricted() {
+        return mIsRestricted;
+    }
+
+    public final int getUserId() {
+        return mUserId;
+    }
+
+    public final int getGroupId() {
+        return mGroupId;
+    }
+
+    public final IBinder getToken() {
+        return mToken;
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
new file mode 100644
index 0000000..ce5b890
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -0,0 +1,136 @@
+/**
+ * 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.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+    private static final long MS_PER_SEC = 1000;
+    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+    private byte[] mCryptoToken;
+
+    public EnrollClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+        mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+    }
+
+    @Override
+    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
+        if (remaining == 0) {
+            FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
+                    getUserId());
+        }
+        return sendEnrollResult(fingerId, groupId, remaining);
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+
+        FingerprintUtils.vibrateFingerprintSuccess(getContext());
+        MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
+        try {
+            receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
+            return remaining == 0;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify EnrollResult:", e);
+            return true;
+        }
+    }
+
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "enroll: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+        try {
+            final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
+            if (result != 0) {
+                Slog.w(TAG, "startEnroll failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startEnroll failed", e);
+        }
+        return 0; // success
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopEnrollment: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancelEnrollment();
+            if (result != 0) {
+                Slog.w(TAG, "startEnrollCancel failed, result = " + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stopEnrollment failed", e);
+        }
+        if (initiatedByClient) {
+            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+        return 0;
+    }
+
+    @Override
+    public boolean onRemoved(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+    @Override
+    public boolean onAuthenticated(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
+        return true; // Invalid for EnrollClient
+    }
+
+}
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
new file mode 100644
index 0000000..b2e4099
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
@@ -0,0 +1,92 @@
+/**
+ * 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.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the enumeration state for a given client.
+ */
+public abstract class EnumerateClient extends ClientMonitor {
+    public EnumerateClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+    }
+
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        // The fingerprint template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = daemon.enumerate();
+            if (result != 0) {
+                Slog.w(TAG, "start enumerate for user " + getUserId()
+                    + " failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startRemove failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        if (daemon == null) {
+            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+            return ERROR_ESRCH;
+        }
+        try {
+            final int result = daemon.cancelEnumeration();
+            if (result != 0) {
+                Slog.w(TAG, "stop enumeration failed, result=" + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "stop enumeration failed", e);
+            return ERROR_ESRCH;
+        }
+        // We don't actually stop enumerate, but inform the client that the cancel operation
+        // succeeded so we can start the next operation.
+        if (initiatedByClient) {
+            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+        return 0; // success
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+        try {
+            receiver.onRemoved(getHalDeviceId(), fingerId, groupId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify enumerated:", e);
+        }
+        return fingerId == 0; // done when id hits 0
+    }
+}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 3d8bf51..2dafa3e 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -19,7 +19,6 @@
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.trust.TrustManager;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -48,7 +47,6 @@
 import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.server.SystemService;
 
 import org.json.JSONArray;
@@ -75,7 +73,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.NoSuchElementException;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -85,31 +82,36 @@
  * @hide
  */
 public class FingerprintService extends SystemService implements IBinder.DeathRecipient {
-    private static final String TAG = "FingerprintService";
-    private static final boolean DEBUG = true;
+    static final String TAG = "FingerprintService";
+    static final boolean DEBUG = true;
     private static final String FP_DATA_DIR = "fpdata";
     private static final String FINGERPRINTD = "android.hardware.fingerprint.IFingerprintDaemon";
     private static final int MSG_USER_SWITCHING = 10;
-    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
 
-    private ClientMonitor mAuthClient = null;
-    private ClientMonitor mEnrollClient = null;
-    private ClientMonitor mRemoveClient = null;
     private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
             new ArrayList<>();
     private final AppOpsManager mAppOps;
 
-    private static final long MS_PER_SEC = 1000;
     private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
     private static final int MAX_FAILED_ATTEMPTS = 5;
-    private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+    private static final long CANCEL_TIMEOUT_LIMIT = 300; // max wait for onCancel() from HAL,in ms
     private final String mKeyguardPackage;
     private int mCurrentUserId = UserHandle.USER_CURRENT;
-    private int mUserIdForRemove = UserHandle.USER_NULL;
+    private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
+    private Context mContext;
+    private long mHalDeviceId;
+    private int mFailedAttempts;
+    private IFingerprintDaemon mDaemon;
+    private final PowerManager mPowerManager;
+    private final AlarmManager mAlarmManager;
+    private final UserManager mUserManager;
+    private ClientMonitor mCurrentClient;
+    private ClientMonitor mPendingClient;
+    private long mCurrentAuthenticatorId;
 
-    Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(android.os.Message msg) {
             switch (msg.what) {
@@ -123,15 +125,6 @@
         }
     };
 
-    private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
-    private Context mContext;
-    private long mHalDeviceId;
-    private int mFailedAttempts;
-    private IFingerprintDaemon mDaemon;
-    private final PowerManager mPowerManager;
-    private final AlarmManager mAlarmManager;
-    private final UserManager mUserManager;
-
     private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -148,6 +141,26 @@
         }
     };
 
+    private final Runnable mResetClientState = new Runnable() {
+        @Override
+        public void run() {
+            // Warning: if we get here, the driver never confirmed our call to cancel the current
+            // operation (authenticate, enroll, remove, enumerate, etc), which is
+            // really bad.  The result will be a 3-second delay in starting each new client.
+            // If you see this on a device, make certain the driver notifies with
+            // {@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} in response to cancel()
+            // once it has successfully switched to the IDLE state in the fingerprint HAL.
+            // Additionally,{@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} should only be sent
+            // in response to an actual cancel() call.
+            Slog.w(TAG, "Client "
+                    + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+                    + " failed to respond to cancel, starting client "
+                    + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+            mCurrentClient = null;
+            startClient(mPendingClient, false);
+        }
+    };
+
     public FingerprintService(Context context) {
         super(context);
         mContext = context;
@@ -203,64 +216,49 @@
         // TODO: update fingerprint/name pairs
     }
 
-    protected void handleRemoved(long deviceId, int fingerId, int groupId) {
-        final ClientMonitor client = mRemoveClient;
-        if (fingerId != 0) {
-            removeTemplateForUser(mUserIdForRemove, fingerId);
-        } else {
-            mUserIdForRemove = UserHandle.USER_NULL;
+    protected void handleError(long deviceId, int error) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onError(error)) {
+            removeClient(client);
         }
-        if (client != null && client.sendRemoved(fingerId, groupId)) {
+        if (DEBUG) Slog.v(TAG, "handleError(client="
+                + client != null ? client.getOwnerString() : "null" + ", error = " + error + ")");
+        // This is the magic code that starts the next client when the old client finishes.
+        if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+            mHandler.removeCallbacks(mResetClientState);
+            if (mPendingClient != null) {
+                if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
+                startClient(mPendingClient, false);
+                mPendingClient = null;
+            }
+        }
+    }
+
+    protected void handleRemoved(long deviceId, int fingerId, int groupId) {
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onRemoved(fingerId, groupId)) {
             removeClient(client);
         }
     }
 
-    protected void handleError(long deviceId, int error) {
-        if (mEnrollClient != null) {
-            final IBinder token = mEnrollClient.token;
-            if (mEnrollClient.sendError(error)) {
-                stopEnrollment(token, false);
-            }
-        } else if (mAuthClient != null) {
-            final IBinder token = mAuthClient.token;
-            if (mAuthClient.sendError(error)) {
-                stopAuthentication(token, false);
-            }
-        } else if (mRemoveClient != null) {
-            if (mRemoveClient.sendError(error)) removeClient(mRemoveClient);
-        }
-    }
-
     protected void handleAuthenticated(long deviceId, int fingerId, int groupId) {
-        if (mAuthClient != null) {
-            final IBinder token = mAuthClient.token;
-            if (mAuthClient.sendAuthenticated(fingerId, groupId)) {
-                stopAuthentication(token, false);
-                removeClient(mAuthClient);
-            }
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onAuthenticated(fingerId, groupId)) {
+            removeClient(client);
         }
     }
 
     protected void handleAcquired(long deviceId, int acquiredInfo) {
-        if (mEnrollClient != null) {
-            if (mEnrollClient.sendAcquired(acquiredInfo)) {
-                removeClient(mEnrollClient);
-            }
-        } else if (mAuthClient != null) {
-            if (mAuthClient.sendAcquired(acquiredInfo)) {
-                removeClient(mAuthClient);
-            }
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onAcquired(acquiredInfo)) {
+            removeClient(client);
         }
     }
 
     protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
-        if (mEnrollClient != null) {
-            if (mEnrollClient.sendEnrollResult(fingerId, groupId, remaining)) {
-                if (remaining == 0) {
-                    addTemplateForUser(mEnrollClient, fingerId);
-                    removeClient(mEnrollClient);
-                }
-            }
+        ClientMonitor client = mCurrentClient;
+        if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
+            removeClient(client);
         }
     }
 
@@ -274,14 +272,16 @@
     }
 
     private void removeClient(ClientMonitor client) {
-        if (client == null) return;
-        client.destroy();
-        if (client == mAuthClient) {
-            mAuthClient = null;
-        } else if (client == mEnrollClient) {
-            mEnrollClient = null;
-        } else if (client == mRemoveClient) {
-            mRemoveClient = null;
+        if (client != null) {
+            client.destroy();
+            if (client != mCurrentClient && mCurrentClient != null) {
+                Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
+                        + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
+            }
+        }
+        if (mCurrentClient != null) {
+            if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
+            mCurrentClient = null;
         }
     }
 
@@ -303,60 +303,6 @@
                 new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
-    private void resetFailedAttempts() {
-        if (DEBUG && inLockoutMode()) {
-            Slog.v(TAG, "Reset fingerprint lockout");
-        }
-        mFailedAttempts = 0;
-        // If we're asked to reset failed attempts externally (i.e. from Keyguard), the alarm might
-        // still be pending; remove it.
-        cancelLockoutReset();
-        notifyLockoutResetMonitors();
-    }
-
-    private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
-        mFailedAttempts++;
-        if (inLockoutMode()) {
-            // Failing multiple times will continue to push out the lockout time.
-            scheduleLockoutReset();
-            if (clientMonitor != null
-                    && !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
-                Slog.w(TAG, "Cannot send lockout message to client");
-            }
-            return true;
-        }
-        return false;
-    }
-
-    private void removeTemplateForUser(int userId, int fingerId) {
-        mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, userId);
-    }
-
-    private void addTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
-        mFingerprintUtils.addFingerprintForUser(mContext, fingerId, clientMonitor.userId);
-    }
-
-    void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
-            IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "enroll: no fingeprintd!");
-            return;
-        }
-        stopPendingOperations(true);
-        mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted, token.toString());
-        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
-        try {
-            final int result = daemon.enroll(cryptoToken, groupId, timeout);
-            if (result != 0) {
-                Slog.w(TAG, "startEnroll failed, result=" + result);
-                handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startEnroll failed", e);
-        }
-    }
-
     public long startPreEnroll(IBinder token) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
@@ -385,123 +331,52 @@
         return 0;
     }
 
-    private void stopPendingOperations(boolean initiatedByClient) {
-        if (mEnrollClient != null) {
-            stopEnrollment(mEnrollClient.token, initiatedByClient);
-        }
-        if (mAuthClient != null) {
-            stopAuthentication(mAuthClient.token, initiatedByClient);
-        }
-        // mRemoveClient is allowed to continue
-    }
-
     /**
-     * Stop enrollment in progress and inform client if they initiated it.
-     *
-     * @param token token for client
-     * @param initiatedByClient if this call is the result of client action (e.g. calling cancel)
+     * Calls fingerprintd to switch states to the new task. If there's already a current task,
+     * it calls cancel() and sets mPendingClient to begin when the current task finishes
+     * ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
+     * @param newClient the new client that wants to connect
+     * @param initiatedByClient true for authenticate, remove and enroll
      */
-    void stopEnrollment(IBinder token, boolean initiatedByClient) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopEnrollment: no fingeprintd!");
-            return;
-        }
-        final ClientMonitor client = mEnrollClient;
-        if (client == null || client.token != token) return;
-        if (initiatedByClient) {
-            try {
-                int result = daemon.cancelEnrollment();
-                if (result != 0) {
-                    Slog.w(TAG, "startEnrollCancel failed, result = " + result);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "stopEnrollment failed", e);
-            }
-            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
-        }
-        removeClient(mEnrollClient);
-    }
-
-    void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
-            IFingerprintServiceReceiver receiver, int flags, boolean restricted,
-            String opPackageName) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "startAuthentication: no fingeprintd!");
-            return;
-        }
-        stopPendingOperations(true);
-        updateActiveGroup(groupId, opPackageName);
-        mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
-        if (inLockoutMode()) {
-            Slog.v(TAG, "In lockout mode; disallowing authentication");
-            if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
-                Slog.w(TAG, "Cannot send timeout message to client");
-            }
-            mAuthClient = null;
-            return;
-        }
-        try {
-            final int result = daemon.authenticate(opId, groupId);
-            if (result != 0) {
-                Slog.w(TAG, "startAuthentication failed, result=" + result);
-                handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startAuthentication failed", e);
+    private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+        ClientMonitor currentClient = mCurrentClient;
+        if (currentClient != null) {
+            if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
+            currentClient.stop(initiatedByClient);
+            mPendingClient = newClient;
+            mHandler.removeCallbacks(mResetClientState);
+            mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
+        } else if (newClient != null) {
+            mCurrentClient = newClient;
+            if (DEBUG) Slog.v(TAG, "starting client "
+                    + newClient.getClass().getSuperclass().getSimpleName()
+                    + "(" + newClient.getOwnerString() + ")"
+                    + ", initiatedByClient = " + initiatedByClient + ")");
+            newClient.start();
         }
     }
 
-    /**
-     * Stop authentication in progress and inform client if they initiated it.
-     *
-     * @param token token for client
-     * @param initiatedByClient if this call is the result of client action (e.g. calling cancel)
-     */
-    void stopAuthentication(IBinder token, boolean initiatedByClient) {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
-            return;
-        }
-        final ClientMonitor client = mAuthClient;
-        if (client == null || client.token != token) return;
-        if (initiatedByClient) {
-            try {
-                int result = daemon.cancelAuthentication();
-                if (result != 0) {
-                    Slog.w(TAG, "stopAuthentication failed, result=" + result);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "stopAuthentication failed", e);
-            }
-            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
-        }
-        removeClient(mAuthClient);
-    }
-
-    void startRemove(IBinder token, int fingerId, int userId,
+    void startRemove(IBinder token, int fingerId, int userId, int groupId,
             IFingerprintServiceReceiver receiver, boolean restricted) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon == null) {
             Slog.w(TAG, "startRemove: no fingeprintd!");
             return;
         }
-
-        stopPendingOperations(true);
-        mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
-        mUserIdForRemove = mCurrentUserId;
-        // The fingerprint template ids will be removed when we get confirmation from the HAL
-        try {
-            final int result = daemon.remove(fingerId, userId);
-            if (result != 0) {
-                Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
-                handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+        RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
+                receiver, userId, groupId, fingerId, restricted, token.toString()) {
+            @Override
+            public void notifyUserActivity() {
+                FingerprintService.this.userActivity();
             }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "startRemove failed", e);
-        }
+
+            @Override
+            public IFingerprintDaemon getFingerprintDaemon() {
+                FingerprintService.this.getFingerprintDaemon();
+                return null;
+            }
+        };
+        startClient(client, true);
     }
 
     public List<Fingerprint> getEnrolledFingerprints(int userId) {
@@ -541,10 +416,8 @@
         UserManager um = UserManager.get(mContext);
 
         // Allow current user or profiles of the current user...
-        List<UserInfo> profiles = um.getEnabledProfiles(userId);
-        final int n = profiles.size();
-        for (int i = 0; i < n; i++) {
-            if (profiles.get(i).id == userId) {
+        for (int profileId : um.getEnabledProfileIds(userId)) {
+            if (profileId == userId) {
                 return true;
             }
         }
@@ -574,10 +447,9 @@
      * @param foregroundOnly only allow this call while app is in the foreground
      * @return true if caller can use fingerprint API
      */
-    private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly) {
+    private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly, int uid,
+            int pid) {
         checkPermission(USE_FINGERPRINT);
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
         if (isKeyguard(opPackageName)) {
             return true; // Keyguard is always allowed
         }
@@ -622,165 +494,83 @@
         }
     }
 
-    private class ClientMonitor implements IBinder.DeathRecipient {
-        IBinder token;
-        IFingerprintServiceReceiver receiver;
-        int userId;
-        boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
-        String owner;
+    private void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
+                IFingerprintServiceReceiver receiver, int flags, boolean restricted,
+                String opPackageName) {
+        updateActiveGroup(groupId, opPackageName);
 
-        public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
-                boolean restricted, String owner) {
-            this.token = token;
-            this.receiver = receiver;
-            this.userId = userId;
-            this.restricted = restricted;
-            this.owner = owner; // name of the client that owns this - for debugging
-            try {
-                token.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
-            }
-        }
+        if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
 
-        public void destroy() {
-            if (token != null) {
-                try {
-                    token.unlinkToDeath(this, 0);
-                } catch (NoSuchElementException e) {
-                    // TODO: remove when duplicate call bug is found
-                    Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+        AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
+                receiver, realUserId, groupId, opId, restricted, opPackageName) {
+            @Override
+            public boolean handleFailedAttempt() {
+                mFailedAttempts++;
+                if (inLockoutMode()) {
+                    // Failing multiple times will continue to push out the lockout time.
+                    scheduleLockoutReset();
+                    return true;
                 }
-                token = null;
+                return false;
             }
-            receiver = null;
-        }
 
-        @Override
-        public void binderDied() {
-            token = null;
-            removeClient(this);
-            receiver = null;
-        }
+            @Override
+            public void resetFailedAttempts() {
+                FingerprintService.this.resetFailedAttempts();
+            }
 
-        @Override
-        protected void finalize() throws Throwable {
-            try {
-                if (token != null) {
-                    if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
-                    removeClient(this);
-                }
-            } finally {
-                super.finalize();
+            @Override
+            public void notifyUserActivity() {
+                FingerprintService.this.userActivity();
             }
-        }
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendRemoved(int fingerId, int groupId) {
-            if (receiver == null) return true; // client not listening
-            try {
-                receiver.onRemoved(mHalDeviceId, fingerId, groupId);
-                return fingerId == 0;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to notify Removed:", e);
+            @Override
+            public IFingerprintDaemon getFingerprintDaemon() {
+                return FingerprintService.this.getFingerprintDaemon();
             }
-            return false;
-        }
+        };
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
-            if (receiver == null) return true; // client not listening
-            FingerprintUtils.vibrateFingerprintSuccess(getContext());
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_FINGERPRINT_ENROLL);
-            try {
-                receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
-                return remaining == 0;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to notify EnrollResult:", e);
-                return true;
+        if (inLockoutMode()) {
+            Slog.v(TAG, "In lockout mode; disallowing authentication");
+            // Don't bother starting the client. Just send the error message.
+            if (!client.onError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
+                Slog.w(TAG, "Cannot send timeout message to client");
             }
+            return;
         }
+        startClient(client, true /* initiatedByClient */);
+    }
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendAuthenticated(int fpId, int groupId) {
-            boolean result = false;
-            boolean authenticated = fpId != 0;
-            if (receiver != null) {
-                try {
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_FINGERPRINT_AUTH,
-                            authenticated);
-                    if (!authenticated) {
-                        receiver.onAuthenticationFailed(mHalDeviceId);
-                    } else {
-                        if (DEBUG) {
-                            Slog.v(TAG, "onAuthenticated(owner=" + mAuthClient.owner
-                                    + ", id=" + fpId + ", gp=" + groupId + ")");
-                        }
-                        Fingerprint fp = !restricted ?
-                                new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
-                        receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
-                    }
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to notify Authenticated:", e);
-                    result = true; // client failed
-                }
-            } else {
-                result = true; // client not listening
-	    }
-	    if (fpId == 0) {
-                if (receiver != null) {
-                    FingerprintUtils.vibrateFingerprintError(getContext());
-                }
-                result |= handleFailedAttempt(this);
-            } else {
-                if (receiver != null) {
-                    FingerprintUtils.vibrateFingerprintSuccess(getContext());
-                }
-                result |= true; // we have a valid fingerprint
-                resetFailedAttempts();
-            }
-            return result;
-        }
+    private void startEnrollment(IBinder token, byte [] cryptoToken, int userId, int groupId,
+            IFingerprintServiceReceiver receiver, int flags, boolean restricted,
+            String opPackageName) {
+        updateActiveGroup(groupId, opPackageName);
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendAcquired(int acquiredInfo) {
-            if (receiver == null) return true; // client not listening
-            try {
-                receiver.onAcquired(mHalDeviceId, acquiredInfo);
-                return false; // acquisition continues...
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to invoke sendAcquired:", e);
-                return true; // client failed
-            }
-            finally {
-                // Good scans will keep the device awake
-                if (acquiredInfo == FINGERPRINT_ACQUIRED_GOOD) {
-                    userActivity();
-                }
-            }
-        }
+        EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
+                userId, groupId, cryptoToken, restricted, opPackageName) {
 
-        /*
-         * @return true if we're done.
-         */
-        private boolean sendError(int error) {
-            if (receiver != null) {
-                try {
-                    receiver.onError(mHalDeviceId, error);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to invoke sendError:", e);
-                }
+            @Override
+            public IFingerprintDaemon getFingerprintDaemon() {
+                return FingerprintService.this.getFingerprintDaemon();
             }
-            return true; // errors always terminate progress
+
+            @Override
+            public void notifyUserActivity() {
+                FingerprintService.this.userActivity();
+            }
+        };
+        startClient(client, true /* initiatedByClient */);
+    }
+
+    protected void resetFailedAttempts() {
+        if (DEBUG && inLockoutMode()) {
+            Slog.v(TAG, "Reset fingerprint lockout");
         }
+        mFailedAttempts = 0;
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+        // the alarm might still be pending; remove it.
+        cancelLockoutReset();
+        notifyLockoutResetMonitors();
     }
 
     private class FingerprintServiceLockoutResetMonitor {
@@ -878,8 +668,6 @@
     };
 
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
-        private static final String KEYGUARD_PACKAGE = "com.android.systemui";
-
         @Override // Binder call
         public long preEnroll(IBinder token) {
             checkPermission(MANAGE_FINGERPRINT);
@@ -894,7 +682,8 @@
 
         @Override // Binder call
         public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
-                final IFingerprintServiceReceiver receiver, final int flags) {
+                final IFingerprintServiceReceiver receiver, final int flags,
+                final String opPackageName) {
             checkPermission(MANAGE_FINGERPRINT);
             final int limit =  mContext.getResources().getInteger(
                     com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
@@ -905,7 +694,6 @@
                 Slog.w(TAG, "Too many fingerprints registered");
                 return;
             }
-            final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
 
             // Group ID is arbitrarily set to parent profile user ID. It just represents
             // the default fingerprints for the user.
@@ -917,7 +705,8 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startEnrollment(token, cryptoClone, groupId, receiver, flags, restricted);
+                    startEnrollment(token, cryptoToken, userId, groupId, receiver, flags,
+                            restricted, opPackageName);
                 }
             });
         }
@@ -934,7 +723,10 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopEnrollment(token, true);
+                    ClientMonitor client = mCurrentClient;
+                    if (client instanceof EnrollClient && client.getToken() == token) {
+                        client.stop(client.getToken() == token);
+                    }
                 }
             });
         }
@@ -943,17 +735,18 @@
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName) {
-            if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) {
-                if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
-                return;
-            }
             final int realUserId = Binder.getCallingUid();
-
+            final int pid = Binder.getCallingPid();
             final boolean restricted = isRestricted();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
+                    if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
+                            realUserId, pid)) {
+                        if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
+                        return;
+                    }
                     startAuthentication(token, opId, realUserId, groupId, receiver,
                             flags, restricted, opPackageName);
                 }
@@ -961,14 +754,29 @@
         }
 
         @Override // Binder call
-        public void cancelAuthentication(final IBinder token, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
-                return;
-            }
+        public void cancelAuthentication(final IBinder token, final String opPackageName) {
+            final int uid = Binder.getCallingUid();
+            final int pid = Binder.getCallingPid();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopAuthentication(token, true);
+                    if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, uid, pid)) {
+                        if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
+                    } else {
+                        ClientMonitor client = mCurrentClient;
+                        if (client instanceof AuthenticationClient) {
+                            if (client.getToken() == token) {
+                                if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
+                                client.stop(client.getToken() == token);
+                            } else {
+                                if (DEBUG) Slog.v(TAG, "can't stop client "
+                                        + client.getOwnerString() + " since tokens don't match");
+                            }
+                        } else if (client != null) {
+                            if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
+                                    + client.getOwnerString());
+                        }
+                    }
                 }
             });
         }
@@ -989,10 +797,11 @@
                 final IFingerprintServiceReceiver receiver) {
             checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
             final boolean restricted = isRestricted();
+            final int realUserId = Binder.getCallingUid();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startRemove(token, fingerId, groupId, receiver, restricted);
+                    startRemove(token, fingerId, realUserId, groupId, receiver, restricted);
                 }
             });
 
@@ -1000,7 +809,8 @@
 
         @Override // Binder call
         public boolean isHardwareDetected(long deviceId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid())) {
                 return false;
             }
             return mHalDeviceId != 0;
@@ -1023,7 +833,8 @@
 
         @Override // Binder call
         public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid())) {
                 return Collections.emptyList();
             }
             if (!isCurrentUserOrProfile(userId)) {
@@ -1035,7 +846,8 @@
 
         @Override // Binder call
         public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
-            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */)) {
+            if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                    Binder.getCallingUid(), Binder.getCallingPid())) {
                 return false;
             }
 
@@ -1063,7 +875,7 @@
             // The permission check should be restored once Android Keystore no longer invokes this
             // method from inside app processes.
 
-            return FingerprintService.this.getAuthenticatorId();
+            return FingerprintService.this.getAuthenticatorId(opPackageName);
         }
 
         @Override // Binder call
@@ -1156,6 +968,7 @@
                     }
                     daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
                     mCurrentUserId = userId;
+                    mCurrentAuthenticatorId = daemon.getAuthenticatorId();
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to setActiveGroup():", e);
@@ -1206,14 +1019,12 @@
         }
     }
 
-    public long getAuthenticatorId() {
-        IFingerprintDaemon daemon = getFingerprintDaemon();
-        if (daemon != null) {
-            try {
-                return daemon.getAuthenticatorId();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "getAuthenticatorId failed", e);
-            }
+    public long getAuthenticatorId(String opPackageName) {
+        if (canUseFingerprint(opPackageName, false /* foregroundOnly */,
+                Binder.getCallingUid(), Binder.getCallingPid())) {
+            return mCurrentAuthenticatorId;
+        } else {
+            Slog.w(TAG, "Client isn't current, returning authenticator_id=0");
         }
         return 0;
     }
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
new file mode 100644
index 0000000..69a96e1
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/RemovalClient.java
@@ -0,0 +1,117 @@
+/**
+ * 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.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintDaemon;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+    private int mFingerId;
+    private int mUserIdForRemove;
+
+    public RemovalClient(Context context, long halDeviceId, IBinder token,
+            IFingerprintServiceReceiver receiver, int userId, int groupId, int fingerId,
+            boolean restricted, String owner) {
+        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
+        mFingerId = fingerId;
+        mUserIdForRemove = userId;
+    }
+
+    @Override
+    public int start() {
+        IFingerprintDaemon daemon = getFingerprintDaemon();
+        // The fingerprint template ids will be removed when we get confirmation from the HAL
+        try {
+            final int result = daemon.remove(mFingerId, getUserId());
+            if (result != 0) {
+                Slog.w(TAG, "startRemove with id = " + mFingerId + " failed, result=" + result);
+                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "startRemove failed", e);
+        }
+        return 0;
+    }
+
+    @Override
+    public int stop(boolean initiatedByClient) {
+        // We don't actually stop remove, but inform the client that the cancel operation succeeded
+        // so we can start the next operation.
+        if (initiatedByClient) {
+            onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
+        return 0;
+    }
+
+    /*
+     * @return true if we're done.
+     */
+    private boolean sendRemoved(int fingerId, int groupId) {
+        IFingerprintServiceReceiver receiver = getReceiver();
+        if (receiver == null)
+            return true; // client not listening
+        try {
+            receiver.onRemoved(getHalDeviceId(), fingerId, groupId);
+            return fingerId == 0;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to notify Removed:", e);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onRemoved(int fingerId, int groupId) {
+        if (fingerId != 0) {
+            if (fingerId != mFingerId)
+            FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
+                    mUserIdForRemove);
+        } else {
+            mUserIdForRemove = UserHandle.USER_NULL;
+        }
+        return sendRemoved(fingerId, getGroupId());
+    }
+
+    @Override
+    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
+        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
+        return true; // Invalid for Remove
+    }
+
+    @Override
+    public boolean onAuthenticated(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
+        return true; // Invalid for Remove.
+    }
+
+    @Override
+    public boolean onEnumerationResult(int fingerId, int groupId) {
+        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for remove!");
+        return false; // Invalid for Remove.
+    }
+
+
+}
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/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index d339f69..5e68f8e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -329,6 +329,7 @@
             final String ssid = removeDoubleQuotes(config.SSID);
             if (id.equals(ssid)) {
                 final NetworkPolicy policy = newPolicy(ssid);
+                policy.metered = true;
                 Log.i(TAG, "Creating new policy for " + ssid + ": " + policy);
                 final NetworkPolicy[] newPolicies = new NetworkPolicy[policies.length + 1];
                 System.arraycopy(policies, 0, newPolicies, 0, policies.length);
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 f20d0a1..cabdced 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
@@ -2186,16 +2186,18 @@
 
     // Clears the 'fake' auto-bunding summary.
     private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
-        if (adjustment.getSignals() != null
-                && adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
+        if (adjustment.getSignals() != null) {
+            Bundle.setDefusable(adjustment.getSignals(), true);
+            if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
                 && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
-            if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
-                // Clear summary.
-                final NotificationRecord removed = mNotificationsByKey.get(
-                        mAutobundledSummaries.remove(adjustment.getPackage()));
-                if (removed != null) {
-                    mNotificationList.remove(removed);
-                    cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
+                if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
+                    // Clear summary.
+                    final NotificationRecord removed = mNotificationsByKey.get(
+                            mAutobundledSummaries.remove(adjustment.getPackage()));
+                    if (removed != null) {
+                        mNotificationList.remove(removed);
+                        cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
+                    }
                 }
             }
         }
@@ -2203,47 +2205,50 @@
 
     // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
     private void maybeAddAutobundleSummary(Adjustment adjustment) {
-        if (adjustment.getSignals() != null
-                && adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
-            final String newAutoBundleKey =
-                    adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
-            int userId = -1;
-            NotificationRecord summaryRecord = null;
-            synchronized (mNotificationList) {
-                if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
-                        && newAutoBundleKey != null) {
-                    // Add summary
-                    final StatusBarNotification adjustedSbn
-                            = mNotificationsByKey.get(adjustment.getKey()).sbn;
+        if (adjustment.getSignals() != null) {
+            Bundle.setDefusable(adjustment.getSignals(), true);
+            if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
+                final String newAutoBundleKey =
+                        adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
+                int userId = -1;
+                NotificationRecord summaryRecord = null;
+                synchronized (mNotificationList) {
+                    if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
+                            && newAutoBundleKey != null) {
+                        // Add summary
+                        final StatusBarNotification adjustedSbn
+                                = mNotificationsByKey.get(adjustment.getKey()).sbn;
 
-                    final ApplicationInfo appInfo =
-                            adjustedSbn.getNotification().extras.getParcelable(
-                                    Notification.EXTRA_BUILDER_APPLICATION_INFO);
-                    final Bundle extras = new Bundle();
-                    extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
-                    final Notification summaryNotification =
-                            new Notification.Builder(getContext()).setSmallIcon(
-                                    adjustedSbn.getNotification().getSmallIcon())
-                                    .setGroupSummary(true)
-                                    .setGroup(newAutoBundleKey)
-                                    .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
-                                    .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
-                                    .build();
-                    summaryNotification.extras.putAll(extras);
-                    final StatusBarNotification summarySbn =
-                            new StatusBarNotification(adjustedSbn.getPackageName(),
-                                    adjustedSbn.getOpPkg(),
-                                    Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
-                                    adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
-                                    summaryNotification, adjustedSbn.getUser(), newAutoBundleKey,
-                                    System.currentTimeMillis());
-                    summaryRecord = new NotificationRecord(getContext(), summarySbn);
-                    mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
-                    userId = adjustedSbn.getUser().getIdentifier();
+                        final ApplicationInfo appInfo =
+                                adjustedSbn.getNotification().extras.getParcelable(
+                                        Notification.EXTRA_BUILDER_APPLICATION_INFO);
+                        final Bundle extras = new Bundle();
+                        extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
+                        final Notification summaryNotification =
+                                new Notification.Builder(getContext()).setSmallIcon(
+                                        adjustedSbn.getNotification().getSmallIcon())
+                                        .setGroupSummary(true)
+                                        .setGroup(newAutoBundleKey)
+                                        .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
+                                        .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+                                        .build();
+                        summaryNotification.extras.putAll(extras);
+                        final StatusBarNotification summarySbn =
+                                new StatusBarNotification(adjustedSbn.getPackageName(),
+                                        adjustedSbn.getOpPkg(),
+                                        Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
+                                        adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
+                                        summaryNotification, adjustedSbn.getUser(),
+                                        newAutoBundleKey,
+                                        System.currentTimeMillis());
+                        summaryRecord = new NotificationRecord(getContext(), summarySbn);
+                        mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
+                        userId = adjustedSbn.getUser().getIdentifier();
+                    }
                 }
-            }
-            if (summaryRecord != null) {
-                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
+                if (summaryRecord != null) {
+                    mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
+                }
             }
         }
     }
@@ -2459,10 +2464,10 @@
 
         // Fix the notification as best we can.
         try {
-            Notification.addFieldsFromContext(getContext().createApplicationContext(
-                    getContext().getPackageManager().getApplicationInfoAsUser(
-                            pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId),
-                    Context.CONTEXT_RESTRICTED), notification);
+            final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
+                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
+            Notification.addFieldsFromContext(ai, userId, notification);
         } catch (NameNotFoundException e) {
             Slog.e(TAG, "Cannot create a context for sending app", e);
             return;
@@ -2543,18 +2548,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 +2562,6 @@
                             enqueueStatus);
                 }
 
-                if (ignoreNotification) {
-                    return;
-                }
-
                 mRankingHelper.extractSignals(r);
 
                 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
@@ -2681,58 +2677,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 +3795,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 +3813,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 +3845,6 @@
             final INotificationListener listener = (INotificationListener) info.service;
             final NotificationRankingUpdate update;
             synchronized (mNotificationList) {
-                updateNotificationGroupsDesiredLocked();
                 update = makeRankingUpdateLocked(info);
             }
             try {
@@ -3919,7 +3861,6 @@
                 updateEffectsSuppressorLocked();
             }
             mLightTrimListeners.remove(removed);
-            updateNotificationGroupsDesiredLocked();
         }
 
         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
@@ -4112,31 +4053,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/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a89a422..f29970c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -176,10 +176,7 @@
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
         mCreationTimeMs = previous.mCreationTimeMs;
         mVisibleSinceMs = previous.mVisibleSinceMs;
-        mUserImportance = previous.mUserImportance;
-        mImportance = previous.mImportance;
-        mImportanceExplanation = previous.mImportanceExplanation;
-        // Don't copy mGlobalSortKey, recompute it.
+        // Don't copy importance information or mGlobalSortKey, recompute them.
     }
 
     public Notification getNotification() { return sbn.getNotification(); }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index eae2eaa..f6255af 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+
 import android.app.AlarmManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
@@ -23,6 +25,9 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
 import android.os.ServiceManager;
 import android.util.ArraySet;
 import android.util.Log;
@@ -38,7 +43,9 @@
 
     static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
 
-    static final int BACKGROUND_DEXOPT_JOB = 800;
+    static final int JOB_IDLE_OPTIMIZE = 800;
+    static final int JOB_POST_BOOT_UPDATE = 801;
+
     private static ComponentName sDexoptServiceName = new ComponentName(
             "android",
             BackgroundDexOptService.class.getName());
@@ -48,66 +55,193 @@
      */
     static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
 
-    final AtomicBoolean mIdleTime = new AtomicBoolean(false);
+    /**
+     * Atomics set to true if the JobScheduler requests an abort.
+     */
+    final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
+    final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
+
+    /**
+     * Atomic set to true if one job should exit early because another job was started.
+     */
+    final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
 
     public static void schedule(Context context) {
         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
-                .setRequiresDeviceIdle(true)
-                .setRequiresCharging(true)
-                .setPeriodic(TimeUnit.DAYS.toMillis(1))
-                .build();
-        js.schedule(job);
+
+        // Schedule a one-off job which scans installed packages and updates
+        // out-of-date oat files.
+        js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+                    .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
+                    .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
+                    .build());
+
+        // Schedule a daily job which scans installed packages and compiles
+        // those with fresh profiling data.
+        js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
+                    .setRequiresDeviceIdle(true)
+                    .setRequiresCharging(true)
+                    .setPeriodic(TimeUnit.DAYS.toMillis(1))
+                    .build());
+
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "Jobs scheduled");
+        }
     }
 
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        Log.i(TAG, "onStartJob");
-        final PackageManagerService pm =
-                (PackageManagerService)ServiceManager.getService("package");
-
-        if (pm.isStorageLow()) {
-            Log.i(TAG, "Low storage, skipping this run");
-            return false;
+    public static void notifyPackageChanged(String packageName) {
+        // The idle maintanance job skips packages which previously failed to
+        // compile. The given package has changed and may successfully compile
+        // now. Remove it from the list of known failing packages.
+        synchronized (sFailedPackageNames) {
+            sFailedPackageNames.remove(packageName);
         }
-        final ArraySet<String> pkgs = pm.getOptimizablePackages();
-        if (pkgs == null || pkgs.isEmpty()) {
-            Log.i(TAG, "No packages to optimize");
+    }
+
+    // Returns the current battery level as a 0-100 integer.
+    private int getBatteryLevel() {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        Intent intent = registerReceiver(null, filter);
+        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+        if (level < 0 || scale <= 0) {
+            // Battery data unavailable. This should never happen, so assume the worst.
+            return 0;
+        }
+
+        return (100 * level / scale);
+    }
+
+    private boolean runPostBootUpdate(final JobParameters jobParams,
+            final PackageManagerService pm, final ArraySet<String> pkgs) {
+        if (mExitPostBootUpdate.get()) {
+            // This job has already been superseded. Do not start it.
             return false;
         }
 
-        final JobParameters jobParams = params;
-        mIdleTime.set(true);
-        new Thread("BackgroundDexOptService_DexOpter") {
+        // Load low battery threshold from the system config. This is a 0-100 integer.
+        final int lowBatteryThreshold = getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+
+        mAbortPostBootUpdate.set(false);
+        new Thread("BackgroundDexOptService_PostBootUpdate") {
             @Override
             public void run() {
                 for (String pkg : pkgs) {
-                    if (!mIdleTime.get()) {
-                        // Out of the idle state. Stop the compilation.
+                    if (mAbortPostBootUpdate.get()) {
+                        // JobScheduler requested an early abort.
+                        return;
+                    }
+                    if (mExitPostBootUpdate.get()) {
+                        // Different job, which supersedes this one, is running.
+                        break;
+                    }
+                    if (getBatteryLevel() < lowBatteryThreshold) {
+                        // Rather bail than completely drain the battery.
+                        break;
+                    }
+                    if (DEBUG_DEXOPT) {
+                        Log.i(TAG, "Updating package " + pkg);
+                    }
+                    // Update package if needed. Note that there can be no race between concurrent
+                    // jobs because PackageDexOptimizer.performDexOpt is synchronized.
+                    pm.performDexOpt(pkg,
+                            /* instruction set */ null,
+                            /* checkProfiles */ false,
+                            PackageManagerService.REASON_BOOT,
+                            /* force */ false);
+                }
+                // Ran to completion, so we abandon our timeslice and do not reschedule.
+                jobFinished(jobParams, /* reschedule */ false);
+            }
+        }.start();
+        return true;
+    }
+
+    private boolean runIdleOptimization(final JobParameters jobParams,
+            final PackageManagerService pm, final ArraySet<String> pkgs) {
+        // If post-boot update is still running, request that it exits early.
+        mExitPostBootUpdate.set(true);
+
+        mAbortIdleOptimization.set(false);
+        new Thread("BackgroundDexOptService_IdleOptimization") {
+            @Override
+            public void run() {
+                for (String pkg : pkgs) {
+                    if (mAbortIdleOptimization.get()) {
+                        // JobScheduler requested an early abort.
                         return;
                     }
                     if (sFailedPackageNames.contains(pkg)) {
-                        // skip previously failing package
+                        // Skip previously failing package
                         continue;
                     }
-                    if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
-                            PackageManagerService.REASON_BACKGROUND_DEXOPT, /* force */ false)) {
-                        // there was a problem running dexopt,
-                        // remember this so we do not keep retrying.
+                    // Conservatively add package to the list of failing ones in case performDexOpt
+                    // never returns.
+                    synchronized (sFailedPackageNames) {
                         sFailedPackageNames.add(pkg);
                     }
+                    // Optimize package if needed. Note that there can be no race between
+                    // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
+                    if (pm.performDexOpt(pkg,
+                            /* instruction set */ null,
+                            /* checkProfiles */ true,
+                            PackageManagerService.REASON_BACKGROUND_DEXOPT,
+                            /* force */ false)) {
+                        // Dexopt succeeded, remove package from the list of failing ones.
+                        synchronized (sFailedPackageNames) {
+                            sFailedPackageNames.remove(pkg);
+                        }
+                    }
                 }
-                // ran to completion, so we abandon our timeslice and do not reschedule
-                jobFinished(jobParams, false);
+                // Ran to completion, so we abandon our timeslice and do not reschedule.
+                jobFinished(jobParams, /* reschedule */ false);
             }
         }.start();
         return true;
     }
 
     @Override
+    public boolean onStartJob(JobParameters params) {
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "onStartJob");
+        }
+
+        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+        if (pm.isStorageLow()) {
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "Low storage, skipping this run");
+            }
+            return false;
+        }
+
+        final ArraySet<String> pkgs = pm.getOptimizablePackages();
+        if (pkgs == null || pkgs.isEmpty()) {
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "No packages to optimize");
+            }
+            return false;
+        }
+
+        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+            return runPostBootUpdate(params, pm, pkgs);
+        } else {
+            return runIdleOptimization(params, pm, pkgs);
+        }
+    }
+
+    @Override
     public boolean onStopJob(JobParameters params) {
-        Log.i(TAG, "onIdleStop");
-        mIdleTime.set(false);
+        if (DEBUG_DEXOPT) {
+            Log.i(TAG, "onStopJob");
+        }
+
+        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+            mAbortPostBootUpdate.set(true);
+        } else {
+            mAbortIdleOptimization.set(true);
+        }
         return false;
     }
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 4c18e15..43a0b91 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -147,6 +147,7 @@
         @Override
         public void addOnAppsChangedListener(String callingPackage, IOnAppsChangedListener listener)
                 throws RemoteException {
+            verifyCallingPackage(callingPackage);
             synchronized (mListeners) {
                 if (DEBUG) {
                     Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
@@ -213,8 +214,11 @@
          * Checks if the caller is in the same group as the userToCheck.
          */
         private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+            ensureInUserProfiles(userToCheck.getIdentifier(), message);
+        }
+
+        private void ensureInUserProfiles(int targetUserId, String message) {
             final int callingUserId = injectCallingUserId();
-            final int targetUserId = userToCheck.getIdentifier();
 
             if (targetUserId == callingUserId) return;
 
@@ -253,9 +257,13 @@
          * Checks if the user is enabled.
          */
         private boolean isUserEnabled(UserHandle user) {
+            return isUserEnabled(user.getIdentifier());
+        }
+
+        private boolean isUserEnabled(int userId) {
             long ident = injectClearCallingIdentity();
             try {
-                UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
+                UserInfo targetUserInfo = mUm.getUserInfo(userId);
                 return targetUserInfo != null && targetUserInfo.isEnabled();
             } finally {
                 injectRestoreCallingIdentity(ident);
@@ -286,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)) {
@@ -295,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);
             }
@@ -346,8 +354,12 @@
         }
 
         private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) {
+            ensureShortcutPermission(callingPackage, user.getIdentifier());
+        }
+
+        private void ensureShortcutPermission(@NonNull String callingPackage, int userId) {
             verifyCallingPackage(callingPackage);
-            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+            ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
 
             if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
                     callingPackage)) {
@@ -357,32 +369,24 @@
 
         @Override
         public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
-                String packageName, ComponentName componentName, int flags, UserHandle user) {
+                String packageName, List shortcutIds, ComponentName componentName, int flags,
+                UserHandle user) {
             ensureShortcutPermission(callingPackage, user);
             if (!isUserEnabled(user)) {
                 return new ParceledListSlice<>(new ArrayList(0));
             }
+            if (shortcutIds != null && packageName == null) {
+                throw new IllegalArgumentException(
+                        "To query by shortcut ID, package name must also be set");
+            }
 
             return new ParceledListSlice<>(
                     mShortcutServiceInternal.getShortcuts(getCallingUserId(),
-                            callingPackage, changedSince, packageName,
+                            callingPackage, changedSince, packageName, shortcutIds,
                             componentName, flags, user.getIdentifier()));
         }
 
         @Override
-        public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
-                List<String> ids, UserHandle user) {
-            ensureShortcutPermission(callingPackage, user);
-            if (!isUserEnabled(user)) {
-                return new ParceledListSlice<>(new ArrayList(0));
-            }
-
-            return new ParceledListSlice<>(
-                    mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
-                            callingPackage, packageName, ids, user.getIdentifier()));
-        }
-
-        @Override
         public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
                 UserHandle user) {
             ensureShortcutPermission(callingPackage, user);
@@ -396,27 +400,27 @@
         }
 
         @Override
-        public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
-                UserHandle user) {
-            ensureShortcutPermission(callingPackage, user);
-            if (!isUserEnabled(user)) {
+        public int getShortcutIconResId(String callingPackage, String packageName, String id,
+                int userId) {
+            ensureShortcutPermission(callingPackage, userId);
+            if (!isUserEnabled(userId)) {
                 return 0;
             }
 
             return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
-                    callingPackage, shortcut, user.getIdentifier());
+                    callingPackage, packageName, id, userId);
         }
 
         @Override
-        public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
-                UserHandle user) {
-            ensureShortcutPermission(callingPackage, user);
-            if (!isUserEnabled(user)) {
+        public ParcelFileDescriptor getShortcutIconFd(String callingPackage,
+                String packageName, String id, int userId) {
+            ensureShortcutPermission(callingPackage, userId);
+            if (!isUserEnabled(userId)) {
                 return null;
             }
 
             return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
-                    callingPackage, shortcut, user.getIdentifier());
+                    callingPackage, packageName, id, userId);
         }
 
         @Override
@@ -428,23 +432,23 @@
 
         @Override
         public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
-                Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
+                Rect sourceBounds, Bundle startActivityOptions, int userId) {
             verifyCallingPackage(callingPackage);
-            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+            ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
 
-            if (!isUserEnabled(user)) {
+            if (!isUserEnabled(userId)) {
                 throw new IllegalStateException("Cannot start a shortcut for disabled profile "
-                        + user);
+                        + userId);
             }
 
             // Even without the permission, pinned shortcuts are always launchable.
             if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
-                    callingPackage, packageName, shortcutId, user.getIdentifier())) {
-                ensureShortcutPermission(callingPackage, user);
+                    callingPackage, packageName, shortcutId, userId)) {
+                ensureShortcutPermission(callingPackage, userId);
             }
 
             final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
-                    callingPackage, packageName, shortcutId, user.getIdentifier());
+                    callingPackage, packageName, shortcutId, userId);
             if (intent == null) {
                 return false;
             }
@@ -455,7 +459,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                mContext.startActivityAsUser(intent, startActivityOptions, user);
+                mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -768,7 +772,8 @@
                     final List<ShortcutInfo> list =
                             mShortcutServiceInternal.getShortcuts(launcherUserId,
                                     cookie.packageName,
-                                    /* changedSince= */ 0, packageName, /* component= */ null,
+                                    /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
+                                    /* component= */ null,
                                     ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
                                     | ShortcutQuery.FLAG_GET_PINNED
                                     | ShortcutQuery.FLAG_GET_DYNAMIC
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index d13f472..b3ac05c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -53,7 +53,6 @@
     // TODO b/19550105 Remove error codes and use exceptions
     static final int DEX_OPT_SKIPPED = 0;
     static final int DEX_OPT_PERFORMED = 1;
-    static final int DEX_OPT_DEFERRED = 2;
     static final int DEX_OPT_FAILED = -1;
 
     private final Installer mInstaller;
@@ -170,6 +169,8 @@
         final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
 
         boolean performedDexOpt = false;
+        boolean successfulDexOpt = true;
+
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
         for (String dexCodeInstructionSet : dexCodeInstructionSets) {
             for (String path : paths) {
@@ -226,15 +227,20 @@
                     performedDexOpt = true;
                 } catch (InstallerException e) {
                     Slog.w(TAG, "Failed to dexopt", e);
+                    successfulDexOpt = false;
                 }
             }
         }
 
-        // If we've gotten here, we're sure that no error occurred and that we haven't
-        // deferred dex-opt. We've either dex-opted one more paths or instruction sets or
-        // we've skipped all of them because they are up to date. In both cases this
-        // package doesn't need dexopt any longer.
-        return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+        if (successfulDexOpt) {
+            // If we've gotten here, we're sure that no error occurred. We've either
+            // dex-opted one or more paths or instruction sets or we've skipped
+            // all of them because they are up to date. In both cases this package
+            // doesn't need dexopt any longer.
+            return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+        } else {
+            return DEX_OPT_FAILED;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ddccbb3..1fb260d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -229,6 +229,7 @@
 import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
+import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -2051,6 +2052,10 @@
         PackageManagerService m = new PackageManagerService(context, installer,
                 factoryTest, onlyCore);
         m.enableSystemUserPackages();
+        // Disable any carrier apps. We do this very early in boot to prevent the apps from being
+        // disabled after already being started.
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m,
+                UserHandle.USER_SYSTEM);
         ServiceManager.addService("package", m);
         return m;
     }
@@ -4716,27 +4721,39 @@
     @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags, int userId) {
-        if (!sUserManager.exists(userId)) return null;
-        flags = updateFlagsForResolve(flags, userId, intent);
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                false /* requireFullPermission */, false /* checkShell */, "resolve intent");
-        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
-                userId);
-        final ResolveInfo bestChoice =
-                chooseBestActivity(intent, resolvedType, flags, query, userId);
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
 
-        if (isEphemeralAllowed(intent, query, userId)) {
-            final EphemeralResolveInfo ai =
-                    getEphemeralResolveInfo(intent, resolvedType, userId);
-            if (ai != null) {
-                if (DEBUG_EPHEMERAL) {
-                    Slog.v(TAG, "Returning an EphemeralResolveInfo");
+            if (!sUserManager.exists(userId)) return null;
+            flags = updateFlagsForResolve(flags, userId, intent);
+            enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                    false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
+
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+            final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
+                    flags, userId);
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+            final ResolveInfo bestChoice =
+                    chooseBestActivity(intent, resolvedType, flags, query, userId);
+
+            if (isEphemeralAllowed(intent, query, userId)) {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+                final EphemeralResolveInfo ai =
+                        getEphemeralResolveInfo(intent, resolvedType, userId);
+                if (ai != null) {
+                    if (DEBUG_EPHEMERAL) {
+                        Slog.v(TAG, "Returning an EphemeralResolveInfo");
+                    }
+                    bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
+                    bestChoice.ephemeralResolveInfo = ai;
                 }
-                bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
-                bestChoice.ephemeralResolveInfo = ai;
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
+            return bestChoice;
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
-        return bestChoice;
     }
 
     @Override
@@ -4905,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) {
@@ -5183,8 +5201,14 @@
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
             String resolvedType, int flags, int userId) {
-        return new ParceledListSlice<>(
-                queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+        try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
+
+            return new ParceledListSlice<>(
+                    queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
     }
 
     private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
@@ -7061,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) {
@@ -7076,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);
             }
@@ -7147,6 +7161,8 @@
         }
     }
 
+    // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
+    // if the package can now be considered up to date for the given filter.
     private boolean performDexOptInternal(String packageName, String instructionSet,
                 boolean checkProfiles, String targetCompilerFilter, boolean force) {
         PackageParser.Package p;
@@ -7167,7 +7183,7 @@
                 final String[] instructionSets = new String[] { targetInstructionSet };
                 int result = performDexOptInternalWithDependenciesLI(p, instructionSets,
                         checkProfiles, targetCompilerFilter, force);
-                return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
+                return result != PackageDexOptimizer.DEX_OPT_FAILED;
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -13646,6 +13662,16 @@
                 }
             }
 
+            // Check for shared user id changes
+            String invalidPackageName =
+                    getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+            if (invalidPackageName != null) {
+                res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                        "Package " + invalidPackageName + " tried to change user "
+                                + oldPackage.mSharedUserId);
+                return;
+            }
+
             // In case of rollback, remember per-user/profile install state
             allUsers = sUserManager.getUserIds();
         }
@@ -13895,15 +13921,6 @@
             setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
                     System.currentTimeMillis());
 
-            // Check for shared user id changes
-            String invalidPackageName = getParentOrChildPackageChangedSharedUser(
-                    deletedPackage, newPackage);
-            if (invalidPackageName != null) {
-                res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
-                        "Forbidding shared user change from " + deletedPkgSetting.sharedUser
-                                + " to " + invalidPackageName);
-            }
-
             // Update the package dynamic state if succeeded
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                 // Now that the install succeeded make sure we remove data
@@ -14514,7 +14531,6 @@
                 return;
             }
 
-
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
             // Do not run PackageDexOptimizer through the local performDexOpt
             // method because `pkg` is not in `mPackages` yet.
@@ -14522,10 +14538,15 @@
                     false /* checkProfiles */, getCompilerFilterForReason(REASON_INSTALL));
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
-                String msg = "Extracking package failed for " + pkgName;
+                String msg = "Extracting package failed for " + pkgName;
                 res.setError(INSTALL_FAILED_DEXOPT, msg);
                 return;
             }
+
+            // Notify BackgroundDexOptService that the package has been changed.
+            // If this is an update of a package which used to fail to compile,
+            // BDOS will remove it from its blacklist.
+            BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
         }
 
         if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -16002,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);
@@ -16068,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) {
@@ -17032,8 +17067,13 @@
         }
         PackageSetting pkgSetting;
         final int uid = Binder.getCallingUid();
-        final int permission = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+        final int permission;
+        if (uid == Process.SYSTEM_UID) {
+            permission = PackageManager.PERMISSION_GRANTED;
+        } else {
+            permission = mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
+        }
         enforceCrossUserPermission(uid, userId,
                 false /* requireFullPermission */, true /* checkShell */, "set enabled");
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
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 1076a7a..b9b65eb 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
@@ -27,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;
@@ -36,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;
 
@@ -50,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";
@@ -66,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.
      */
@@ -304,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++;
@@ -484,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());
@@ -522,7 +540,7 @@
                         ret.getPackageInfo().loadFromXml(parser, fromBackup);
                         continue;
                     case TAG_SHORTCUT:
-                        final ShortcutInfo si = parseShortcut(parser, packageName);
+                        final ShortcutInfo si = parseShortcut(parser, packageName, ownerUserId);
 
                         // Don't use addShortcut(), we don't need to save the icon.
                         ret.mShortcuts.put(si.getId(), si);
@@ -534,8 +552,8 @@
         return ret;
     }
 
-    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
-            throws IOException, XmlPullParserException {
+    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
+            @UserIdInt int userId) throws IOException, XmlPullParserException {
         String id;
         ComponentName activityComponent;
         // Icon icon;
@@ -549,6 +567,7 @@
         int flags;
         int iconRes;
         String bitmapPath;
+        String[] categories = null;
 
         id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
         activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -582,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(
-                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 5c1e7a8..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.
@@ -419,26 +419,26 @@
             result = false;
         }
 
-        mSaveDelayMillis = (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
-                DEFAULT_SAVE_DELAY_MS);
+        mSaveDelayMillis = Math.max(0, (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
+                DEFAULT_SAVE_DELAY_MS));
 
-        mResetInterval = parser.getLong(
+        mResetInterval = Math.max(1, parser.getLong(
                 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
-                * 1000L;
+                * 1000L);
 
-        mMaxDailyUpdates = (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 = (int) parser.getLong(
-                ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
+        mMaxDynamicShortcuts = Math.max(0, (int) parser.getLong(
+                ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP));
 
-        final int iconDimensionDp = injectIsLowRamDevice()
+        final int iconDimensionDp = Math.max(1, injectIsLowRamDevice()
                 ? (int) parser.getLong(
                     ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
                     DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
                 : (int) parser.getLong(
                     ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
-                    DEFAULT_MAX_ICON_DIMENSION_DP);
+                    DEFAULT_MAX_ICON_DIMENSION_DP));
 
         mMaxIconDimension = injectDipToPixel(iconDimensionDp);
 
@@ -968,7 +968,8 @@
                 return; // has no icon
             }
 
-            Bitmap bitmap = null;
+            Bitmap bitmap;
+            Bitmap bitmapToRecycle = null;
             try {
                 switch (icon.getType()) {
                     case Icon.TYPE_RESOURCE: {
@@ -979,7 +980,7 @@
                         return;
                     }
                     case Icon.TYPE_BITMAP: {
-                        bitmap = icon.getBitmap();
+                        bitmap = icon.getBitmap(); // Don't recycle in this case.
                         break;
                     }
                     case Icon.TYPE_URI: {
@@ -987,7 +988,8 @@
 
                         try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
 
-                            bitmap = BitmapFactory.decodeStream(is);
+                            bitmapToRecycle = BitmapFactory.decodeStream(is);
+                            bitmap = bitmapToRecycle;
 
                         } catch (IOException e) {
                             Slog.e(TAG, "Unable to load icon from " + uri);
@@ -1011,8 +1013,14 @@
                     try {
                         path = out.getFile();
 
-                        shrinkBitmap(bitmap, mMaxIconDimension)
-                                .compress(mIconPersistFormat, mIconPersistQuality, out);
+                        Bitmap shrunk = shrinkBitmap(bitmap, mMaxIconDimension);
+                        try {
+                            shrunk.compress(mIconPersistFormat, mIconPersistQuality, out);
+                        } finally {
+                            if (bitmap != shrunk) {
+                                shrunk.recycle();
+                            }
+                        }
 
                         shortcut.setBitmapPath(out.getFile().getAbsolutePath());
                         shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
@@ -1027,8 +1035,8 @@
                     }
                 }
             } finally {
-                if (bitmap != null) {
-                    bitmap.recycle();
+                if (bitmapToRecycle != null) {
+                    bitmapToRecycle.recycle();
                 }
                 // Once saved, we won't use the original icon information, so null it out.
                 shortcut.clearIcon();
@@ -1076,8 +1084,6 @@
 
         c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
 
-        in.recycle();
-
         return scaledBitmap;
     }
 
@@ -1122,7 +1128,7 @@
         if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
             return; // Caller is valid.
         }
-        throw new SecurityException("Caller UID= doesn't own " + packageName);
+        throw new SecurityException("Calling package name mismatch");
     }
 
     void postToHandler(Runnable r) {
@@ -1313,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);
 
@@ -1324,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);
 
@@ -1337,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) {
@@ -1403,7 +1418,7 @@
         verifyCaller(packageName, userId);
 
         synchronized (mLock) {
-            return mMaxDailyUpdates
+            return mMaxUpdatesPerInterval
                     - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
         }
     }
@@ -1419,6 +1434,8 @@
 
     @Override
     public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
+        verifyCaller(packageName, userId);
+
         synchronized (mLock) {
             return mMaxIconDimension;
         }
@@ -1439,7 +1456,15 @@
             getUserShortcutsLocked(userId).resetThrottling();
         }
         scheduleSaveUser(userId);
-        Slog.i(TAG, "ShortcutManager: throttling counter reset");
+        Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
+    }
+
+    void resetAllThrottlingInner() {
+        synchronized (mLock) {
+            mRawLastResetTime = injectCurrentTimeMillis();
+        }
+        scheduleSaveBaseState();
+        Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
     }
 
     // We override this method in unit tests to do a simpler check.
@@ -1522,14 +1547,20 @@
 
     // === House keeping ===
 
+    @VisibleForTesting
+    void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
+        cleanUpPackageLocked(packageName, owningUserId, packageUserId,
+                /* forceForCommandLine= */ false);
+    }
+
     /**
      * Remove all the information associated with a package.  This will really remove all the
      * information, including the restore information (i.e. it'll remove packages even if they're
      * shadow).
      */
-    @VisibleForTesting
-    void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
-        if (isPackageInstalled(packageName, packageUserId)) {
+    private void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
+            boolean forceForCommandLine) {
+        if (!forceForCommandLine && isPackageInstalled(packageName, packageUserId)) {
             wtf("Package " + packageName + " is still installed for user " + packageUserId);
             return;
         }
@@ -1580,13 +1611,17 @@
         @Override
         public List<ShortcutInfo> getShortcuts(int launcherUserId,
                 @NonNull String callingPackage, long changedSince,
-                @Nullable String packageName, @Nullable ComponentName componentName,
+                @Nullable String packageName, @Nullable List<String> shortcutIds,
+                @Nullable ComponentName componentName,
                 int queryFlags, int userId) {
             final ArrayList<ShortcutInfo> ret = new ArrayList<>();
             final int cloneFlag =
                     ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
                             ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
                             : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+            if (packageName == null) {
+                shortcutIds = null; // LauncherAppsService already threw for it though.
+            }
 
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
@@ -1594,14 +1629,14 @@
 
                 if (packageName != null) {
                     getShortcutsInnerLocked(launcherUserId,
-                            callingPackage, packageName, changedSince,
+                            callingPackage, packageName, shortcutIds, changedSince,
                             componentName, queryFlags, userId, ret, cloneFlag);
                 } else {
                     final ArrayMap<String, ShortcutPackage> packages =
                             getUserShortcutsLocked(userId).getAllPackages();
                     for (int i = packages.size() - 1; i >= 0; i--) {
                         getShortcutsInnerLocked(launcherUserId,
-                                callingPackage, packages.keyAt(i), changedSince,
+                                callingPackage, packages.keyAt(i), shortcutIds, changedSince,
                                 componentName, queryFlags, userId, ret, cloneFlag);
                     }
                 }
@@ -1610,14 +1645,20 @@
         }
 
         private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
-                @Nullable String packageName,long changedSince,
+                @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
                 @Nullable ComponentName componentName, int queryFlags,
                 int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
+            final ArraySet<String> ids = shortcutIds == null ? null
+                    : new ArraySet<>(shortcutIds);
+
             getPackageShortcutsLocked(packageName, userId).findAll(ShortcutService.this, ret,
                     (ShortcutInfo si) -> {
                         if (si.getLastChangedTimestamp() < changedSince) {
                             return false;
                         }
+                        if (ids != null && !ids.contains(si.getId())) {
+                            return false;
+                        }
                         if (componentName != null
                                 && !componentName.equals(si.getActivityComponent())) {
                             return false;
@@ -1633,27 +1674,6 @@
         }
 
         @Override
-        public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
-                @NonNull String callingPackage,
-                @NonNull String packageName, @Nullable List<String> ids, int userId) {
-            // Calling permission must be checked by LauncherAppsImpl.
-            Preconditions.checkStringNotEmpty(packageName, "packageName");
-
-            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
-            final ArraySet<String> idSet = new ArraySet<>(ids);
-            synchronized (mLock) {
-                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
-                        .attemptToRestoreIfNeededAndSave(ShortcutService.this);
-
-                getPackageShortcutsLocked(packageName, userId).findAll(
-                        ShortcutService.this, ret,
-                        (ShortcutInfo si) -> idSet.contains(si.getId()),
-                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
-            }
-            return ret;
-        }
-
-        @Override
         public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
             Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1733,17 +1753,18 @@
         }
 
         @Override
-        public int getShortcutIconResId(int launcherUserId,
-                @NonNull String callingPackage,
-                @NonNull ShortcutInfo shortcut, int userId) {
-            Preconditions.checkNotNull(shortcut, "shortcut");
+        public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
+                @NonNull String packageName, @NonNull String shortcutId, int userId) {
+            Preconditions.checkNotNull(callingPackage, "callingPackage");
+            Preconditions.checkNotNull(packageName, "packageName");
+            Preconditions.checkNotNull(shortcutId, "shortcutId");
 
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
                         .attemptToRestoreIfNeededAndSave(ShortcutService.this);
 
                 final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
-                        shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
+                        packageName, userId).findShortcutById(shortcutId);
                 return (shortcutInfo != null && shortcutInfo.hasIconResource())
                         ? shortcutInfo.getIconResourceId() : 0;
             }
@@ -1751,16 +1772,18 @@
 
         @Override
         public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
-                @NonNull String callingPackage,
-                @NonNull ShortcutInfo shortcutIn, int userId) {
-            Preconditions.checkNotNull(shortcutIn, "shortcut");
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull String shortcutId, int userId) {
+            Preconditions.checkNotNull(callingPackage, "callingPackage");
+            Preconditions.checkNotNull(packageName, "packageName");
+            Preconditions.checkNotNull(shortcutId, "shortcutId");
 
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
                         .attemptToRestoreIfNeededAndSave(ShortcutService.this);
 
                 final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
-                        shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
+                        packageName, userId).findShortcutById(shortcutId);
                 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
                     return null;
                 }
@@ -1865,9 +1888,15 @@
             Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
                     packageUserId));
         }
+        handlePackageRemovedInner(packageName, packageUserId, /* forceForCommandLine =*/ false);
+    }
+
+    private void handlePackageRemovedInner(String packageName, @UserIdInt int packageUserId,
+            boolean forceForCommandLine) {
         synchronized (mLock) {
             forEachLoadedUserLocked(user ->
-                cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
+                cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
+                        forceForCommandLine));
         }
     }
 
@@ -2048,17 +2077,26 @@
             pw.print(formatTime(next));
             pw.println();
 
-            pw.print("  Max icon dim: ");
-            pw.print(mMaxIconDimension);
-            pw.print("  Icon format: ");
-            pw.print(mIconPersistFormat);
-            pw.print("  Icon quality: ");
+            pw.print("  Config:");
+            pw.print("    Max icon dim: ");
+            pw.println(mMaxIconDimension);
+            pw.print("    Icon format: ");
+            pw.println(mIconPersistFormat);
+            pw.print("    Icon quality: ");
             pw.println(mIconPersistQuality);
+            pw.print("    saveDelayMillis:");
+            pw.println(mSaveDelayMillis);
+            pw.print("    resetInterval:");
+            pw.println(mResetInterval);
+            pw.print("    maxUpdatesPerInterval:");
+            pw.println(mMaxUpdatesPerInterval);
+            pw.print("    maxDynamicShortcuts:");
+            pw.println(mMaxDynamicShortcuts);
             pw.println();
 
             pw.println("  Stats:");
             synchronized (mStatLock) {
-                final String p = "     ";
+                final String p = "    ";
                 dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
                 dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
 
@@ -2144,6 +2182,9 @@
                     case "reset-throttling":
                         handleResetThrottling();
                         break;
+                    case "reset-all-throttling":
+                        handleResetAllThrottling();
+                        break;
                     case "override-config":
                         handleOverrideConfig();
                         break;
@@ -2162,6 +2203,9 @@
                     case "unload-user":
                         handleUnloadUser();
                         break;
+                    case "clear-shortcuts":
+                        handleClearShortcuts();
+                        break;
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -2181,9 +2225,12 @@
             pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
             pw.println("    Reset throttling for a package");
             pw.println();
-            pw.println("cmd shortcut reset-throttling");
+            pw.println("cmd shortcut reset-throttling [--user USER_ID]");
             pw.println("    Reset throttling for all packages and users");
             pw.println();
+            pw.println("cmd shortcut reset-all-throttling");
+            pw.println("    Reset the throttling state for all users");
+            pw.println();
             pw.println("cmd shortcut override-config CONFIG");
             pw.println("    Override the configuration for testing (will last until reboot)");
             pw.println();
@@ -2203,13 +2250,23 @@
             pw.println("    Unload a user from the memory");
             pw.println("    (This should not affect any observable behavior)");
             pw.println();
+            pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
+            pw.println("    Remove all shortcuts from a package, including pinned shortcuts");
+            pw.println();
         }
 
-        private int handleResetThrottling() throws CommandException {
+        private void handleResetThrottling() throws CommandException {
             parseOptions(/* takeUser =*/ true);
 
+            Slog.i(TAG, "cmd: handleResetThrottling");
+
             resetThrottlingInner(mUserId);
-            return 0;
+        }
+
+        private void handleResetAllThrottling() {
+            Slog.i(TAG, "cmd: handleResetAllThrottling");
+
+            resetAllThrottlingInner();
         }
 
         private void handleResetPackageThrottling() throws CommandException {
@@ -2217,6 +2274,8 @@
 
             final String packageName = getNextArgRequired();
 
+            Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
+
             synchronized (mLock) {
                 getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
                 saveUserLocked(mUserId);
@@ -2226,6 +2285,8 @@
         private void handleOverrideConfig() throws CommandException {
             final String config = getNextArgRequired();
 
+            Slog.i(TAG, "cmd: handleOverrideConfig: " + config);
+
             synchronized (mLock) {
                 if (!updateConfigurationLocked(config)) {
                     throw new CommandException("override-config failed.  See logcat for details.");
@@ -2234,6 +2295,8 @@
         }
 
         private void handleResetConfig() {
+            Slog.i(TAG, "cmd: handleResetConfig");
+
             synchronized (mLock) {
                 loadConfigurationLocked();
             }
@@ -2278,8 +2341,20 @@
         private void handleUnloadUser() throws CommandException {
             parseOptions(/* takeUser =*/ true);
 
+            Slog.i(TAG, "cmd: handleUnloadUser: " + mUserId);
+
             ShortcutService.this.handleCleanupUser(mUserId);
         }
+
+        private void handleClearShortcuts() throws CommandException {
+            parseOptions(/* takeUser =*/ true);
+            final String packageName = getNextArgRequired();
+
+            Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
+
+            ShortcutService.this.handlePackageRemovedInner(packageName, mUserId,
+                    /* forceForCommandLine= */ true);
+        }
     }
 
     // === Unit test support ===
@@ -2357,8 +2432,8 @@
     }
 
     @VisibleForTesting
-    int getMaxDailyUpdatesForTest() {
-        return mMaxDailyUpdates;
+    int getMaxUpdatesPerIntervalForTest() {
+        return mMaxUpdatesPerInterval;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 60a0d62..60ea254 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -65,6 +65,7 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.util.AtomicFile;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -483,13 +484,50 @@
         }
     }
 
+    @Override
+    public int[] getProfileIds(int userId, boolean enabledOnly) {
+        if (userId != UserHandle.getCallingUserId()) {
+            checkManageUsersPermission("getting profiles related to user " + userId);
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mUsersLock) {
+                return getProfileIdsLU(userId, enabledOnly).toArray();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     /** Assume permissions already checked and caller's identity cleared */
     private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly, boolean fullInfo) {
+        IntArray profileIds = getProfileIdsLU(userId, enabledOnly);
+        ArrayList<UserInfo> users = new ArrayList<>(profileIds.size());
+        for (int i = 0; i < profileIds.size(); i++) {
+            int profileId = profileIds.get(i);
+            UserInfo userInfo = mUsers.get(profileId).info;
+            // If full info is not required - clear PII data to prevent 3P apps from reading it
+            if (!fullInfo) {
+                userInfo = new UserInfo(userInfo);
+                userInfo.name = null;
+                userInfo.iconPath = null;
+            } else {
+                userInfo = userWithName(userInfo);
+            }
+            users.add(userInfo);
+        }
+        return users;
+    }
+
+    /**
+     *  Assume permissions already checked and caller's identity cleared
+     */
+    private IntArray getProfileIdsLU(int userId, boolean enabledOnly) {
         UserInfo user = getUserInfoLU(userId);
-        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+        IntArray result = new IntArray(mUsers.size());
         if (user == null) {
             // Probably a dying user
-            return users;
+            return result;
         }
         final int userSize = mUsers.size();
         for (int i = 0; i < userSize; i++) {
@@ -506,16 +544,9 @@
             if (profile.partial) {
                 continue;
             }
-            UserInfo userInfo = userWithName(profile);
-            // If full info is not required - clear PII data to prevent 3P apps from reading it
-            if (!fullInfo) {
-                userInfo = new UserInfo(userInfo);
-                userInfo.name = null;
-                userInfo.iconPath = null;
-            }
-            users.add(userInfo);
+            result.add(profile.id);
         }
-        return users;
+        return result;
     }
 
     @Override
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..8f259db 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(
@@ -3393,7 +3396,9 @@
             IShortcutService shortcutService = mShortcutKeyServices.get(shortcutCode);
             if (shortcutService != null) {
                 try {
-                    shortcutService.notifyShortcutKeyPressed(shortcutCode);
+                    if (isUserSetupComplete()) {
+                        shortcutService.notifyShortcutKeyPressed(shortcutCode);
+                    }
                 } catch (RemoteException e) {
                     mShortcutKeyServices.delete(shortcutCode);
                 }
@@ -4749,7 +4754,7 @@
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
         if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw()
-                && !win.getGivenInsetsPendingLw()) {
+                && win.isDisplayedLw() && !win.getGivenInsetsPendingLw()) {
             setLastInputMethodWindowLw(null, null);
             offsetInputMethodWindowLw(win);
         }
@@ -5780,9 +5785,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 +6705,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;
@@ -7180,10 +7190,6 @@
             tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
         }
 
-        if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
-            tmpVisibility |= StatusBarManager.DISABLE_RECENT;
-        }
-
         final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
                 mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
         final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index a14c614..ab404db 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -27,7 +27,9 @@
 import android.util.SparseArray;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+
 import com.android.internal.util.XmlUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 9614417..95923fe 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -26,7 +26,7 @@
     void buzzBeepBlinked();
     void notificationLightPulse(int argb, int onMillis, int offMillis);
     void notificationLightOff();
-    void showScreenPinningRequest();
+    void showScreenPinningRequest(int taskId);
     void showAssistDisclosure();
     void startAssist(Bundle args);
     void onCameraLaunchGestureDetected(int source);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index dbbaa5e..e71bdb8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -153,10 +153,10 @@
         }
 
         @Override
-        public void showScreenPinningRequest() {
+        public void showScreenPinningRequest(int taskId) {
             if (mBar != null) {
                 try {
-                    mBar.showScreenPinningRequest();
+                    mBar.showScreenPinningRequest(taskId);
                 } catch (RemoteException e) {
                 }
             }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9b96154..728e244 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -981,7 +981,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/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index eb926c1..30194bf 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -213,18 +212,7 @@
         if (userManager == null) {
             return null;
         }
-        int currentUserId = ActivityManager.getCurrentUser();
-        List<UserInfo> profiles = userManager.getProfiles(currentUserId);
-        if (profiles == null) {
-            return null;
-        }
-        final int s = profiles.size();
-        int[] userIds = new int[s];
-        int ctr = 0;
-        for (UserInfo info : profiles) {
-            userIds[ctr++] = info.id;
-        }
-        return userIds;
+        return userManager.getProfileIdsWithDisabled(ActivityManager.getCurrentUser());
     }
 
     public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
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 fb3c6ec..91b6914 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,9 +44,9 @@
 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;
@@ -54,13 +57,13 @@
 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;
@@ -78,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();
 
     /**
@@ -265,70 +297,134 @@
      */
     private void generateCrop(WallpaperData wallpaper) {
         boolean success = false;
-        boolean needCrop = false;
-        boolean needScale = false;
+
+        Rect cropHint = new Rect(wallpaper.cropHint);
 
         if (DEBUG) {
             Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
                     + Integer.toHexString(wallpaper.whichPending)
-                    + " to " + wallpaper.cropFile.getName());
+                    + " to " + wallpaper.cropFile.getName()
+                    + " crop=(" + cropHint.width() + 'x' + cropHint.height()
+                    + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
         }
 
         // Analyse the source; needed in multiple cases
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inJustDecodeBounds = true;
         BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
-
-        // We'll need to scale if the crop is sufficiently bigger than the display
-
-        // Legacy case uses an empty crop rect here, so we just preserve the
-        // source image verbatim
-        if (!wallpaper.cropHint.isEmpty()) {
-            // ...clamp the crop rect to the measured bounds...
-            wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth);
-            wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight);
-            // ...and don't bother cropping if what we're left with is identity
-            needCrop = (options.outHeight >= wallpaper.cropHint.height()
-                    && options.outWidth >= wallpaper.cropHint.width());
-        }
-
-        if (!needCrop && !needScale) {
-            // Simple case:  the nominal crop is at least as big as the source image,
-            // so we take the whole thing and just copy the image file directly.
-            if (DEBUG) {
-                Slog.v(TAG, "Null crop of new wallpaper; copying");
-            }
-            success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
-            if (!success) {
-                wallpaper.cropFile.delete();
-                // TODO: fall back to default wallpaper in this case
-            }
+        if (options.outWidth <= 0 || options.outHeight <= 0) {
+            Slog.e(TAG, "Invalid wallpaper data");
+            success = false;
         } else {
-            // Fancy case: crop and/or scale
-            FileOutputStream f = null;
-            BufferedOutputStream bos = null;
-            try {
-                BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
-                        wallpaper.wallpaperFile.getAbsolutePath(), false);
-                Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null);
-                decoder.recycle();
+            boolean needCrop = false;
+            boolean needScale = false;
 
-                if (cropped == null) {
-                    Slog.e(TAG, "Could not decode new wallpaper");
-                } else {
-                    f = new FileOutputStream(wallpaper.cropFile);
-                    bos = new BufferedOutputStream(f, 32*1024);
-                    cropped.compress(Bitmap.CompressFormat.PNG, 90, bos);
-                    bos.flush();  // don't rely on the implicit flush-at-close when noting success
-                    success = true;
-                }
-            } catch (IOException e) {
+            // Empty crop means use the full image
+            if (cropHint.isEmpty()) {
+                cropHint.left = cropHint.top = 0;
+                cropHint.right = options.outWidth;
+                cropHint.bottom = options.outHeight;
+            } else {
+                // force the crop rect to lie within the measured bounds
+                cropHint.offset(
+                        (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
+                        (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
+
+                // Don't bother cropping if what we're left with is identity
+                needCrop = (options.outHeight >= cropHint.height()
+                        && options.outWidth >= cropHint.width());
+            }
+
+            // scale if the crop height winds up not matching the recommended metrics
+            needScale = (wallpaper.height != cropHint.height());
+
+            if (DEBUG) {
+                Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
+                Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+                Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
+                Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
+            }
+
+            if (!needCrop && !needScale) {
+                // Simple case:  the nominal crop fits what we want, so we take
+                // the whole thing and just copy the image file directly.
                 if (DEBUG) {
-                    Slog.e(TAG, "I/O error decoding crop: " + e.getMessage());
+                    Slog.v(TAG, "Null crop of new wallpaper; copying");
                 }
-            } finally {
-                IoUtils.closeQuietly(bos);
-                IoUtils.closeQuietly(f);
+                success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+                if (!success) {
+                    wallpaper.cropFile.delete();
+                    // TODO: fall back to default wallpaper in this case
+                }
+            } else {
+                // Fancy case: crop and scale.  First, we decode and scale down if appropriate.
+                FileOutputStream f = null;
+                BufferedOutputStream bos = null;
+                try {
+                    BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
+                            wallpaper.wallpaperFile.getAbsolutePath(), false);
+
+                    // This actually downsamples only by powers of two, but that's okay; we do
+                    // a proper scaling blit later.  This is to minimize transient RAM use.
+                    // We calculate the largest power-of-two under the actual ratio rather than
+                    // just let the decode take care of it because we also want to remap where the
+                    // cropHint rectangle lies in the decoded [super]rect.
+                    final BitmapFactory.Options scaler;
+                    final int actualScale = cropHint.height() / wallpaper.height;
+                    int scale = 1;
+                    while (2*scale < actualScale) {
+                        scale *= 2;
+                    }
+                    if (scale > 1) {
+                        scaler = new BitmapFactory.Options();
+                        scaler.inSampleSize = scale;
+                        if (DEBUG) {
+                            Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
+                        }
+                    } else {
+                        scaler = null;
+                    }
+                    Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
+                    decoder.recycle();
+
+                    if (cropped == null) {
+                        Slog.e(TAG, "Could not decode new wallpaper");
+                    } else {
+                        // We've got the extracted crop; now we want to scale it properly to
+                        // the desired rectangle.  That's a height-biased operation: make it
+                        // fit the hinted height, and accept whatever width we end up with.
+                        cropHint.offsetTo(0, 0);
+                        cropHint.right /= scale;    // adjust by downsampling factor
+                        cropHint.bottom /= scale;
+                        final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+                        if (DEBUG) {
+                            Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
+                        }
+                        final int destWidth = (int)(cropHint.width() * heightR);
+                        final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
+                                destWidth, wallpaper.height, true);
+                        if (DEBUG) {
+                            Slog.v(TAG, "Final extract:");
+                            Slog.v(TAG, "  dims: w=" + wallpaper.width
+                                    + " h=" + wallpaper.height);
+                            Slog.v(TAG, "   out: w=" + finalCrop.getWidth()
+                                    + " h=" + finalCrop.getHeight());
+                        }
+
+                        f = new FileOutputStream(wallpaper.cropFile);
+                        bos = new BufferedOutputStream(f, 32*1024);
+                        finalCrop.compress(Bitmap.CompressFormat.PNG, 90, bos);
+                        bos.flush();  // don't rely on the implicit flush-at-close when noting success
+                        success = true;
+                    }
+                } catch (Exception e) {
+                    if (DEBUG) {
+                        Slog.e(TAG, "Error decoding crop", e);
+                    }
+                } finally {
+                    IoUtils.closeQuietly(bos);
+                    IoUtils.closeQuietly(f);
+                }
             }
         }
 
@@ -352,6 +448,7 @@
     final AppOpsManager mAppOpsManager;
     WallpaperData mLastWallpaper;
     IWallpaperManagerCallback mKeyguardListener;
+    boolean mWaitingForUnlock;
 
     /**
      * ID of the current wallpaper, changed every time anything sets a wallpaper.
@@ -679,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);
@@ -690,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;
                 }
@@ -728,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
@@ -753,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);
@@ -763,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));
@@ -827,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;
 
@@ -854,18 +958,35 @@
 
     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);
+                    ensureSaneWallpaperData(fallback);
+                    bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
+                    mWaitingForUnlock = true;
+                }
             }
-            Slog.w(TAG, "Failure starting previous wallpaper", e);
-            clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
         }
     }
 
@@ -1653,12 +1774,14 @@
                     wallpaper = new WallpaperData(userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
                     mLockWallpaperMap.put(userId, wallpaper);
+                    ensureSaneWallpaperData(wallpaper);
                 } else {
                     // sanity fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
                     mWallpaperMap.put(userId, wallpaper);
+                    ensureSaneWallpaperData(wallpaper);
                 }
             }
         }
@@ -1751,6 +1874,8 @@
             wallpaper.cropHint.set(0, 0, 0, 0);
             wallpaper.padding.set(0, 0, 0, 0);
             wallpaper.name = "";
+
+            mLockWallpaperMap.remove(userId);
         } else {
             if (wallpaper.wallpaperId <= 0) {
                 wallpaper.wallpaperId = makeWallpaperIdLocked();
@@ -1761,6 +1886,14 @@
             }
         }
 
+        ensureSaneWallpaperData(wallpaper);
+        WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
+        if (lockWallpaper != null) {
+            ensureSaneWallpaperData(lockWallpaper);
+        }
+    }
+
+    private void ensureSaneWallpaperData(WallpaperData wallpaper) {
         // We always want to have some reasonable width hint.
         int baseSize = getMaximumSizeDimension();
         if (wallpaper.width < baseSize) {
@@ -1952,11 +2085,11 @@
         }
 
         synchronized (mLock) {
-            pw.println("Current Wallpaper Service state:");
+            pw.println("System wallpaper state:");
             for (int i = 0; i < mWallpaperMap.size(); i++) {
                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
-                pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
                 pw.print("  mWidth=");
                     pw.print(wallpaper.width);
                     pw.print(" mHeight=");
@@ -1984,6 +2117,18 @@
                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
                 }
             }
+            pw.println("Lock wallpaper state:");
+            for (int i = 0; i < mLockWallpaperMap.size(); i++) {
+                WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
+                pw.print(" User "); pw.print(wallpaper.userId);
+                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                pw.print("  mWidth="); pw.print(wallpaper.width);
+                    pw.print(" mHeight="); pw.println(wallpaper.height);
+                pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
+                pw.print("  mPadding="); pw.println(wallpaper.padding);
+                pw.print("  mName=");  pw.println(wallpaper.name);
+            }
+
         }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 9486cfd..361f0d4 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -33,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;
 
@@ -59,14 +59,22 @@
     private static final String TAG_AVAILABILITY = "availableByDefault";
     private static final String TAG_SIGNATURE = "signature";
     private static final String TAG_FALLBACK = "isFallback";
+    private final WebViewProviderInfo[] mWebViewProviderPackages;
 
-    /**
-     * Returns all packages declared in the framework resources as potential WebView providers.
-     * @hide
-     * */
-    @Override
-    public WebViewProviderInfo[] getWebViewPackages() {
+    // Initialization-on-demand holder idiom for getting the WebView provider packages once and
+    // for all in a thread-safe manner.
+    private static class LazyHolder {
+        private static final SystemImpl INSTANCE = new SystemImpl();
+    }
+
+    public static SystemImpl getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    private SystemImpl() {
         int numFallbackPackages = 0;
+        int numAvailableByDefaultPackages = 0;
+        int numAvByDefaultAndNotFallback = 0;
         XmlResourceParser parser = null;
         List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
         try {
@@ -82,12 +90,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(
@@ -99,23 +107,50 @@
                             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++;
+                        if (!currentProvider.isFallback) {
+                            numAvByDefaultAndNotFallback++;
                         }
                     }
                     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();
         }
-        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
+        if (numAvailableByDefaultPackages == 0) {
+            throw new AndroidRuntimeException("There must be at least one WebView package "
+                    + "that is available by default");
+        }
+        if (numAvByDefaultAndNotFallback == 0) {
+            throw new AndroidRuntimeException("There must be at least one WebView package "
+                    + "that is available by default and not a fallback");
+        }
+        mWebViewProviderPackages =
+                webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
+    }
+    /**
+     * Returns all packages declared in the framework resources as potential WebView providers.
+     * @hide
+     * */
+    @Override
+    public WebViewProviderInfo[] getWebViewPackages() {
+        return mWebViewProviderPackages;
     }
 
     public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
@@ -187,7 +222,8 @@
         enablePackageForAllUsers(context, packageName, false);
         try {
             PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-            if (pm.getApplicationInfo(packageName, 0).isUpdatedSystemApp()) {
+            ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
+            if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) {
                 pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
                         public void packageDeleted(String packageName, int returnCode) {
                             enablePackageForAllUsers(context, packageName, false);
@@ -214,8 +250,9 @@
                     enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
                     userId, null);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Tried to disable " + packageName + " for user " + userId + ": " + e);
+        } catch (RemoteException | IllegalArgumentException e) {
+            Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
+                    + " for user " + userId + ": " + e);
         }
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 1f6fb2a..9b971e0 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -55,7 +55,7 @@
 
     public WebViewUpdateService(Context context) {
         super(context);
-        mImpl = new WebViewUpdateServiceImpl(context, new SystemImpl());
+        mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance());
     }
 
     @Override
@@ -242,7 +242,12 @@
                 throw new SecurityException(msg);
             }
 
-            WebViewUpdateService.this.mImpl.enableFallbackLogic(enable);
+            long callingId = Binder.clearCallingIdentity();
+            try {
+                WebViewUpdateService.this.mImpl.enableFallbackLogic(enable);
+            } finally {
+                Binder.restoreCallingIdentity(callingId);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 32b195b..d90d922 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);
     }
@@ -160,6 +162,8 @@
             isFallbackEnabled = isEnabledPackage(
                     mSystemInterface.getPackageInfoForProvider(fallbackProvider));
         } catch (NameNotFoundException e) {
+            // No fallback package installed -> early out.
+            return;
         }
 
         if (existsValidNonFallbackProvider
@@ -241,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.
@@ -279,6 +291,13 @@
             try {
                 synchronized(mLock) {
                     mCurrentWebViewPackage = findPreferredWebViewPackage();
+                    // Don't persist the user-chosen setting across boots if the package being
+                    // chosen is not used (could be disabled or uninstalled) so that the user won't
+                    // be surprised by the device switching to using a certain webview package,
+                    // that was uninstalled/disabled a long time ago, if it is installed/enabled
+                    // again.
+                    mSystemInterface.updateUserSetting(mContext,
+                            mCurrentWebViewPackage.packageName);
                     onWebViewProviderChanged(mCurrentWebViewPackage);
                 }
             } catch (Throwable t) {
@@ -289,38 +308,42 @@
 
         /**
          * Change WebView provider and provider setting and kill packages using the old provider.
-         * Return the new provider (in case we are in the middle of creating relro files this new
-         * provider will not be in use directly, but will when the relros are done).
+         * Return the new provider (in case we are in the middle of creating relro files, or
+         * replacing that provider it will not be in use directly, but will be used when the relros
+         * or the replacement are done).
          */
         public String changeProviderAndSetting(String newProviderName) {
             PackageInfo oldPackage = null;
             PackageInfo newPackage = null;
+            boolean providerChanged = false;
             synchronized(mLock) {
                 oldPackage = mCurrentWebViewPackage;
                 mSystemInterface.updateUserSetting(mContext, newProviderName);
 
                 try {
                     newPackage = findPreferredWebViewPackage();
-                    if (oldPackage != null
-                            && newPackage.packageName.equals(oldPackage.packageName)) {
-                        // If we don't perform the user change, revert the settings change.
-                        mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
-                        return newPackage.packageName;
-                    }
+                    providerChanged = (oldPackage == null)
+                            || !newPackage.packageName.equals(oldPackage.packageName);
                 } catch (WebViewFactory.MissingWebViewPackageException e) {
                     Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
                             "package " + e);
                     // If we don't perform the user change but don't have an installed WebView
                     // package, we will have changed the setting and it will be used when a package
                     // is available.
-                    return newProviderName;
+                    return "";
                 }
-                onWebViewProviderChanged(newPackage);
+                // Perform the provider change if we chose a new provider
+                if (providerChanged) {
+                    onWebViewProviderChanged(newPackage);
+                }
             }
-            // Kill apps using the old provider
-            if (oldPackage != null) {
+            // Kill apps using the old provider only if we changed provider
+            if (providerChanged && oldPackage != null) {
                 mSystemInterface.killPackageDependents(oldPackage.packageName);
             }
+            // Return the new provider, this is not necessarily the one we were asked to switch to
+            // But the persistent setting will now be pointing to the provider we were asked to
+            // switch to anyway
             return newPackage.packageName;
         }
 
@@ -333,7 +356,6 @@
                 mAnyWebViewInstalled = true;
                 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
                     mCurrentWebViewPackage = newPackage;
-                    mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
 
                     // The relro creations might 'finish' (not start at all) before
                     // WebViewFactory.onWebViewProviderChanged which means we might not know the
@@ -382,7 +404,7 @@
         }
 
 
-        private class ProviderAndPackageInfo {
+        private static class ProviderAndPackageInfo {
             public final WebViewProviderInfo provider;
             public final PackageInfo packageInfo;
 
@@ -420,9 +442,12 @@
                 }
             }
 
-            // Could not find any enabled package either, use the most stable provider.
+            // Could not find any enabled package either, use the most stable and default-available
+            // provider.
             for (ProviderAndPackageInfo providerAndPackage : providers) {
-                return providerAndPackage.packageInfo;
+                if (providerAndPackage.provider.availableByDefault) {
+                    return providerAndPackage.packageInfo;
+                }
             }
 
             mAnyWebViewInstalled = false;
@@ -495,8 +520,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/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4ec297e..545b9db 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -499,7 +499,7 @@
                 + " with replacing child windows.");
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             final WindowState w = allAppWindows.get(i);
-            if (w.isChildWindow()) {
+            if (w.shouldBeReplacedWithChildren()) {
                 w.setReplacing(false /* animate */);
             }
         }
@@ -539,8 +539,9 @@
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             WindowState candidate = allAppWindows.get(i);
             if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
-                    candidate.getWindowTag().equals(w.getWindowTag().toString())) {
+                    candidate.getWindowTag().toString().equals(w.getWindowTag().toString())) {
                 candidate.mReplacingWindow = w;
+                w.mSkipEnterAnimationForSeamlessReplacement = !candidate.mAnimateReplacingWindow;
 
                 // if we got a replacement window, reset the timeout to give drawing more time
                 service.mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
@@ -575,6 +576,9 @@
                 continue;
             }
             candidate.mWillReplaceWindow = false;
+            if (candidate.mReplacingWindow != null) {
+                candidate.mReplacingWindow.mSkipEnterAnimationForSeamlessReplacement = false;
+            }
             // Since the window already timed out, remove it immediately now.
             // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter
             // delays removal on certain conditions, which will leave the stale window in the
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 3ec02b9..c240d07 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -169,6 +169,10 @@
                 + " dimLayerUser=" + dimLayerUser.toShortString()
                 + " state.continueDimming=" + state.continueDimming
                 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
+        if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
+            return;
+        }
+
         if (!state.continueDimming && state.dimLayer.isDimming()) {
             state.animator = null;
             dimLayerUser.getDimBounds(mTmpBounds);
@@ -303,7 +307,7 @@
         applyDim(dimLayerUser, animator, true /* aboveApp */);
     }
 
-    private void applyDim(
+    void applyDim(
             DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
         if (dimLayerUser == null) {
             Slog.e(TAG, "Trying to apply dim layer for: " + this
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..3bd7a96 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.DOCKED_TOP;
 import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
 import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -38,8 +39,10 @@
 import android.view.SurfaceControl;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import com.android.server.wm.DimLayer.DimLayerUser;
+import com.android.server.wm.WindowManagerService.H;
 
 import java.util.ArrayList;
 
@@ -74,6 +77,13 @@
      */
     private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
 
+    private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
+            new PathInterpolator(0.2f, 0f, 0.1f, 1f);
+
+    private static final long IME_ADJUST_ANIM_DURATION = 280;
+
+    private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
+
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private int mDividerWindowWidth;
@@ -95,11 +105,13 @@
     private float mAnimationStart;
     private float mAnimationTarget;
     private long mAnimationDuration;
+    private boolean mAnimationStartDelayed;
     private final Interpolator mMinimizedDockInterpolator;
     private float mMaximizeMeetFraction;
     private final Rect mTouchRegion = new Rect();
     private boolean mAnimatingForIme;
     private boolean mAdjustedForIme;
+    private WindowState mDelayedImeWin;
 
     DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
@@ -186,10 +198,16 @@
         return mLastVisibility;
     }
 
-    void setAdjustedForIme(boolean adjusted, boolean animate) {
+    void setAdjustedForIme(boolean adjusted, boolean animate, WindowState imeWin) {
         if (mAdjustedForIme != adjusted) {
-            mAnimatingForIme = animate;
             mAdjustedForIme = adjusted;
+            if (animate) {
+                startImeAdjustAnimation(adjusted, imeWin);
+            } else {
+
+                // Animation might be delayed, so only notify if we don't run an animation.
+                notifyAdjustedForImeChanged(adjusted, 0 /* duration */);
+            }
         }
     }
 
@@ -270,12 +288,40 @@
         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 notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
+        final int size = mDockedStackListeners.beginBroadcast();
+        for (int i = 0; i < size; ++i) {
+            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+            try {
+                listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
+            }
+        }
+        mDockedStackListeners.finishBroadcast();
+    }
+
     void registerDockedStackListener(IDockedStackListener listener) {
         mDockedStackListeners.register(listener);
         notifyDockedDividerVisibilityChanged(wasVisible());
         notifyDockedStackExistsChanged(
                 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
         notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
+        notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
+
     }
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
@@ -358,7 +404,7 @@
             return;
         }
 
-        mAnimatingForIme = false;
+        clearImeAdjustAnimation();
         if (minimizedDock) {
             if (animate) {
                 startAdjustAnimation(0f, 1f);
@@ -374,6 +420,17 @@
         }
     }
 
+    private void clearImeAdjustAnimation() {
+        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = stacks.get(i);
+            if (stack != null && stack.isAdjustedForIme()) {
+                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+            }
+        }
+        mAnimatingForIme = false;
+    }
+
     private void startAdjustAnimation(float from, float to) {
         mAnimatingForMinimizedDockedStack = true;
         mAnimationStarted = false;
@@ -381,6 +438,48 @@
         mAnimationTarget = to;
     }
 
+    private void startImeAdjustAnimation(boolean adjusted, WindowState imeWin) {
+        mAnimatingForIme = true;
+        mAnimationStarted = false;
+        mAnimationStart = adjusted ? 0 : 1;
+        mAnimationTarget = adjusted ? 1 : 0;
+
+        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = stacks.get(i);
+            if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
+                stack.beginImeAdjustAnimation();
+            }
+        }
+
+        // We put all tasks into drag resizing mode - wait until all of them have completed the
+        // drag resizing switch.
+        if (!mService.mWaitingForDrawn.isEmpty()) {
+            mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
+            mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
+                    IME_ADJUST_DRAWN_TIMEOUT);
+            mAnimationStartDelayed = true;
+            if (imeWin != null) {
+
+                // There might be an old window delaying the animation start - clear it.
+                if (mDelayedImeWin != null) {
+                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                }
+                mDelayedImeWin = imeWin;
+                imeWin.mWinAnimator.startDelayingAnimationStart();
+            }
+            mService.mWaitingForDrawnCallback = () -> {
+                mAnimationStartDelayed = false;
+                if (mDelayedImeWin != null) {
+                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                }
+                notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION);
+            };
+        } else {
+            notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION);
+        }
+    }
+
     private void setMinimizedDockedStack(boolean minimized) {
         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
         notifyDockedStackMinimizedChanged(minimized, 0);
@@ -400,39 +499,48 @@
         if (mAnimatingForMinimizedDockedStack) {
             return animateForMinimizedDockedStack(now);
         } else if (mAnimatingForIme) {
-            return animateForIme();
+            return animateForIme(now);
         } else {
             return false;
         }
     }
 
-    private boolean animateForIme() {
-        boolean updated = false;
-        boolean animating = false;
-
+    private boolean animateForIme(long now) {
+        if (!mAnimationStarted || mAnimationStartDelayed) {
+            mAnimationStarted = true;
+            mAnimationStartTime = now;
+            mAnimationDuration = (long)
+                    (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
+        }
+        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
+        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
+                .getInterpolation(t);
         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        boolean updated = false;
         for (int i = stacks.size() - 1; i >= 0; --i) {
             final TaskStack stack = stacks.get(i);
             if (stack != null && stack.isAdjustedForIme()) {
-                updated |= stack.updateAdjustForIme();
-                animating |= stack.isAnimatingForIme();
-            }
-        }
-
-        if (updated) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-
-        if (!animating) {
-            mAnimatingForIme = false;
-            for (int i = stacks.size() - 1; i >= 0; --i) {
-                final TaskStack stack = stacks.get(i);
-                if (stack != null) {
-                    stack.clearImeGoingAway();
+                if (t >= 1f && mAnimationTarget == 0f) {
+                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+                    updated = true;
+                } else {
+                    updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t),
+                            false /* force */);
+                }
+                if (t >= 1f) {
+                    stack.endImeAdjustAnimation();
                 }
             }
         }
-        return animating;
+        if (updated) {
+            mService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+        if (t >= 1.0f) {
+            mAnimatingForIme = false;
+            return false;
+        } else {
+            return true;
+        }
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
@@ -465,11 +573,15 @@
         }
     }
 
+    private float getInterpolatedAnimationValue(float t) {
+        return t * mAnimationTarget + (1 - t) * mAnimationStart;
+    }
+
     /**
      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
      */
     private float getMinimizeAmount(TaskStack stack, float t) {
-        final float naturalAmount = t * mAnimationTarget + (1 - t) * mAnimationStart;
+        final float naturalAmount = getInterpolatedAnimationValue(t);
         if (isAnimationMaximizing()) {
             return adjustMaximizeAmount(stack, t, naturalAmount);
         } else {
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/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7b16dbe..46a8dff 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,6 +27,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import android.app.ActivityManager.StackId;
 import android.content.pm.ActivityInfo;
@@ -456,6 +457,11 @@
     /** Bounds of the task to be used for dimming, as well as touch related tests. */
     @Override
     public void getDimBounds(Rect out) {
+        final DisplayContent displayContent = mStack.getDisplayContent();
+        // It doesn't matter if we in particular are part of the resize, since we couldn't have
+        // a DimLayer anyway if we weren't visible.
+        final boolean dockedResizing = displayContent != null ?
+                displayContent.mDividerControllerLocked.isResizing() : false;
         if (useCurrentBounds()) {
             if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
                 return;
@@ -464,8 +470,16 @@
             if (!mFullscreen) {
                 // When minimizing the docked stack when going home, we don't adjust the task bounds
                 // so we need to intersect the task bounds with the stack bounds here.
-                mStack.getBounds(mTmpRect);
-                mTmpRect.intersect(mBounds);
+                //
+                // If we are Docked Resizing with snap points, the task bounds could be smaller than the stack
+                // bounds and so we don't even want to use them. Even if the app should not be resized the Dim
+                // should keep up with the divider.
+                if (dockedResizing) {
+                    mStack.getBounds(out);
+                } else {
+                    mStack.getBounds(mTmpRect);
+                    mTmpRect.intersect(mBounds);
+                }
                 out.set(mTmpRect);
             } else {
                 out.set(mBounds);
@@ -476,7 +490,7 @@
         // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
         // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
         // system.
-        mStack.getDisplayContent().getLogicalDisplayRect(out);
+        displayContent.getLogicalDisplayRect(out);
     }
 
     void setDragResizing(boolean dragResizing, int dragResizeMode) {
@@ -509,6 +523,22 @@
         return mDragResizeMode;
     }
 
+    /**
+     * Adds all of the tasks windows to {@link WindowManagerService#mWaitingForDrawn} if drag
+     * resizing state of the window has been changed.
+     */
+    void addWindowsWaitingForDrawnIfResizingChanged() {
+        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState win = windows.get(winNdx);
+                if (win.isDragResizeChanged()) {
+                    mService.mWaitingForDrawn.add(win);
+                }
+            }
+        }
+    }
+
     void updateDisplayInfo(final DisplayContent displayContent) {
         if (displayContent == null) {
             return;
@@ -561,7 +591,16 @@
 
                     // If we are not drag resizing, force recreating of a new surface so updating
                     // the content and positioning that surface will be in sync.
-                    if (!win.computeDragResizing()) {
+                    //
+                    // As we use this flag as a hint to freeze surface boundary updates,
+                    // we'd like to only apply this to TYPE_BASE_APPLICATION,
+                    // windows of TYPE_APPLICATION like dialogs, could appear
+                    // to not be drag resizing while they resize, but we'd
+                    // still like to manipulate their frame to update crop, etc...
+                    //
+                    // Anyway we don't need to synchronize position and content updates for these
+                    // windows since they aren't at the base layer and could be moved around anyway.
+                    if (!win.computeDragResizing() && win.mAttrs.type == TYPE_BASE_APPLICATION) {
                         win.mResizedWhileNotDragResizing = true;
                     }
                 }
@@ -619,6 +658,16 @@
         return false;
     }
 
+    boolean isVisible() {
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            final AppWindowToken appToken = mAppTokens.get(i);
+            if (appToken.isVisible()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     boolean inHomeStack() {
         return mStack != null && mStack.mStackId == HOME_STACK_ID;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 446b74b..c322cd8 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -16,25 +16,6 @@
 
 package com.android.server.wm;
 
-import android.app.ActivityManager.StackId;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.RemoteException;
-import android.util.EventLog;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.DisplayInfo;
-import android.view.Surface;
-
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.server.EventLogTags;
-
-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;
@@ -45,21 +26,34 @@
 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.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 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;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+
+import android.app.ActivityManager.StackId;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Debug;
+import android.os.RemoteException;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.animation.PathInterpolator;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.server.EventLogTags;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
 
 public class TaskStack implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
 
-    // If the stack should be resized to fullscreen.
-    private static final boolean FULLSCREEN = true;
-
-    // When we have a top-bottom split screen, we shift the bottom stack up to accommodate
-    // the IME window. The static flag below controls whether to run animation when the
-    // IME window goes away.
-    private static final boolean ANIMATE_IME_GOING_AWAY = false;
-
     /** Unique identifier */
     final int mStackId;
 
@@ -83,6 +77,12 @@
     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
     private final Rect mAdjustedBounds = new Rect();
 
+    /**
+     * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
+     * represent the state when the animation has ended.
+     */
+    private final Rect mFullyAdjustedImeBounds = new Rect();
+
     /** Whether mBounds is fullscreen */
     private boolean mFullscreen = true;
 
@@ -118,6 +118,7 @@
     private boolean mImeGoingAway;
     private WindowState mImeWin;
     private float mMinimizeAmount;
+    private float mAdjustImeAmount;
     private final int mDockedStackMinimizeThickness;
 
     // If this is true, the task will be down or upscaled
@@ -211,21 +212,26 @@
      * the normal task bounds.
      *
      * @param bounds The adjusted bounds.
-     * @param keepInsets Whether to keep the insets from the original bounds or to calculate new
-     *                   ones depending on the adjusted bounds.
-     * @return true if the adjusted bounds has changed.
      */
-    private boolean setAdjustedBounds(Rect bounds, boolean keepInsets) {
-        if (mAdjustedBounds.equals(bounds)) {
-            return false;
+    private void setAdjustedBounds(Rect bounds) {
+        if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
+            return;
         }
 
         mAdjustedBounds.set(bounds);
         final boolean adjusted = !mAdjustedBounds.isEmpty();
-        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
-                adjusted && keepInsets ? mBounds : null);
+        Rect insetBounds = null;
+        if (adjusted && isAdjustedForMinimizedDock()) {
+            insetBounds = mBounds;
+        } else if (adjusted && isAdjustedForIme()) {
+            if (mImeGoingAway) {
+                insetBounds = mBounds;
+            } else {
+                insetBounds = mFullyAdjustedImeBounds;
+            }
+        }
+        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
         mDisplayContent.layoutNeeded = true;
-        return true;
     }
 
     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -379,10 +385,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) {
@@ -823,17 +834,18 @@
      * @param imeWin The IME window.
      */
     void setAdjustedForIme(WindowState imeWin) {
-        mAdjustedForIme = true;
         mImeWin = imeWin;
         mImeGoingAway = false;
+        if (!mAdjustedForIme) {
+            mAdjustedForIme = true;
+            mAdjustImeAmount = 0f;
+            updateAdjustForIme(0f, true /* force */);
+        }
     }
 
     boolean isAdjustedForIme() {
         return mAdjustedForIme || mImeGoingAway;
     }
-    void clearImeGoingAway() {
-        mImeGoingAway = false;
-    }
 
     boolean isAnimatingForIme() {
         return mImeWin != null && mImeWin.isAnimatingLw();
@@ -847,16 +859,14 @@
      *
      * @return true if a traversal should be performed after the adjustment.
      */
-    boolean updateAdjustForIme() {
-        boolean stopped = false;
-        if (mImeGoingAway && (!ANIMATE_IME_GOING_AWAY || !isAnimatingForIme())) {
-            mImeWin = null;
-            mAdjustedForIme = false;
-            stopped = true;
+    boolean updateAdjustForIme(float adjustAmount, boolean force) {
+        if (adjustAmount != mAdjustImeAmount || force) {
+            mAdjustImeAmount = adjustAmount;
+            updateAdjustedBounds();
+            return isVisibleForUserLocked();
+        } else {
+            return false;
         }
-        // Make sure to run a traversal when the animation stops so that the stack
-        // is moved to its final position.
-        return updateAdjustedBounds() || stopped;
     }
 
     /**
@@ -870,6 +880,7 @@
             mImeWin = null;
             mAdjustedForIme = false;
             mImeGoingAway = false;
+            mAdjustImeAmount = 0f;
             updateAdjustedBounds();
         } else {
             mImeGoingAway |= mAdjustedForIme;
@@ -896,10 +907,32 @@
         return mMinimizeAmount != 0f;
     }
 
+    /**
+     * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
+     * to the list of to be drawn windows the service is waiting for.
+     */
+    void beginImeAdjustAnimation() {
+        for (int j = mTasks.size() - 1; j >= 0; j--) {
+            final Task task = mTasks.get(j);
+            if (task.isVisibleForUser()) {
+                task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+                task.addWindowsWaitingForDrawnIfResizingChanged();
+            }
+        }
+    }
+
+    /**
+     * Resets the resizing state of all windows.
+     */
+    void endImeAdjustAnimation() {
+        for (int j = mTasks.size() - 1; j >= 0; j--) {
+            mTasks.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+        }
+    }
+
     private boolean adjustForIME(final WindowState imeWin) {
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
-        final Rect adjustedBounds = mTmpAdjustedBounds;
         if (imeWin == null || !dockedTopOrBottom) {
             return false;
         }
@@ -910,43 +943,39 @@
         // 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)
-        if (imeWin.isAnimatingLw()) {
-            imeTop = Math.max(imeTop, imeWin.getShownPositionLw().y);
-        }
         imeTop += imeWin.getGivenContentInsetsLw().top;
         if (contentBounds.bottom > imeTop) {
             contentBounds.bottom = imeTop;
         }
 
-        // If content bounds not changing, nothing to do.
-        if (mLastContentBounds.equals(contentBounds)) {
-            return true;
-        }
-
-        // Content bounds changed, need to apply adjustments depending on dock sides.
         mLastContentBounds.set(contentBounds);
-        adjustedBounds.set(mBounds);
         final int yOffset = displayContentRect.bottom - contentBounds.bottom;
 
         if (dockedSide == DOCKED_TOP) {
             // If this stack is docked on top, we make it smaller so the bottom stack is not
             // occluded by IME. We shift its bottom up by the height of the IME (capped by
             // the display content rect). Note that we don't change the task bounds.
-            adjustedBounds.bottom = Math.max(
-                    adjustedBounds.bottom - yOffset, displayContentRect.top);
+            int bottom = Math.max(
+                    mBounds.bottom - yOffset, displayContentRect.top);
+            mTmpAdjustedBounds.set(mBounds);
+            mTmpAdjustedBounds.bottom =
+                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
+            mFullyAdjustedImeBounds.set(mBounds);
         } else {
             // If this stack is docked on bottom, we shift it up so that it's not occluded by
             // IME. We try to move it up by the height of the IME window (although the best
             // we could do is to make the top stack fully collapsed).
             final int dividerWidth = getDisplayContent().mDividerControllerLocked
                     .getContentWidth();
-            adjustedBounds.top = Math.max(
-                    adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
-            adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
+            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
+            mTmpAdjustedBounds.set(mBounds);
+            mTmpAdjustedBounds.top =
+                    (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
+            mFullyAdjustedImeBounds.set(mBounds);
+            mFullyAdjustedImeBounds.top = top;
+            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
         }
         return true;
     }
@@ -1002,7 +1031,7 @@
     /**
      * Updates the adjustment depending on it's current state.
      */
-    boolean updateAdjustedBounds() {
+    void updateAdjustedBounds() {
         boolean adjust = false;
         if (mMinimizeAmount != 0f) {
             adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
@@ -1013,7 +1042,7 @@
             mTmpAdjustedBounds.setEmpty();
             mLastContentBounds.setEmpty();
         }
-        return setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
+        setAdjustedBounds(mTmpAdjustedBounds);
     }
 
     boolean isAdjustedForMinimizedDockedStack() {
@@ -1025,6 +1054,9 @@
         pw.println(prefix + "mDeferDetach=" + mDeferDetach);
         pw.println(prefix + "mFullscreen=" + mFullscreen);
         pw.println(prefix + "mBounds=" + mBounds.toShortString());
+        if (!mAdjustedBounds.isEmpty()) {
+            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
+        }
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
             mTasks.get(taskNdx).dump(prefix + "  ", pw);
         }
@@ -1153,7 +1185,7 @@
         return mDragResizing;
     }
 
-    private void setDragResizingLocked(boolean resizing) {
+    void setDragResizingLocked(boolean resizing) {
         if (mDragResizing == resizing) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index eae7838..6bc633f 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -670,7 +670,6 @@
         if (SHOW_TRANSACTIONS) Slog.i(
                 TAG, ">>> OPEN TRANSACTION animateLocked");
         SurfaceControl.openTransaction();
-        SurfaceControl.setAnimationTransaction();
         try {
             final int numDisplays = mDisplayContentsAnimators.size();
             for (int i = 0; i < numDisplays; i++) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 57f38d1..a4238c1 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,49 @@
     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 (uri == null) {
+                return;
+            }
+
             if (mDisplayInversionEnabledUri.equals(uri)) {
                 updateCircularDisplayMaskIfNeeded();
+            } else {
+                @UpdateAnimationScaleMode
+                final int mode;
+                if (mWindowAnimationScaleUri.equals(uri)) {
+                    mode = WINDOW_ANIMATION_SCALE;
+                } else if (mTransitionAnimationScaleUri.equals(uri)) {
+                    mode = TRANSITION_ANIMATION_SCALE;
+                } else if (mAnimationDurationScaleUri.equals(uri)) {
+                    mode = ANIMATION_DURATION_SCALE;
+                } else {
+                    // Ignoring unrecognized content changes
+                    return;
+                }
+                Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
+                mH.sendMessage(m);
             }
         }
     }
@@ -2072,6 +2114,11 @@
             if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                 reportNewConfig = true;
             }
+            if (attrs.removeTimeoutMilliseconds > 0) {
+                mH.sendMessageDelayed(
+                        mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win),
+                        attrs.removeTimeoutMilliseconds);
+            }
         }
 
         if (reportNewConfig) {
@@ -2555,7 +2602,7 @@
 
     void repositionChild(Session session, IWindow client,
             int left, int top, int right, int bottom,
-            long deferTransactionUntilFrame, Rect outFrame) {
+            long frameNumber, Rect outFrame) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
         long origId = Binder.clearCallingIdentity();
 
@@ -2591,10 +2638,8 @@
 
                         win.mWinAnimator.setSurfaceBoundariesLocked(false);
 
-                        if (deferTransactionUntilFrame > 0) {
-                            win.mWinAnimator.mSurfaceController.deferTransactionUntil(
-                                    win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
-                                    deferTransactionUntilFrame);
+                        if (frameNumber > 0) {
+                            win.mWinAnimator.deferTransactionUntilParentFrame(frameNumber);
                         }
 
                     } finally {
@@ -3905,16 +3950,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 +3995,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);
@@ -5958,7 +6003,7 @@
             @Override
             public void run() {
                 Bitmap bm = screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1,
-                        true, 1f);
+                        true, 1f, Bitmap.Config.ARGB_8888);
                 try {
                     receiver.send(bm);
                 } catch (RemoteException e) {
@@ -5971,8 +6016,7 @@
 
     /**
      * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
-     * In portrait mode, it grabs the upper region of the screen based on the vertical dimension
-     * of the target image.
+     * In portrait mode, it grabs the full screenshot.
      *
      * @param displayId the Display to take a screenshot of.
      * @param width the width of the target bitmap
@@ -5986,11 +6030,17 @@
                 "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, Bitmap.Config.RGB_565);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
     }
 
     Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
-            boolean includeFullDisplay, float frameScale) {
+            boolean includeFullDisplay, float frameScale, Bitmap.Config config) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
             displayContent = getDisplayContentLocked(displayId);
@@ -6229,7 +6279,7 @@
 
         // Create a copy of the screenshot that is immutable and backed in ashmem.
         // This greatly reduces the overhead of passing the bitmap between processes.
-        Bitmap ret = bm.createAshmemBitmap();
+        Bitmap ret = bm.createAshmemBitmap(config);
         bm.recycle();
         return ret;
     }
@@ -7393,7 +7443,7 @@
         }
     }
 
-    private void adjustForImeIfNeeded(final DisplayContent displayContent) {
+    void adjustForImeIfNeeded(final DisplayContent displayContent) {
         final WindowState imeWin = mInputMethodWindow;
         final TaskStack focusedStack =
                 mCurrentFocus != null ? mCurrentFocus.getStack() : null;
@@ -7409,14 +7459,14 @@
                     stack.setAdjustedForIme(imeWin);
                 }
             }
-            displayContent.mDividerControllerLocked.setAdjustedForIme(true, true);
+            displayContent.mDividerControllerLocked.setAdjustedForIme(true, true, imeWin);
         } else {
             final ArrayList<TaskStack> stacks = displayContent.getStacks();
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 final TaskStack stack = stacks.get(i);
                 stack.resetAdjustedForIme(!dockVisible);
             }
-            displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible);
+            displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible, imeWin);
         }
     }
 
@@ -7747,6 +7797,8 @@
         public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
         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;
+        public static final int WINDOW_REMOVE_TIMEOUT = 52;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -8030,6 +8082,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
@@ -8329,6 +8411,18 @@
                     mAmInternal.notifyStartingWindowDrawn();
                 }
                 break;
+                case WINDOW_REMOVE_TIMEOUT: {
+                    final WindowState window = (WindowState) msg.obj;
+                    synchronized(mWindowMap) {
+                        // It's counterintuitive that we check that "mWindowRemovalAllowed"
+                        // is false. But in fact if it's true, it means a remove has already
+                        // been requested and we better just not do anything.
+                        if (!window.mRemoved && !window.mWindowRemovalAllowed) {
+                            removeWindowLocked(window);
+                        }
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c991130..bf69717 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -82,6 +82,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -444,6 +445,11 @@
     // If not null, the window that will be used to replace the old one. This is being set when
     // the window is added and unset when this window reports its first draw.
     WindowState mReplacingWindow = null;
+    // For the new window in the replacement transition, if we have
+    // requested to replace without animation, then we should
+    // make sure we also don't apply an enter animation for
+    // the new window.
+    boolean mSkipEnterAnimationForSeamlessReplacement = false;
     // Whether this window is being moved via the resize API
     boolean mMovedByResize;
 
@@ -774,10 +780,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 +818,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 +859,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 +1395,11 @@
         return configChanged;
     }
 
+    boolean isAdjustedForMinimizedDock() {
+        return mAppToken != null && mAppToken.mTask != null
+                && mAppToken.mTask.mStack.isAdjustedForMinimizedDock();
+    }
+
     void removeLocked() {
         disposeInputChannel();
 
@@ -1548,7 +1558,7 @@
             // If app died visible, apply a dim over the window to indicate that it's inactive
             mDisplayContent.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
         } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
-                && mDisplayContent != null && !mAnimatingExit && isDisplayedLw()) {
+                && mDisplayContent != null && !mAnimatingExit && isVisibleUnchecked()) {
             mDisplayContent.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
         }
     }
@@ -1567,12 +1577,16 @@
         }
         for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
             final WindowState win = mAppToken.allAppWindows.get(i);
-            if (win.mWillReplaceWindow && win.mReplacingWindow == this) {
+            if (win.mWillReplaceWindow && win.mReplacingWindow == this && hasDrawnLw()) {
                 if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win);
+                if (win.isDimming()) {
+                    win.transferDimToReplacement();
+                }
                 win.mWillReplaceWindow = false;
                 win.mAnimateReplacingWindow = false;
                 win.mReplacingRemoveRequested = false;
                 win.mReplacingWindow = null;
+                mSkipEnterAnimationForSeamlessReplacement = false;
                 if (win.mAnimatingExit) {
                     mService.removeWindowInnerLocked(win);
                 }
@@ -1776,7 +1790,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 +2420,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 +2506,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=");
@@ -2704,4 +2720,25 @@
         }
         return winY;
     }
+
+    void transferDimToReplacement() {
+        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+        if (dimLayerUser != null && mDisplayContent != null) {
+            mDisplayContent.mDimLayerController.applyDim(dimLayerUser,
+                    mReplacingWindow.mWinAnimator,
+                    (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? true : false);
+        }
+    }
+
+    // During activity relaunch due to resize, we sometimes use window replacement
+    // for only child windows (as the main window is handled by window preservation)
+    // and the big surface.
+    //
+    // Though windows of TYPE_APPLICATION (as opposed to TYPE_BASE_APPLICATION)
+    // are not children in the sense of an attached window, we also want to replace
+    // them at such phases, as they won't be covered by window preservation,
+    // and in general we expect them to return following relaunch.
+    boolean shouldBeReplacedWithChildren() {
+        return isChildWindow() || mAttrs.type == TYPE_APPLICATION;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9c25f63..6fcc8f9 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,11 +16,13 @@
 
 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;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -181,6 +183,8 @@
      * window is first added or shown, cleared when the callback has been made. */
     boolean mEnteringAnimation;
 
+    private boolean mAnimationStartDelayed;
+
     boolean mKeyguardGoingAwayAnimation;
     boolean mKeyguardGoingAwayWithWallpaper;
 
@@ -219,6 +223,10 @@
 
     int mAttrType;
 
+    static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
+    long mDeferTransactionUntilFrame = -1;
+    long mDeferTransactionTime = -1;
+
     private final Rect mTmpSize = new Rect();
 
     WindowStateAnimator(final WindowState win) {
@@ -293,7 +301,7 @@
     /** Is the window animating the DummyAnimation? */
     boolean isDummyAnimation() {
         return mAppAnimator != null
-                && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
+                && mAppAnimator.animation == sDummyAnimation;
     }
 
     /** Is this window currently set to animate or currently animating? */
@@ -317,8 +325,12 @@
         if ((mAnimation == null) || !mLocalAnimating) {
             return false;
         }
+        currentTime = getAnimationFrameTime(mAnimation, currentTime);
         mTransformation.clear();
         final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
+        if (mAnimationStartDelayed && mAnimationIsEntrance) {
+            mTransformation.setAlpha(0f);
+        }
         if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more
                 + ", xform=" + mTransformation);
         return more;
@@ -1032,7 +1044,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 +1176,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 +1190,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 +1216,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 +1227,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 +1255,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 +1267,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 +1303,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 +1327,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 +1381,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;
@@ -1661,6 +1684,12 @@
     }
 
     void applyEnterAnimationLocked() {
+        // If we are the new part of a window replacement transition and we have requested
+        // not to animate, we instead want to make it seamless, so we don't want to apply
+        // an enter transition.
+        if (mWin.mSkipEnterAnimationForSeamlessReplacement) {
+            return;
+        }
         final int transit;
         if (mEnterAnimationPending) {
             mEnterAnimationPending = false;
@@ -1743,7 +1772,9 @@
         } else {
             clearAnimation();
         }
-
+        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mService.adjustForImeIfNeeded(mWin.mDisplayContent);
+        }
         return mAnimation != null;
     }
 
@@ -1775,7 +1806,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 +1824,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);
             }
@@ -1822,6 +1853,9 @@
                     pw.print(" mDsDy="); pw.print(mDsDy);
                     pw.print(" mDtDy="); pw.println(mDtDy);
         }
+        if (mAnimationStartDelayed) {
+            pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed);
+        }
     }
 
     @Override
@@ -1868,4 +1902,57 @@
         mAnimDy = mWin.mLastFrame.top - top;
         mAnimateMove = true;
     }
+
+    void deferTransactionUntilParentFrame(long frameNumber) {
+        if (!mWin.isChildWindow()) {
+            return;
+        }
+        mDeferTransactionUntilFrame = frameNumber;
+        mDeferTransactionTime = System.currentTimeMillis();
+        mSurfaceController.deferTransactionUntil(
+                mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                frameNumber);
+    }
+
+    // Defer the current transaction to the frame number of the last saved transaction.
+    // We do this to avoid shooting through an unsynchronized transaction while something is
+    // pending. This is generally fine, as either we will get in on the synchronization,
+    // or SurfaceFlinger will see that the frame has already occured. The only
+    // potential problem is in frame number resets so we reset things with a timeout
+    // every so often to be careful.
+    void deferToPendingTransaction() {
+        if (mDeferTransactionUntilFrame < 0) {
+            return;
+        }
+        long time = System.currentTimeMillis();
+        if (time > mDeferTransactionTime + PENDING_TRANSACTION_FINISH_WAIT_TIME) {
+            mDeferTransactionTime = -1;
+            mDeferTransactionUntilFrame = -1;
+        } else {
+            mSurfaceController.deferTransactionUntil(
+                    mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                    mDeferTransactionUntilFrame);
+        }
+    }
+
+    /**
+     * Sometimes we need to synchronize the first frame of animation with some external event.
+     * To achieve this, we prolong the start of the animation and keep producing the first frame of
+     * the animation.
+     */
+    private long getAnimationFrameTime(Animation animation, long currentTime) {
+        if (mAnimationStartDelayed) {
+            animation.setStartTime(currentTime);
+            return currentTime + 1;
+        }
+        return currentTime;
+    }
+
+    void startDelayingAnimationStart() {
+        mAnimationStartDelayed = true;
+    }
+
+    void endDelayingAnimationStart() {
+        mAnimationStartDelayed = false;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 04aa735..1e6c585 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -721,6 +721,10 @@
 
                 // Moved from updateWindowsAndWallpaperLocked().
                 if (w.mHasSurface) {
+                    // If we have recently synchronized a previous transaction for this
+                    // window ensure we don't push through an unsynchronized one now.
+                    winAnimator.deferToPendingTransaction();
+
                     // Take care of the window being ready to display.
                     final boolean committed = winAnimator.commitFinishDrawingLocked();
                     if (isDefaultDisplay && committed) {
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 72eebb5..d3d05f3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21,8 +21,6 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -57,7 +55,6 @@
 import android.app.backup.IBackupManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -85,7 +82,6 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
-import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -215,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";
@@ -659,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");
@@ -874,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) {
@@ -2065,10 +2061,9 @@
      */
     private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy,
             int userHandle) {
-        List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
-        for (UserInfo ui : profiles) {
-            int id = ui.id;
-            sendAdminCommandLocked(action, reqPolicy, id);
+        int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle);
+        for (int profileId : profileIds) {
+            sendAdminCommandLocked(action, reqPolicy, profileId);
         }
     }
 
@@ -2701,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);
 
@@ -2731,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)
@@ -3968,9 +3966,9 @@
         // moment so we set the screen off timeout regardless of whether it affects the parent user
         // or the profile challenge only.
         long timeMs = Long.MAX_VALUE;
-        List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
-        for (UserInfo userInfo : profiles) {
-            DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+        int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle);
+        for (int profileId : profileIds) {
+            DevicePolicyData policy = getUserDataUnchecked(profileId);
             final int N = policy.mAdminList.size();
             for (int i = 0; i < N; i++) {
                 ActiveAdmin admin = policy.mAdminList.get(i);
@@ -6708,19 +6706,18 @@
             // If we have multiple profiles we return the intersection of the
             // permitted lists. This can happen in cases where we have a device
             // and profile owner.
-            List<UserInfo> profiles = mUserManager.getProfiles(userId);
-            final int PROFILES_SIZE = profiles.size();
-            for (int i = 0; i < PROFILES_SIZE; ++i) {
+            int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId);
+            for (int profileId : profileIds) {
                 // Just loop though all admins, only device or profiles
                 // owners can have permitted lists set.
-                DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id);
+                DevicePolicyData policy = getUserDataUnchecked(profileId);
                 final int N = policy.mAdminList.size();
                 for (int j = 0; j < N; j++) {
                     ActiveAdmin admin = policy.mAdminList.get(j);
                     List<String> fromAdmin = admin.permittedAccessiblityServices;
                     if (fromAdmin != null) {
                         if (result == null) {
-                            result = new ArrayList<String>(fromAdmin);
+                            result = new ArrayList<>(fromAdmin);
                         } else {
                             result.retainAll(fromAdmin);
                         }
@@ -6888,12 +6885,11 @@
             // If we have multiple profiles we return the intersection of the
             // permitted lists. This can happen in cases where we have a device
             // and profile owner.
-            List<UserInfo> profiles = mUserManager.getProfiles(userId);
-            final int PROFILES_SIZE = profiles.size();
-            for (int i = 0; i < PROFILES_SIZE; ++i) {
+            int[] profileIds = mUserManager.getProfileIdsWithDisabled(userId);
+            for (int profileId : profileIds) {
                 // Just loop though all admins, only device or profiles
                 // owners can have permitted lists set.
-                DevicePolicyData policy = getUserDataUnchecked(profiles.get(i).id);
+                DevicePolicyData policy = getUserDataUnchecked(profileId);
                 final int N = policy.mAdminList.size();
                 for (int j = 0; j < N; j++) {
                     ActiveAdmin admin = policy.mAdminList.get(j);
@@ -8508,7 +8504,7 @@
     }
 
     @Override
-    public void setShortSupportMessage(@NonNull ComponentName who, String message) {
+    public void setShortSupportMessage(@NonNull ComponentName who, CharSequence message) {
         if (!mHasFeature) {
             return;
         }
@@ -8525,7 +8521,7 @@
     }
 
     @Override
-    public String getShortSupportMessage(@NonNull ComponentName who) {
+    public CharSequence getShortSupportMessage(@NonNull ComponentName who) {
         if (!mHasFeature) {
             return null;
         }
@@ -8538,7 +8534,7 @@
     }
 
     @Override
-    public void setLongSupportMessage(@NonNull ComponentName who, String message) {
+    public void setLongSupportMessage(@NonNull ComponentName who, CharSequence message) {
         if (!mHasFeature) {
             return;
         }
@@ -8555,7 +8551,7 @@
     }
 
     @Override
-    public String getLongSupportMessage(@NonNull ComponentName who) {
+    public CharSequence getLongSupportMessage(@NonNull ComponentName who) {
         if (!mHasFeature) {
             return null;
         }
@@ -8568,7 +8564,7 @@
     }
 
     @Override
-    public String getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
+    public CharSequence getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
         if (!mHasFeature) {
             return null;
         }
@@ -8586,7 +8582,7 @@
     }
 
     @Override
-    public String getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
+    public CharSequence getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
         if (!mHasFeature) {
             return null;
         }
@@ -8664,7 +8660,7 @@
     }
 
     @Override
-    public void setOrganizationName(@NonNull ComponentName who, String text) {
+    public void setOrganizationName(@NonNull ComponentName who, CharSequence text) {
         if (!mHasFeature) {
             return;
         }
@@ -8675,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;
         }
@@ -8696,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/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 406dd56..804fa03 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -336,17 +336,17 @@
     class ReceiveThread extends Thread {
 
         private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
-        private boolean stopped = false;
+        private volatile boolean mStopped = false;
 
         public void halt() {
-            stopped = true;
+            mStopped = true;
             closeSockets();  // Interrupts the read() call the thread is blocked in.
         }
 
         @Override
         public void run() {
             if (DBG) Log.d(TAG, "Receive thread started");
-            while (!stopped) {
+            while (!mStopped) {
                 int length = 0;  // Or compiler can't tell it's initialized if a parse error occurs.
                 try {
                     length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
@@ -355,7 +355,7 @@
                     if (DBG) Log.d(TAG, "Received packet: " + packet);
                     sendMessage(CMD_RECEIVED_PACKET, packet);
                 } catch (IOException|ErrnoException e) {
-                    if (!stopped) {
+                    if (!mStopped) {
                         Log.e(TAG, "Read error", e);
                     }
                     DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_RECV_ERROR,
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index a881408..b184fe5 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -3,6 +3,7 @@
 import android.net.DhcpResults;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
+import android.net.metrics.DhcpErrorEvent;
 import android.os.Build;
 import android.os.SystemProperties;
 import android.system.OsConstants;
@@ -765,6 +766,7 @@
         // check to see if we need to parse L2, IP, and UDP encaps
         if (pktType == ENCAP_L2) {
             if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.L2_TOO_SHORT);
                 throw new ParseException("L2 packet too short, %d < %d",
                         packet.remaining(), MIN_PACKET_LENGTH_L2);
             }
@@ -777,13 +779,16 @@
 
             short l2type = packet.getShort();
 
-            if (l2type != OsConstants.ETH_P_IP)
+            if (l2type != OsConstants.ETH_P_IP) {
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.L2_WRONG_ETH_TYPE);
                 throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x",
                         l2type, OsConstants.ETH_P_IP);
+            }
         }
 
         if (pktType <= ENCAP_L3) {
             if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.L3_TOO_SHORT);
                 throw new ParseException("L3 packet too short, %d < %d",
                         packet.remaining(), MIN_PACKET_LENGTH_L3);
             }
@@ -791,6 +796,7 @@
             byte ipTypeAndLength = packet.get();
             int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
             if (ipVersion != 4) {
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.L3_NOT_IPV4);
                 throw new ParseException("Invalid IP version %d", ipVersion);
             }
 
@@ -808,6 +814,7 @@
             ipDst = readIpAddress(packet);
 
             if (ipProto != IP_TYPE_UDP) {
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.L4_NOT_UDP);
                 throw new ParseException("Protocol not UDP: %d", ipProto);
             }
 
@@ -834,12 +841,14 @@
                 // socket to drop packets that don't have the right source ports. However, it's
                 // possible that a packet arrives between when the socket is bound and when the
                 // filter is set. http://b/26696823 .
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.L4_WRONG_PORT);
                 throw new ParseException("Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
             }
         }
 
         // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
         if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
+            DhcpErrorEvent.logEvent(DhcpErrorEvent.BOOTP_TOO_SHORT);
             throw new ParseException("Invalid type or BOOTP packet too short, %d < %d",
                         packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
         }
@@ -864,6 +873,7 @@
             packet.get(ipv4addr);
             relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
         } catch (UnknownHostException ex) {
+            DhcpErrorEvent.logEvent(DhcpErrorEvent.L3_INVALID_IP);
             throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
         }
 
@@ -888,6 +898,7 @@
         int dhcpMagicCookie = packet.getInt();
 
         if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
+            DhcpErrorEvent.logEvent(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE);
             throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie,
                     DHCP_MAGIC_COOKIE);
         }
@@ -896,9 +907,8 @@
         boolean notFinishedOptions = true;
 
         while ((packet.position() < packet.limit()) && notFinishedOptions) {
+            final byte optionType = packet.get(); // cannot underflow because position < limit
             try {
-                byte optionType = packet.get();
-
                 if (optionType == DHCP_OPTION_END) {
                     notFinishedOptions = false;
                 } else if (optionType == DHCP_OPTION_PAD) {
@@ -999,11 +1009,14 @@
                     }
 
                     if (expectedLen != optionLen) {
+                        DhcpErrorEvent.logEvent(
+                                DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType);
                         throw new ParseException("Invalid length %d for option %d, expected %d",
                                 optionLen, optionType, expectedLen);
                     }
                 }
             } catch (BufferUnderflowException e) {
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.BUFFER_UNDERFLOW, optionType);
                 throw new ParseException("BufferUnderflowException");
             }
         }
@@ -1012,6 +1025,7 @@
 
         switch(dhcpType) {
             case (byte) 0xFF:
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.DHCP_NO_MSG_TYPE);
                 throw new ParseException("No DHCP message type option");
             case DHCP_MESSAGE_TYPE_DISCOVER:
                 newPacket = new DhcpDiscoverPacket(
@@ -1045,6 +1059,7 @@
                     clientMac);
                 break;
             default:
+                DhcpErrorEvent.logEvent(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE);
                 throw new ParseException("Unimplemented DHCP type %d", dhcpType);
         }
 
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/Android.mk b/services/tests/servicestests/Android.mk
index e6dd6a4..7ffdb35 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -19,7 +19,8 @@
     easymocklib \
     guava \
     android-support-test \
-    mockito-target
+    mockito-target \
+    ShortcutManagerTestUtils
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
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/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 5b4803b..4468857 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -16,23 +16,34 @@
 
 package com.android.server.accounts;
 
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.accounts.Account;
 import android.accounts.AuthenticatorDescription;
+import android.app.AppOpsManager;
 import android.app.Notification;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.RegisteredServicesCache.ServiceInfo;
 import android.content.pm.RegisteredServicesCacheListener;
+import android.content.pm.UserInfo;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.test.AndroidTestCase;
-import android.test.IsolatedContext;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
 import android.test.mock.MockPackageManager;
+import android.util.Log;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -41,20 +52,28 @@
 import java.util.Comparator;
 
 public class AccountManagerServiceTest extends AndroidTestCase {
+    private static final String TAG = AccountManagerServiceTest.class.getSimpleName();
+
+    static final String PREN_DB = "pren.db";
+    static final String DE_DB = "de.db";
+    static final String CE_DB = "ce.db";
     private AccountManagerService mAms;
 
     @Override
     protected void setUp() throws Exception {
-        final String filenamePrefix = "test.";
-        MockContentResolver resolver = new MockContentResolver();
-        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
-                new MyMockContext(), // The context that most methods are delegated to
-                getContext(), // The context that file methods are delegated to
-                filenamePrefix);
-        Context context = new IsolatedContext(resolver, targetContextWrapper);
-        setContext(context);
+        Context realTestContext = getContext();
+        Context mockContext = new MyMockContext(realTestContext);
+        setContext(mockContext);
         mAms = new MyAccountManagerService(getContext(),
-                new MyMockPackageManager(), new MockAccountAuthenticatorCache());
+                new MyMockPackageManager(), new MockAccountAuthenticatorCache(), realTestContext);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        new File(mAms.getCeDatabaseName(UserHandle.USER_SYSTEM)).delete();
+        new File(mAms.getDeDatabaseName(UserHandle.USER_SYSTEM)).delete();
+        new File(mAms.getPreNDatabaseName(UserHandle.USER_SYSTEM)).delete();
+        super.tearDown();
     }
 
     public class AccountSorter implements Comparator<Account> {
@@ -69,6 +88,7 @@
     }
 
     public void testCheckAddAccount() throws Exception {
+        unlockUser(UserHandle.USER_SYSTEM);
         Account a11 = new Account("account1", "type1");
         Account a21 = new Account("account2", "type1");
         Account a31 = new Account("account3", "type1");
@@ -109,6 +129,7 @@
     }
 
     public void testPasswords() throws Exception {
+        unlockUser(UserHandle.USER_SYSTEM);
         Account a11 = new Account("account1", "type1");
         Account a12 = new Account("account1", "type2");
         mAms.addAccountExplicitly(a11, "p11", null);
@@ -124,6 +145,7 @@
     }
 
     public void testUserdata() throws Exception {
+        unlockUser(UserHandle.USER_SYSTEM);
         Account a11 = new Account("account1", "type1");
         Bundle u11 = new Bundle();
         u11.putString("a", "a_a11");
@@ -156,6 +178,7 @@
     }
 
     public void testAuthtokens() throws Exception {
+        unlockUser(UserHandle.USER_SYSTEM);
         Account a11 = new Account("account1", "type1");
         Account a12 = new Account("account1", "type2");
         mAms.addAccountExplicitly(a11, "p11", null);
@@ -188,15 +211,21 @@
         assertNull(mAms.peekAuthToken(a12, "att2"));
     }
 
+    private void unlockUser(int userId) {
+        Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        mAms.onUserUnlocked(intent);
+    }
+
     static public class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache {
         private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices;
 
         public MockAccountAuthenticatorCache() {
-            mServices = new ArrayList<ServiceInfo<AuthenticatorDescription>>();
+            mServices = new ArrayList<>();
             AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0);
             AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0);
-            mServices.add(new ServiceInfo<AuthenticatorDescription>(d1, null, null));
-            mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, null));
+            mServices.add(new ServiceInfo<>(d1, null, null));
+            mServices.add(new ServiceInfo<>(d2, null, null));
         }
 
         @Override
@@ -232,10 +261,68 @@
     }
 
     static public class MyMockContext extends MockContext {
+        private Context mTestContext;
+        private AppOpsManager mAppOpsManager;
+        private UserManager mUserManager;
+
+        public MyMockContext(Context testContext) {
+            this.mTestContext = testContext;
+            this.mAppOpsManager = mock(AppOpsManager.class);
+            this.mUserManager = mock(UserManager.class);
+            final UserInfo ui = new UserInfo(UserHandle.USER_SYSTEM, "user0", 0);
+            when(mUserManager.getUserInfo(eq(ui.id))).thenReturn(ui);
+        }
+
         @Override
         public int checkCallingOrSelfPermission(final String permission) {
             return PackageManager.PERMISSION_GRANTED;
         }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.APP_OPS_SERVICE.equals(name)) {
+                return mAppOpsManager;
+            } else if( Context.USER_SERVICE.equals(name)) {
+                return mUserManager;
+            }
+            return null;
+        }
+
+        @Override
+        public String getSystemServiceName(Class<?> serviceClass) {
+            if (AppOpsManager.class.equals(serviceClass)) {
+                return Context.APP_OPS_SERVICE;
+            }
+            return null;
+        }
+
+        @Override
+        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+            return null;
+        }
+
+        @Override
+        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+                IntentFilter filter, String broadcastPermission, Handler scheduler) {
+            return null;
+        }
+
+        @Override
+        public SQLiteDatabase openOrCreateDatabase(String file, int mode,
+                SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
+            Log.i(TAG, "openOrCreateDatabase " + file + " mode " + mode);
+            return mTestContext.openOrCreateDatabase(file, mode, factory,errorHandler);
+        }
+
+        @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+            Log.i(TAG, "sendBroadcastAsUser " + intent + " " + user);
+        }
+
+        @Override
+        public String getOpPackageName() {
+            return null;
+        }
     }
 
     static public class MyMockPackageManager extends MockPackageManager {
@@ -246,9 +333,11 @@
     }
 
     static public class MyAccountManagerService extends AccountManagerService {
+        private Context mRealTestContext;
         public MyAccountManagerService(Context context, PackageManager packageManager,
-                IAccountAuthenticatorCache authenticatorCache) {
+                IAccountAuthenticatorCache authenticatorCache, Context realTestContext) {
             super(context, packageManager, authenticatorCache);
+            this.mRealTestContext = realTestContext;
         }
 
         @Override
@@ -258,5 +347,20 @@
         @Override
         protected void cancelNotification(final int id, UserHandle user) {
         }
+
+        @Override
+        protected String getCeDatabaseName(int userId) {
+            return new File(mRealTestContext.getCacheDir(), CE_DB).getPath();
+        }
+
+        @Override
+        protected String getDeDatabaseName(int userId) {
+            return new File(mRealTestContext.getCacheDir(), DE_DB).getPath();
+        }
+
+        @Override
+        String getPreNDatabaseName(int userId) {
+            return new File(mRealTestContext.getCacheDir(), PREN_DB).getPath();
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 60d7382..f6e35f5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -39,7 +39,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
-import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
@@ -343,31 +342,25 @@
                     @Override
                     public List<UserInfo> answer(InvocationOnMock invocation) throws Throwable {
                         final int userId = (int) invocation.getArguments()[0];
-                        final ArrayList<UserInfo> ret = new ArrayList<UserInfo>();
-                        UserInfo parent = null;
-                        for (UserInfo ui : mUserInfos) {
-                            if (ui.id == userId) {
-                                parent = ui;
-                                break;
-                            }
-                        }
-                        if (parent == null) {
-                            return ret;
-                        }
-                        ret.add(parent);
-                        for (UserInfo ui : mUserInfos) {
-                            if (ui.id == userId) {
-                                continue;
-                            }
-                            if (ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
-                                    && ui.profileGroupId == parent.profileGroupId) {
-                                ret.add(ui);
-                            }
-                        }
-                        return ret;
+                        return getProfiles(userId);
                     }
                 }
         );
+        when(userManager.getProfileIdsWithDisabled(anyInt())).thenAnswer(
+                new Answer<int[]>() {
+                    @Override
+                    public int[] answer(InvocationOnMock invocation) throws Throwable {
+                        final int userId = (int) invocation.getArguments()[0];
+                        List<UserInfo> profiles = getProfiles(userId);
+                        int[] results = new int[profiles.size()];
+                        for (int i = 0; i < results.length; i++) {
+                            results[i] = profiles.get(i).id;
+                        }
+                        return results;
+                    }
+                }
+        );
+
 
         // Create a data directory.
         final File dir = new File(dataDir, "user" + userId);
@@ -377,6 +370,31 @@
         return dir;
     }
 
+    private List<UserInfo> getProfiles(int userId) {
+        final ArrayList<UserInfo> ret = new ArrayList<UserInfo>();
+        UserInfo parent = null;
+        for (UserInfo ui : mUserInfos) {
+            if (ui.id == userId) {
+                parent = ui;
+                break;
+            }
+        }
+        if (parent == null) {
+            return ret;
+        }
+        ret.add(parent);
+        for (UserInfo ui : mUserInfos) {
+            if (ui.id == userId) {
+                continue;
+            }
+            if (ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+                    && ui.profileGroupId == parent.profileGroupId) {
+                ret.add(ui);
+            }
+        }
+        return ret;
+    }
+
     /**
      * Add multiple users at once.  They'll all have flag 0.
      */
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 baa5d36..ce02a79 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.*;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -172,6 +173,11 @@
         public String getPackageName() {
             return mInjectedClientPackage;
         }
+
+        @Override
+        public int getUserId() {
+            return getCallingUserId();
+        }
     }
 
     /** Context used in the service side */
@@ -190,6 +196,11 @@
         public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
                 UserHandle userId) {
         }
+
+        @Override
+        public int getUserId() {
+            return UserHandle.USER_SYSTEM;
+        }
     }
 
     /** ShortcutService with injection override methods. */
@@ -205,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 + ","
@@ -454,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;
 
@@ -598,14 +610,6 @@
         addPackage(packageName, uid, version, packageName);
     }
 
-    private <T> List<T> list(T... array) {
-        return Arrays.asList(array);
-    }
-
-    private <T> Set<T> set(Set<T> in) {
-        return new ArraySet<T>(in);
-    }
-
     private Signature[] genSignatures(String... signatures) {
         final Signature[] sigs = new Signature[signatures.length];
         for (int i = 0; i < signatures.length; i++){
@@ -789,33 +793,6 @@
         runTestOnUiThread(() -> {});
     }
 
-    public static Bundle makeBundle(Object... keysAndValues) {
-        Preconditions.checkState((keysAndValues.length % 2) == 0);
-
-        if (keysAndValues.length == 0) {
-            return null;
-        }
-        final Bundle ret = new Bundle();
-
-        for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
-            final String key = keysAndValues[i].toString();
-            final Object value = keysAndValues[i + 1];
-
-            if (value == null) {
-                ret.putString(key, null);
-            } else if (value instanceof Integer) {
-                ret.putInt(key, (Integer) value);
-            } else if (value instanceof String) {
-                ret.putString(key, (String) value);
-            } else if (value instanceof Bundle) {
-                ret.putBundle(key, (Bundle) value);
-            } else {
-                fail("Type not supported yet: " + value.getClass().getName());
-            }
-        }
-        return ret;
-    }
-
     /**
      * Make a shortcut with an ID.
      */
@@ -913,20 +890,6 @@
         return new ComponentName(mClientContext, clazz);
     }
 
-    private <T> Set<T> makeSet(T... values) {
-        final HashSet<T> ret = new HashSet<>();
-        for (T s : values) {
-            ret.add(s);
-        }
-        return ret;
-    }
-
-    private static void resetAll(Collection<?> mocks) {
-        for (Object o : mocks) {
-            reset(o);
-        }
-    }
-
     @NonNull
     private ShortcutInfo findById(List<ShortcutInfo> list, String id) {
         for (ShortcutInfo s : list) {
@@ -947,59 +910,8 @@
         assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
     }
 
-    @NonNull
-    private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
-            String... expectedIds) {
-        final HashSet<String> expected = new HashSet<>(list(expectedIds));
-        final HashSet<String> actual = new HashSet<>();
-        for (ShortcutInfo s : actualShortcuts) {
-            actual.add(s.getId());
-        }
-
-        // Compare the sets.
-        assertEquals(expected, actual);
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllHaveIntents(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertNotNull("ID " + s.getId(), s.getIntent());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllNotHaveIntents(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertNull("ID " + s.getId(), s.getIntent());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllHaveTitle(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertNotNull("ID " + s.getId(), s.getTitle());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllNotHaveTitle(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertNull("ID " + s.getId(), s.getTitle());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllNotHaveIcon(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
+    public static List<ShortcutInfo> assertAllNotHaveIcon(
+            List<ShortcutInfo> actualShortcuts) {
         for (ShortcutInfo s : actualShortcuts) {
             assertNull("ID " + s.getId(), s.getIcon());
         }
@@ -1007,35 +919,6 @@
     }
 
     @NonNull
-    private List<ShortcutInfo> assertAllHaveIconResId(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
-            assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllHaveIconFile(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
-            assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllHaveIcon(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
     private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
             int shortcutFlags) {
         for (ShortcutInfo s : actualShortcuts) {
@@ -1045,87 +928,6 @@
         return actualShortcuts;
     }
 
-    @NonNull
-    private List<ShortcutInfo> assertAllKeyFieldsOnly(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllNotKeyFieldsOnly(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
-        }
-        return actualShortcuts;
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllDynamic(@NonNull List<ShortcutInfo> actualShortcuts) {
-        return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_DYNAMIC);
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllPinned(@NonNull List<ShortcutInfo> actualShortcuts) {
-        return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_PINNED);
-    }
-
-    @NonNull
-    private List<ShortcutInfo> assertAllDynamicOrPinned(
-            @NonNull List<ShortcutInfo> actualShortcuts) {
-        for (ShortcutInfo s : actualShortcuts) {
-            assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
-        }
-        return actualShortcuts;
-    }
-
-    private void assertDynamicOnly(ShortcutInfo si) {
-        assertTrue(si.isDynamic());
-        assertFalse(si.isPinned());
-    }
-
-    private void assertPinnedOnly(ShortcutInfo si) {
-        assertFalse(si.isDynamic());
-        assertTrue(si.isPinned());
-    }
-
-    private void assertDynamicAndPinned(ShortcutInfo si) {
-        assertTrue(si.isDynamic());
-        assertTrue(si.isPinned());
-    }
-
-    private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
-        assertEquals("width", expectedWidth, bitmap.getWidth());
-        assertEquals("height", expectedHeight, bitmap.getHeight());
-    }
-
-    private <T> void assertAllUnique(Collection<T> list) {
-        final Set<Object> set = new HashSet<>();
-        for (T item : list) {
-            if (set.contains(item)) {
-                fail("Duplicate item found: " + item + " (in the list: " + list + ")");
-            }
-            set.add(item);
-        }
-    }
-
-    @NonNull
-    private Bitmap pfdToBitmap(@NonNull ParcelFileDescriptor pfd) {
-        Preconditions.checkNotNull(pfd);
-        try {
-            return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
-        } finally {
-            IoUtils.closeQuietly(pfd);
-        }
-    }
-
-    private void assertBundleEmpty(BaseBundle b) {
-        assertTrue(b == null || b.size() == 0);
-    }
-
     private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
         return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
     }
@@ -1152,9 +954,25 @@
         return intentCaptor.getValue();
     }
 
+    private Intent launchShortcutAndGetIntent_withShortcutInfo(
+            @NonNull String packageName, @NonNull String shortcutId, int userId) {
+        reset(mServiceContext);
+
+        assertTrue(mLauncherApps.startShortcut(
+                getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null));
+
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mServiceContext).startActivityAsUser(
+                intentCaptor.capture(),
+                any(Bundle.class),
+                eq(UserHandle.of(userId)));
+        return intentCaptor.getValue();
+    }
+
     private void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
             int userId) {
         assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
+        assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
     }
 
     private void assertShortcutNotLaunchable(@NonNull String packageName,
@@ -1193,6 +1011,14 @@
         return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
     }
 
+    private ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId,
+            int userId) {
+        final List<ShortcutInfo> infoList =
+                mLauncherApps.getShortcutInfo(packageName, list(shortcutId),
+                        UserHandle.of(userId));
+        assertEquals("No shortcutInfo found (or too many of them)", 1, infoList.size());
+        return infoList.get(0);
+    }
 
     private Intent genPackageDeleteIntent(String pakcageName, int userId) {
         Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
@@ -1249,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);
@@ -1286,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());
@@ -1309,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());
 
@@ -1329,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()} */
@@ -1416,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()));
 
@@ -1490,7 +1326,7 @@
 
         assertEquals(2, mManager.getRemainingCallCount());
 
-        mManager.deleteAllDynamicShortcuts();
+        mManager.removeAllDynamicShortcuts();
         assertEquals(0, mManager.getDynamicShortcuts().size());
         assertEquals(2, mManager.getRemainingCallCount());
 
@@ -1558,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());
@@ -1684,7 +1520,7 @@
         assertFalse(mManager.setDynamicShortcuts(list(si2)));
     }
 
-    public void testIcons() {
+    public void testIcons() throws IOException {
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
         final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
         final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
@@ -1728,6 +1564,16 @@
                 "res64x64",
                 "none");
 
+        // Different profile.  Note the names and the contents don't match.
+        setCaller(CALLING_PACKAGE_1, USER_P0);
+        assertTrue(mManager.setDynamicShortcuts(list(
+                makeShortcutWithIcon("res32x32", res512x512),
+                makeShortcutWithIcon("bmp32x32", bmp512x512)
+        )));
+        assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+                "res32x32",
+                "bmp32x32");
+
         // Re-initialize and load from the files.
         mService.saveDirtyInfo();
         initService();
@@ -1737,61 +1583,90 @@
 
         setCaller(LAUNCHER_1);
         // Check hasIconResource()/hasIconFile().
-        assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
-                CALLING_PACKAGE_1, list("res32x32"),
-                getCallingUser())), "res32x32");
+        assertShortcutIds(assertAllHaveIconResId(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0))),
+                "res32x32");
 
-        assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
-                CALLING_PACKAGE_1, list("res64x64"), getCallingUser())),
+        assertShortcutIds(assertAllHaveIconResId(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0))),
                 "res64x64");
 
-        assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
-                CALLING_PACKAGE_1, list("bmp32x32"), getCallingUser())),
+        assertShortcutIds(assertAllHaveIconFile(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0))),
                 "bmp32x32");
 
-        assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
-                CALLING_PACKAGE_1, list("bmp64x64"), getCallingUser())),
+        assertShortcutIds(assertAllHaveIconFile(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0))),
                 "bmp64x64");
 
-        assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
-                CALLING_PACKAGE_1, list("bmp512x512"), getCallingUser())),
+        assertShortcutIds(assertAllHaveIconFile(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
                 "bmp512x512");
 
+        assertShortcutIds(assertAllHaveIconResId(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
+                "res32x32");
+        assertShortcutIds(assertAllHaveIconFile(
+                list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0))),
+                "bmp32x32");
+
         // Check
         assertEquals(
                 R.drawable.black_32x32,
                 mLauncherApps.getShortcutIconResId(
-                        makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
+                        getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_0)));
 
         assertEquals(
                 R.drawable.black_64x64,
                 mLauncherApps.getShortcutIconResId(
-
-                        makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
+                        getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res64x64", USER_0)));
 
         assertEquals(
                 0, // because it's not a resource
                 mLauncherApps.getShortcutIconResId(
-                        makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+                        getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
         assertEquals(
                 0, // because it's not a resource
                 mLauncherApps.getShortcutIconResId(
-                        makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+                        getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
         assertEquals(
                 0, // because it's not a resource
                 mLauncherApps.getShortcutIconResId(
-                        makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+                        getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
 
         bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
-                makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+                getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_0)));
         assertBitmapSize(32, 32, bmp);
 
         bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
-                makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+                getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp64x64", USER_0)));
         assertBitmapSize(64, 64, bmp);
 
         bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
-                makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+                getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0)));
+        assertBitmapSize(128, 128, bmp);
+
+        assertEquals(
+                R.drawable.black_512x512,
+                mLauncherApps.getShortcutIconResId(
+                        getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0)));
+        // Should be 512x512, so shrunk.
+        bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+                getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp32x32", USER_P0)));
+        assertBitmapSize(128, 128, bmp);
+
+        // Also check the overload APIs too.
+        assertEquals(
+                R.drawable.black_32x32,
+                mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_0));
+        assertEquals(
+                R.drawable.black_64x64,
+                mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res64x64", HANDLE_USER_0));
+        assertEquals(
+                R.drawable.black_512x512,
+                mLauncherApps.getShortcutIconResId(CALLING_PACKAGE_1, "res32x32", HANDLE_USER_P0));
+        bmp = pfdToBitmap(
+                mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
         assertBitmapSize(128, 128, bmp);
 
         // TODO Test the content URI case too.
@@ -1927,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(
@@ -2034,9 +1909,16 @@
     private static ShortcutQuery buildQuery(long changedSince,
             String packageName, ComponentName componentName,
             /* @ShortcutQuery.QueryFlags */ int flags) {
+        return buildQuery(changedSince, packageName, null, componentName, flags);
+    }
+
+    private static ShortcutQuery buildQuery(long changedSince,
+            String packageName, List<String> shortcutIds, ComponentName componentName,
+            /* @ShortcutQuery.QueryFlags */ int flags) {
         final ShortcutQuery q = new ShortcutQuery();
         q.setChangedSince(changedSince);
         q.setPackage(packageName);
+        q.setShortcutIds(shortcutIds);
         q.setActivity(componentName);
         q.setQueryFlags(flags);
         return q;
@@ -2110,6 +1992,36 @@
                         getCallingUser())),
                 "s2", "s3"))));
 
+        // With ID.
+        assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+                        /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"),
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+                        getCallingUser())),
+                "s3"))));
+        assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+                        /* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"),
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+                        getCallingUser())),
+                "s2", "s3"))));
+        assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+                        /* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"),
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+                        getCallingUser()))
+                /* empty */))));
+        assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+                        /* time =*/ 1000, CALLING_PACKAGE_2, list(),
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+                        getCallingUser()))
+                /* empty */))));
+
         // Pin some shortcuts.
         mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
                 list("s3", "s4"), getCallingUser());
@@ -2132,6 +2044,13 @@
                         getCallingUser())),
                 "s1", "s3");
 
+        assertExpectException(
+                IllegalArgumentException.class, "package name must also be set", () -> {
+            mLauncherApps.getShortcuts(buildQuery(
+                    /* time =*/ 0, /* package= */ null, list("id"),
+                    /* activity =*/ null, /* flags */ 0), getCallingUser());
+        });
+
         // TODO More tests: pinned but dynamic, filter by activity
     }
 
@@ -2181,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();
@@ -2246,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 */);
         });
 
@@ -2310,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");
         });
 
@@ -2318,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");
         });
 
@@ -2428,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");
@@ -2469,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");
@@ -3071,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);
@@ -3143,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();
@@ -3163,7 +3082,7 @@
         // Test for remove
         reset(c0);
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
-            mManager.deleteDynamicShortcut("s1");
+            mManager.removeDynamicShortcuts(list("s1"));
         });
 
         waitOnMainThread();
@@ -3196,7 +3115,7 @@
         // Test for deleteAll
         reset(c0);
         runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
-            mManager.deleteAllDynamicShortcuts();
+            mManager.removeAllDynamicShortcuts();
         });
 
         waitOnMainThread();
@@ -3224,20 +3143,6 @@
         assertEquals(0, shortcuts.getValue().size());
     }
 
-    private void assertCallbackNotReceived(LauncherApps.Callback mock) {
-        verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
-                any(UserHandle.class));
-    }
-
-    private void assertCallbackReceived(LauncherApps.Callback mock,
-            UserHandle user, String packageName, String... ids) {
-        ArgumentCaptor<List> shortcutsCaptor = ArgumentCaptor.forClass(List.class);
-
-        verify(mock, times(1)).onShortcutsChanged(eq(packageName), shortcutsCaptor.capture(),
-                eq(user));
-        assertShortcutIds(shortcutsCaptor.getValue(), ids);
-    }
-
     public void testLauncherCallback_crossProfile() throws Throwable {
         prepareCrossProfileDataSet();
 
@@ -3284,7 +3189,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3301,7 +3206,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3319,7 +3224,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3339,7 +3244,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3359,7 +3264,7 @@
 
         resetAll(all);
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
-            mManager.deleteDynamicShortcut("x");
+            mManager.removeDynamicShortcuts(list());
         });
         waitOnMainThread();
 
@@ -3583,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();
         });
 
 
@@ -3607,18 +3512,18 @@
 
         // Check the registered packages.
         dumpsysOnLogcat();
-        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+                set(PackageWithUser.of(USER_10, LAUNCHER_1),
                         PackageWithUser.of(USER_10, LAUNCHER_2)),
-                set(user10.getAllLaunchers().keySet()));
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_1", "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3639,18 +3544,18 @@
         mService.cleanUpPackageLocked("abc", USER_0, USER_0);
 
         // No changes.
-        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+                set(PackageWithUser.of(USER_10, LAUNCHER_1),
                         PackageWithUser.of(USER_10, LAUNCHER_2)),
-                set(user10.getAllLaunchers().keySet()));
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_1", "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3670,18 +3575,18 @@
         uninstallPackage(USER_0, CALLING_PACKAGE_1);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0);
 
-        assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+                set(PackageWithUser.of(USER_10, LAUNCHER_1),
                         PackageWithUser.of(USER_10, LAUNCHER_2)),
-                set(user10.getAllLaunchers().keySet()));
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3701,17 +3606,17 @@
         uninstallPackage(USER_10, LAUNCHER_1);
         mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10);
 
-        assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
-                set(user10.getAllLaunchers().keySet()));
+                set(PackageWithUser.of(USER_10, LAUNCHER_2)),
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3729,17 +3634,17 @@
         uninstallPackage(USER_10, CALLING_PACKAGE_2);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10);
 
-        assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(CALLING_PACKAGE_1),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
-                set(user10.getAllLaunchers().keySet()));
+                set(PackageWithUser.of(USER_10, LAUNCHER_2)),
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3757,17 +3662,17 @@
         uninstallPackage(USER_10, LAUNCHER_2);
         mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10);
 
-        assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(CALLING_PACKAGE_1),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_1),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
         assertEquals(
-                makeSet(),
-                set(user10.getAllLaunchers().keySet()));
+                set(),
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -3785,16 +3690,16 @@
         uninstallPackage(USER_10, CALLING_PACKAGE_1);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10);
 
-        assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getAllPackages().keySet()));
-        assertEquals(makeSet(),
-                set(user10.getAllPackages().keySet()));
+        assertEquals(set(CALLING_PACKAGE_2),
+                hashSet(user0.getAllPackages().keySet()));
+        assertEquals(set(),
+                hashSet(user10.getAllPackages().keySet()));
         assertEquals(
-                makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+                set(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
-                set(user0.getAllLaunchers().keySet()));
-        assertEquals(makeSet(),
-                set(user10.getAllLaunchers().keySet()));
+                hashSet(user0.getAllLaunchers().keySet()));
+        assertEquals(set(),
+                hashSet(user10.getAllLaunchers().keySet()));
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
                 "s0_2");
         assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -4115,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));
@@ -4935,7 +4840,7 @@
             assertShortcutIds(
                     mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
                     /* empty */);
-            TestUtils.assertExpectException(
+            assertExpectException(
                     SecurityException.class, "", () -> {
                         mLauncherApps.getShortcuts(
                                 buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
@@ -5014,7 +4919,7 @@
             assertShortcutIds(
                     mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
                     "s1", "s4");
-            TestUtils.assertExpectException(
+            assertExpectException(
                     SecurityException.class, "unrelated profile", () -> {
                         mLauncherApps.getShortcuts(
                                 buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
@@ -5030,12 +4935,12 @@
             assertShortcutIds(
                     mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_10)
                     /* empty */);
-            TestUtils.assertExpectException(
+            assertExpectException(
                     SecurityException.class, "unrelated profile", () -> {
                         mLauncherApps.getShortcuts(
                                 buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0);
                     });
-            TestUtils.assertExpectException(
+            assertExpectException(
                     SecurityException.class, "unrelated profile", () -> {
                         mLauncherApps.getShortcuts(
                                 buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0);
@@ -5046,16 +4951,16 @@
     // ShortcutInfo tests
 
     public void testShortcutInfoMissingMandatoryFields() {
-        TestUtils.assertExpectException(
+        assertExpectException(
                 IllegalArgumentException.class,
                 "ID must be provided",
                 () -> new ShortcutInfo.Builder(getTestContext()).build());
-        TestUtils.assertExpectException(
+        assertExpectException(
                 IllegalArgumentException.class,
                 "title must be provided",
                 () -> new ShortcutInfo.Builder(getTestContext()).setId("id").build()
                         .enforceMandatoryFields());
-        TestUtils.assertExpectException(
+        assertExpectException(
                 NullPointerException.class,
                 "Intent must be provided",
                 () -> new ShortcutInfo.Builder(getTestContext()).setId("id").setTitle("x").build()
@@ -5063,12 +4968,15 @@
     }
 
     public void testShortcutInfoParcel() {
-        ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext())
+        setCaller(CALLING_PACKAGE_1, USER_10);
+        ShortcutInfo si = parceled(new ShortcutInfo.Builder(mClientContext)
                 .setId("id")
                 .setTitle("title")
                 .setIntent(makeIntent("action", ShortcutActivity.class))
                 .build());
-        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals(mClientContext.getPackageName(), si.getPackageName());
+        assertEquals(USER_10, si.getUserId());
+        assertEquals(HANDLE_USER_10, si.getUserHandle());
         assertEquals("id", si.getId());
         assertEquals("title", si.getTitle());
         assertEquals("action", si.getIntent().getAction());
@@ -5083,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();
@@ -5098,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());
@@ -5109,14 +5019,17 @@
     }
 
     public void testShortcutInfoClone() {
+        setCaller(CALLING_PACKAGE_1, USER_11);
+
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
-        ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+        ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
                 .setId("id")
                 .setActivityComponent(new ComponentName("a", "b"))
                 .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)
@@ -5127,12 +5040,15 @@
 
         ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
 
-        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals(USER_11, si.getUserId());
+        assertEquals(HANDLE_USER_11, si.getUserHandle());
+        assertEquals(mClientContext.getPackageName(), si.getPackageName());
         assertEquals("id", si.getId());
         assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
         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());
@@ -5144,12 +5060,13 @@
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
 
-        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals(mClientContext.getPackageName(), si.getPackageName());
         assertEquals("id", si.getId());
         assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
         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());
@@ -5157,39 +5074,85 @@
 
         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);
 
-        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals(mClientContext.getPackageName(), si.getPackageName());
         assertEquals("id", si.getId());
         assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
         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);
 
-        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals(mClientContext.getPackageName(), si.getPackageName());
         assertEquals("id", si.getId());
         assertEquals(null, si.getActivityComponent());
         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 {
@@ -5201,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)
@@ -5214,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());
 
 
@@ -5255,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);
 
@@ -5267,7 +5253,7 @@
     }
 
     public void testShortcutInfoSaveAndLoad() throws InterruptedException {
-        setCaller(CALLING_PACKAGE_1, USER_0);
+        setCaller(CALLING_PACKAGE_1, USER_10);
 
         final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_32x32));
@@ -5280,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();
@@ -5293,17 +5280,20 @@
         // Save and load.
         mService.saveDirtyInfo();
         initService();
-        mService.handleUnlockUser(USER_0);
+        mService.handleUnlockUser(USER_10);
 
         ShortcutInfo si;
-        si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+        si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
 
+        assertEquals(USER_10, si.getUserId());
+        assertEquals(HANDLE_USER_10, si.getUserHandle());
         assertEquals(CALLING_PACKAGE_1, si.getPackageName());
         assertEquals("id", si.getId());
         assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
         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());
@@ -5329,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);
@@ -5343,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);
 
@@ -5352,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());
@@ -5367,7 +5361,7 @@
         dumpsysOnLogcat("test1", /* force= */ true);
     }
 
-    public void testDumpsys_withIcons() {
+    public void testDumpsys_withIcons() throws IOException {
         testIcons();
         // Dump after having some icons.
         dumpsysOnLogcat("test1", /* force= */ true);
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..e33be40
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -0,0 +1,116 @@
+/*
+ * 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 = null;
+    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);
+    }
+
+    public void removePackageInfo(String packageName) {
+        mPackages.remove(packageName);
+    }
+
+    @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..c03324a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -0,0 +1,820 @@
+/*
+ * 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 checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName,
+            WebViewProviderInfo[] webviewPackages) {
+        checkCertainPackageUsedAfterWebViewBootPreparation(
+                expectedProviderName, webviewPackages, 1);
+    }
+
+    private void checkCertainPackageUsedAfterWebViewBootPreparation(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;
+    }
+
+    private void checkPreparationPhasesForPackage(String expectedPackage, int numPreparation) {
+        // Verify that onWebViewProviderChanged was called for the numPreparation'th time for the
+        // expected package
+        Mockito.verify(mTestSystemImpl, Mockito.times(numPreparation)).onWebViewProviderChanged(
+                Mockito.argThat(new IsPackageInfoWithName(expectedPackage)));
+
+        mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+        assertEquals(expectedPackage, response.packageInfo.packageName);
+    }
+
+
+    // ****************
+    // Tests
+    // ****************
+
+
+    public void testWithSinglePackage() {
+        String testPackageName = "test.package.name";
+        checkCertainPackageUsedAfterWebViewBootPreparation(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)};
+        checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages);
+    }
+
+    public void testSeveralRelros() {
+        String singlePackage = "singlePackage";
+        checkCertainPackageUsedAfterWebViewBootPreparation(
+                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();
+
+
+        checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */);
+
+        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("package", "", true, true, null);
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
+        setupWithPackages(packages);
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */));
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
+                Matchers.anyObject());
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+
+        // Verify that we can recover from failing to list webview packages.
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+
+        checkPreparationPhasesForPackage(wpi.packageName, 1);
+    }
+
+    // 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) {
+        checkCertainPackageUsedAfterWebViewBootPreparation(initialPackage, packages);
+
+        mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
+        checkPreparationPhasesForPackage(finalPackage, 1 /* first preparation for this package */);
+
+        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());
+
+        checkPreparationPhasesForPackage(fallbackPackage,
+                1 /* first preparation for this package*/);
+
+        // Install primary package
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+
+        // Verify fallback disabled, primary package used as provider, and fallback package killed
+        Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+                Matchers.anyObject(), Mockito.eq(fallbackPackage));
+        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation for this package*/);
+        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());
+
+        checkPreparationPhasesForPackage(primaryPackage, 1);
+
+        // Disable primary package and ensure fallback becomes enabled and used
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_CHANGED);
+
+        Mockito.verify(mTestSystemImpl).enablePackageForUser(
+                Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
+                Matchers.anyInt());
+
+        checkPreparationPhasesForPackage(fallbackPackage, 1);
+
+
+        // Again enable primary package and verify primary is used and fallback becomes disabled
+        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());
+
+        checkPreparationPhasesForPackage(primaryPackage, 2);
+    }
+
+    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)));
+
+        // Change provider during relro creation to enter a state where we are
+        // waiting for relro creation to complete just to re-run relro creation.
+        // (so that in next notifyRelroCreationCompleted() call we have to list webview packages)
+        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_REPLACED);
+
+        // Ensure we use firstPackage
+        checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
+    }
+
+    /**
+     * Verify that even if a user-chosen package is removed temporarily we start using it again when
+     * it is added back.
+     */
+    public void testTempRemovePackageDoesntSwitchProviderPermanently() {
+        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)};
+        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
+
+        // Explicitly use the second package
+        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+        checkPreparationPhasesForPackage(secondPackage, 1 /* first time for this package */);
+
+        // Remove second package (invalidate it) and verify that first package is used
+        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+                    false /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
+                WebViewUpdateService.PACKAGE_ADDED);
+        checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */);
+
+        // Now make the second package valid again and verify that it is used again
+        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+                    true /* valid */));
+        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
+                WebViewUpdateService.PACKAGE_ADDED);
+        checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
+    }
+
+    /**
+     * Ensure that we update the user-chosen setting across boots if the chosen package is no
+     * longer installed and valid.
+     */
+    public void testProviderSettingChangedDuringBootIfProviderNotAvailable() {
+        String chosenPackage = "chosenPackage";
+        String nonChosenPackage = "non-chosenPackage";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(chosenPackage, "", true /* default available */,
+                    false /* fallback */, null),
+            new WebViewProviderInfo(nonChosenPackage, "", true /* default available */,
+                    false /* fallback */, null)};
+
+        setupWithPackages(packages);
+        // Only 'install' nonChosenPackage
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */));
+
+        // Set user-chosen package
+        mTestSystemImpl.updateUserSetting(null, chosenPackage);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+        // Verify that we switch the setting to point to the current package
+        Mockito.verify(mTestSystemImpl).updateUserSetting(
+                Mockito.anyObject(), Mockito.eq(nonChosenPackage));
+        assertEquals(nonChosenPackage, mTestSystemImpl.getUserChosenWebViewProvider(null));
+
+        checkPreparationPhasesForPackage(nonChosenPackage, 1);
+    }
+
+    public void testRecoverFailedListingWebViewPackagesSettingsChange() {
+        checkRecoverAfterFailListingWebviewPackages(true);
+    }
+
+    public void testRecoverFailedListingWebViewPackagesAddedPackage() {
+        checkRecoverAfterFailListingWebviewPackages(false);
+    }
+
+    /**
+     * Test that we can recover correctly from failing to list WebView packages.
+     * settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged
+     */
+    public void checkRecoverAfterFailListingWebviewPackages(boolean settingsChange) {
+        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)};
+        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
+
+        // Make both packages invalid so that we fail listing WebView packages
+        mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+                    false /* valid */));
+        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+                    false /* valid */));
+
+        // Change package to hit the webview packages listing problem.
+        if (settingsChange) {
+            mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+        } else {
+            mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
+                    WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+        }
+
+        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+
+        // Make second package valid and verify that we can load it again
+        mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+                    true /* valid */));
+
+        mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+
+
+        checkPreparationPhasesForPackage(secondPackage, 1);
+    }
+
+    public void testDontKillIfPackageReplaced() {
+        checkDontKillIfPackageRemoved(true);
+    }
+
+    public void testDontKillIfPackageRemoved() {
+        checkDontKillIfPackageRemoved(false);
+    }
+
+    public void checkDontKillIfPackageRemoved(boolean replaced) {
+        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)};
+        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
+
+        // Replace or remove the current webview package
+        if (replaced) {
+            mTestSystemImpl.setPackageInfo(
+                    createPackageInfo(firstPackage, true /* enabled */, false /* valid */));
+            mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
+                    WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+        } else {
+            mTestSystemImpl.removePackageInfo(firstPackage);
+            mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
+                    WebViewUpdateService.PACKAGE_REMOVED);
+        }
+
+        checkPreparationPhasesForPackage(secondPackage, 1);
+
+        Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
+                Mockito.anyObject());
+    }
+
+    public void testKillIfSettingChanged() {
+        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)};
+        checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
+
+        mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+
+        checkPreparationPhasesForPackage(secondPackage, 1);
+
+        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
+    }
+
+    /**
+     * Test that we kill apps using an old provider when we change the provider setting, even if the
+     * new provider is not the one we intended to change to.
+     */
+    public void testKillIfChangeProviderIncorrectly() {
+        String firstPackage = "first";
+        String secondPackage = "second";
+        String thirdPackage = "third";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(firstPackage, "", true /* default available */,
+                    false /* fallback */, null),
+            new WebViewProviderInfo(secondPackage, "", true /* default available */,
+                    false /* fallback */, null),
+            new WebViewProviderInfo(thirdPackage, "", true /* default available */,
+                    false /* fallback */, null)};
+        setupWithPackages(packages);
+        setEnabledAndValidPackageInfos(packages);
+
+        // Start with the setting pointing to the third package
+        mTestSystemImpl.updateUserSetting(null, thirdPackage);
+
+        mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+        checkPreparationPhasesForPackage(thirdPackage, 1);
+
+        mTestSystemImpl.setPackageInfo(
+                createPackageInfo(secondPackage, true /* enabled */, false /* valid */));
+
+        // Try to switch to the invalid second package, this should result in switching to the first
+        // package, since that is more preferred than the third one.
+        assertEquals(firstPackage,
+                mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage));
+
+        checkPreparationPhasesForPackage(firstPackage, 1);
+
+        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage));
+    }
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
new file mode 100644
index 0000000..701e058
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    mockito-target
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
new file mode 100644
index 0000000..d09b62c
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -0,0 +1,506 @@
+/*
+ * 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.pm.shortcutmanagertest;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.test.MoreAsserts;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.mockito.Mockito;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class ShortcutManagerTestUtils {
+    private static final String TAG = "ShortcutManagerUtils";
+
+    private static final boolean ENABLE_DUMPSYS = true; // DO NOT SUBMIT WITH true
+
+    private static final int STANDARD_TIMEOUT_SEC = 5;
+
+    private ShortcutManagerTestUtils() {
+    }
+
+    private static List<String> readAll(ParcelFileDescriptor pfd) {
+        try {
+            try {
+                final ArrayList<String> ret = new ArrayList<>();
+                try (BufferedReader r = new BufferedReader(
+                        new FileReader(pfd.getFileDescriptor()))) {
+                    String line;
+                    while ((line = r.readLine()) != null) {
+                        ret.add(line);
+                    }
+                    r.readLine();
+                }
+                return ret;
+            } finally {
+                pfd.close();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String concatResult(List<String> result) {
+        final StringBuilder sb = new StringBuilder();
+        for (String s : result) {
+            sb.append(s);
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+    private static List<String> runCommand(Instrumentation instrumentation, String command) {
+        return runCommand(instrumentation, command, null);
+    }
+    private static List<String> runCommand(Instrumentation instrumentation, String command,
+            Predicate<List<String>> resultAsserter) {
+        Log.d(TAG, "Running command: " + command);
+        final List<String> result;
+        try {
+            result = readAll(
+                    instrumentation.getUiAutomation().executeShellCommand(command));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        if (resultAsserter != null && !resultAsserter.test(result)) {
+            fail("Command '" + command + "' failed, output was:\n" + concatResult(result));
+        }
+        return result;
+    }
+
+    private static void runCommandForNoOutput(Instrumentation instrumentation, String command) {
+        runCommand(instrumentation, command, result -> result.size() == 0);
+    }
+
+    private static List<String> runShortcutCommand(Instrumentation instrumentation, String command,
+            Predicate<List<String>> resultAsserter) {
+        return runCommand(instrumentation, "cmd shortcut " + command, resultAsserter);
+    }
+
+    public static List<String> runShortcutCommandForSuccess(Instrumentation instrumentation,
+            String command) {
+        return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
+    }
+
+    public static String getDefaultLauncher(Instrumentation instrumentation) {
+        final String PREFIX = "Launcher: ComponentInfo{";
+        final String POSTFIX = "}";
+        final List<String> result = runShortcutCommandForSuccess(
+                instrumentation, "get-default-launcher");
+        for (String s : result) {
+            if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
+                return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+            }
+        }
+        fail("Default launcher not found");
+        return null;
+    }
+
+    public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
+        runCommandForNoOutput(instrumentation, "cmd package set-home-activity " + component);
+    }
+
+    public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
+        setDefaultLauncher(instrumentation, packageContext.getPackageName()
+                + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+    }
+
+    public static void overrideConfig(Instrumentation instrumentation, String config) {
+        runShortcutCommandForSuccess(instrumentation, "override-config " + config);
+    }
+
+    public static void resetConfig(Instrumentation instrumentation) {
+        runShortcutCommandForSuccess(instrumentation, "reset-config");
+    }
+
+    public static void resetThrottling(Instrumentation instrumentation) {
+        runShortcutCommandForSuccess(instrumentation, "reset-throttling");
+    }
+
+    public static void resetAllThrottling(Instrumentation instrumentation) {
+        runShortcutCommandForSuccess(instrumentation, "reset-all-throttling");
+    }
+
+    public static void clearShortcuts(Instrumentation instrumentation, int userId,
+            String packageName) {
+        runShortcutCommandForSuccess(instrumentation, "clear-shortcuts "
+                + " --user " + userId + " " + packageName);
+    }
+
+    public static void dumpsysShortcut(Instrumentation instrumentation) {
+        if (!ENABLE_DUMPSYS) {
+            return;
+        }
+        for (String s : runCommand(instrumentation, "dumpsys shortcut")) {
+            Log.e(TAG, s);
+        }
+    }
+
+    public static Bundle makeBundle(Object... keysAndValues) {
+        assertTrue((keysAndValues.length % 2) == 0);
+
+        if (keysAndValues.length == 0) {
+            return null;
+        }
+        final Bundle ret = new Bundle();
+
+        for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
+            final String key = keysAndValues[i].toString();
+            final Object value = keysAndValues[i + 1];
+
+            if (value == null) {
+                ret.putString(key, null);
+            } else if (value instanceof Integer) {
+                ret.putInt(key, (Integer) value);
+            } else if (value instanceof String) {
+                ret.putString(key, (String) value);
+            } else if (value instanceof Bundle) {
+                ret.putBundle(key, (Bundle) value);
+            } else {
+                fail("Type not supported yet: " + value.getClass().getName());
+            }
+        }
+        return ret;
+    }
+
+    public static <T> List<T> list(T... array) {
+        return Arrays.asList(array);
+    }
+
+    public static <T> Set<T> hashSet(Set<T> in) {
+        return new HashSet<T>(in);
+    }
+
+    public static <T> Set<T> set(T... values) {
+        return set(v -> v, values);
+    }
+
+    public static <T, V> Set<T> set(Function<V, T> converter, V... values) {
+        return set(converter, Arrays.asList(values));
+    }
+
+    public static <T, V> Set<T> set(Function<V, T> converter, List<V> values) {
+        final HashSet<T> ret = new HashSet<>();
+        for (V v : values) {
+            ret.add(converter.apply(v));
+        }
+        return ret;
+    }
+
+    public static void resetAll(Collection<?> mocks) {
+        for (Object o : mocks) {
+            reset(o);
+        }
+    }
+    public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+            String expectedExceptionMessageRegex, Runnable r) {
+        assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
+    }
+
+    public static void assertDynamicShortcutCountExceeded(Runnable r) {
+        assertExpectException(IllegalArgumentException.class,
+                "Max number of dynamic shortcuts exceeded", r);
+    }
+
+    public static void assertExpectException(String message,
+            Class<? extends Throwable> expectedExceptionType,
+            String expectedExceptionMessageRegex, Runnable r) {
+        try {
+            r.run();
+            Assert.fail("Expected exception type " + expectedExceptionType.getName()
+                    + " was not thrown (message=" + message + ")");
+        } catch (Throwable e) {
+            Assert.assertTrue(
+                    "Expected exception type was " + expectedExceptionType.getName()
+                            + " but caught " + e + " (message=" + message + ")",
+                    expectedExceptionType.isAssignableFrom(e.getClass()));
+            if (expectedExceptionMessageRegex != null) {
+                MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
+            }
+        }
+    }
+
+    public static List<ShortcutInfo> assertShortcutIds(List<ShortcutInfo> actualShortcuts,
+            String... expectedIds) {
+        final HashSet<String> expected = new HashSet<>(list(expectedIds));
+        final HashSet<String> actual = new HashSet<>();
+        for (ShortcutInfo s : actualShortcuts) {
+            actual.add(s.getId());
+        }
+
+        // Compare the sets.
+        assertEquals(expected, actual);
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllHaveIntents(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNotNull("ID " + s.getId(), s.getIntent());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllNotHaveIntents(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNull("ID " + s.getId(), s.getIntent());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllHaveTitle(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNotNull("ID " + s.getId(), s.getTitle());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllNotHaveTitle(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNull("ID " + s.getId(), s.getTitle());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllHaveIconResId(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
+            assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllHaveIconFile(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
+            assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllHaveIcon(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllKeyFieldsOnly(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllNotKeyFieldsOnly(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllDynamic(List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.isDynamic());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllPinned(List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.isPinned());
+        }
+        return actualShortcuts;
+    }
+
+    public static List<ShortcutInfo> assertAllDynamicOrPinned(
+            List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
+        }
+        return actualShortcuts;
+    }
+
+    public static void assertDynamicOnly(ShortcutInfo si) {
+        assertTrue(si.isDynamic());
+        assertFalse(si.isPinned());
+    }
+
+    public static void assertPinnedOnly(ShortcutInfo si) {
+        assertFalse(si.isDynamic());
+        assertTrue(si.isPinned());
+    }
+
+    public static void assertDynamicAndPinned(ShortcutInfo si) {
+        assertTrue(si.isDynamic());
+        assertTrue(si.isPinned());
+    }
+
+    public static void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) {
+        assertEquals("width", expectedWidth, bitmap.getWidth());
+        assertEquals("height", expectedHeight, bitmap.getHeight());
+    }
+
+    public static <T> void assertAllUnique(Collection<T> list) {
+        final Set<Object> set = new HashSet<>();
+        for (T item : list) {
+            if (set.contains(item)) {
+                fail("Duplicate item found: " + item + " (in the list: " + list + ")");
+            }
+            set.add(item);
+        }
+    }
+
+    public static Bitmap pfdToBitmap(ParcelFileDescriptor pfd) {
+        assertNotNull(pfd);
+        try {
+            try {
+                return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+            } finally {
+                pfd.close();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void assertBundleEmpty(BaseBundle b) {
+        assertTrue(b == null || b.size() == 0);
+    }
+
+    public static void assertCallbackNotReceived(LauncherApps.Callback mock) {
+        verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
+                any(UserHandle.class));
+    }
+
+    public static void assertCallbackReceived(LauncherApps.Callback mock,
+            UserHandle user, String packageName, String... ids) {
+        verify(mock).onShortcutsChanged(eq(packageName), checkShortcutIds(ids),
+                eq(user));
+    }
+
+    public static boolean checkAssertSuccess(Runnable r) {
+        try {
+            r.run();
+            return true;
+        } catch (AssertionError e) {
+            return false;
+        }
+    }
+
+    public static <T> T checkArgument(Predicate<T> checker, String description,
+            List<T> matchedCaptor) {
+        final Matcher<T> m = new BaseMatcher<T>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) {
+                    return false;
+                }
+                final T value = (T) item;
+                if (!checker.test(value)) {
+                    return false;
+                }
+
+                if (matchedCaptor != null) {
+                    matchedCaptor.add(value);
+                }
+                return true;
+            }
+
+            @Override
+            public void describeTo(Description d) {
+                d.appendText(description);
+            }
+        };
+        return Mockito.argThat(m);
+    }
+
+    public static List<ShortcutInfo> checkShortcutIds(String... ids) {
+        return checkArgument((List<ShortcutInfo> list) -> {
+            final Set<String> actualSet = set(si -> si.getId(), list);
+            return actualSet.equals(set(ids));
+
+        }, "Shortcut IDs=[" + Arrays.toString(ids) + "]", null);
+    }
+
+    public static void waitUntil(String message, BooleanSupplier condition) {
+        waitUntil(message, condition, STANDARD_TIMEOUT_SEC);
+    }
+
+    public static void waitUntil(String message, BooleanSupplier condition, int timeoutSeconds) {
+        final long timeout = System.currentTimeMillis() + (timeoutSeconds * 1000L);
+        while (System.currentTimeMillis() < timeout) {
+            if (condition.getAsBoolean()) {
+                return;
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        fail("Timed out for: " + message);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0aeb96f..ecfeff9 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -152,6 +152,7 @@
     private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
 
+    private boolean mHaveCarrierPrivilegedApps;
     private List<String> mCarrierPrivilegedApps;
 
     public UsageStatsService(Context context) {
@@ -931,11 +932,14 @@
 
     private boolean isCarrierApp(String packageName) {
         synchronized (mLock) {
-            if (mCarrierPrivilegedApps == null) {
+            if (!mHaveCarrierPrivilegedApps) {
                 fetchCarrierPrivilegedAppsLocked();
             }
+            if (mCarrierPrivilegedApps != null) {
+                return mCarrierPrivilegedApps.contains(packageName);
+            }
+            return false;
         }
-        return mCarrierPrivilegedApps.contains(packageName);
     }
 
     void clearCarrierPrivilegedApps() {
@@ -943,6 +947,7 @@
             Slog.i(TAG, "Clearing carrier privileged apps list");
         }
         synchronized (mLock) {
+            mHaveCarrierPrivilegedApps = false;
             mCarrierPrivilegedApps = null; // Need to be refetched.
         }
     }
@@ -951,6 +956,7 @@
         TelephonyManager telephonyManager =
                 getContext().getSystemService(TelephonyManager.class);
         mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+        mHaveCarrierPrivilegedApps = true;
         if (DEBUG) {
             Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
         }
@@ -1024,7 +1030,8 @@
             }
 
             pw.println();
-            pw.println("Carrier privileged apps: " + mCarrierPrivilegedApps);
+            pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+                    + "): " + mCarrierPrivilegedApps);
 
             pw.println();
             pw.println("Settings:");
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index f13e019..53377f9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -84,22 +84,15 @@
     private final PhoneStateListener mPhoneStateListener;
     private final PowerManager mPowerManager;
 
-    // The SoundTriggerManager layer handles multiple generic recognition models. We store the
-    // ModelData here in a hashmap.
-    private final HashMap<UUID, ModelData> mGenericModelDataMap;
+    // The SoundTriggerManager layer handles multiple recognition models of type generic and
+    // keyphrase. We store the ModelData here in a hashmap.
+    private final HashMap<UUID, ModelData> mModelDataMap;
 
-    // This ModelData instance ensures that the keyphrase sound model is a singleton and
-    // all other sound models are of type Generic. Any keyphrase sound model will be stored here
-    // and any previously running instances will be replaced. This restriction was earlier
-    // implemented by three instance variables which stored data about the keyphrase
-    // model. That data now gets encapsulated in this ModelData instance.
-    private ModelData mKeyphraseModelData;
-
-    // The keyphrase ID for keyphrase sound models. We store this specially here since ModelData
-    // does not support this.
-    // TODO: The role of the keyphrase ID is a bit unclear. Its just used to ensure that
-    // recognition events have the correct keyphrase ID check.
-    private int mKeyphraseId = INVALID_VALUE;
+    // An index of keyphrase sound models so that we can reach them easily. We support indexing
+    // keyphrase sound models with a keyphrase ID. Sound model with the same keyphrase ID will
+    // replace an existing model, thus there is a 1:1 mapping from keyphrase ID to a voice
+    // sound model.
+    private HashMap<Integer, UUID> mKeyphraseUuidMap;
 
     private boolean mCallActive = false;
     private boolean mIsPowerSaveMode = false;
@@ -119,7 +112,8 @@
         mContext = context;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mGenericModelDataMap = new HashMap<UUID, ModelData>();
+        mModelDataMap = new HashMap<UUID, ModelData>();
+        mKeyphraseUuidMap = new HashMap<Integer, UUID>();
         mPhoneStateListener = new MyCallStateListener();
         if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
             Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
@@ -153,6 +147,10 @@
 
         synchronized (mLock) {
             ModelData modelData = getOrCreateGenericModelDataLocked(modelId);
+            if (modelData == null) {
+                Slog.w(TAG, "Irrecoverable error occurred, check UUID / sound model data.");
+                return STATUS_ERROR;
+            }
             return startRecognition(soundModel, modelData, callback, recognitionConfig,
                     INVALID_VALUE /* keyphraseId */);
         }
@@ -180,20 +178,48 @@
                         + " soundModel=" + soundModel + ", callback=" + callback.asBinder()
                         + ", recognitionConfig=" + recognitionConfig);
                 Slog.d(TAG, "moduleProperties=" + mModuleProperties);
-                if (mKeyphraseModelData != null) {
-                    Slog.d(TAG, mKeyphraseModelData.toString());
-                } else {
-                    Slog.d(TAG, "Null KeyphraseModelData.");
+                dumpModelStateLocked();
+            }
+
+            ModelData model = getKeyphraseModelDataLocked(keyphraseId);
+            if (model != null && !model.isKeyphraseModel()) {
+                Slog.e(TAG, "Generic model with same UUID exists.");
+                return STATUS_ERROR;
+            }
+
+            // Process existing model first.
+            if (model != null && model.getModelId() != soundModel.uuid) {
+                // The existing model has a different UUID, should be replaced.
+                int status = cleanUpExistingKeyphraseModel(model);
+                removeKeyphraseModelLocked(keyphraseId);
+                if (status != STATUS_OK) {
+                    return status;
                 }
+                model = null;
             }
-            if (mKeyphraseModelData == null) {
-                mKeyphraseModelData = ModelData.createKeyphraseModelData(soundModel.uuid);
+
+            // We need to create a new one: either no previous models existed for given keyphrase id
+            // or the existing model had a different UUID and was cleaned up.
+            if (model == null) {
+                model = createKeyphraseModelDataLocked(soundModel.uuid, keyphraseId);
             }
-            return startRecognition(soundModel, mKeyphraseModelData, callback, recognitionConfig,
+
+            return startRecognition(soundModel, model, callback, recognitionConfig,
                     keyphraseId);
         }
     }
 
+    private int cleanUpExistingKeyphraseModel(ModelData modelData) {
+        // Stop and clean up a previous ModelData if one exists. This usually is used when the
+        // previous model has a different UUID for the same keyphrase ID.
+        int status = tryStopAndUnloadLocked(modelData, true /* stop */, true /* unload */);
+        if (status != STATUS_OK) {
+            Slog.w(TAG, "Unable to stop or unload previous model: " +
+                    modelData.toString());
+        }
+        return status;
+    }
+
     /**
      * Starts recognition for the given sound model. A single routine for both keyphrase and
      * generic sound models.
@@ -228,17 +254,15 @@
                 initializeTelephonyAndPowerStateListeners();
             }
 
-            // If the previous model is different (for the same UUID), ensure that its unloaded
-            // and stopped before proceeding. This works for both keyphrase and generic models.
-            // Specifically for keyphrase since we have 'mKeyphraseModelData' holding a single
-            // allowed instance of such a model, this ensures that a previously loaded (or started)
-            // keyphrase model is appropriately stopped. This ensures no regression with the
-            // previous version of this code as given in the startKeyphrase() routine.
-            //
-            // For generic sound models, all this means is that if we are given a different sound
-            // model with the same UUID, then we will "replace" it.
+            // If the existing SoundModel is different (for the same UUID for Generic and same
+            // keyphrase ID for voice), ensure that it is unloaded and stopped before proceeding.
+            // This works for both keyphrase and generic models. This logic also ensures that a
+            // previously loaded (or started) model is appropriately stopped. Since this is a
+            // generalization of the previous logic with a single keyphrase model, we should have
+            // no regression with the previous version of this code as was given in the
+            // startKeyphrase() routine.
             if (modelData.getSoundModel() != null) {
-                boolean stopModel = false; // Stop the model after checking that its started.
+                boolean stopModel = false; // Stop the model after checking that it is started.
                 boolean unloadModel = false;
                 if (modelData.getSoundModel().equals(soundModel) && modelData.isModelStarted()) {
                     // The model has not changed, but the previous model is "started".
@@ -273,7 +297,7 @@
                 modelData.clearCallback();
             }
 
-            // Load the model if its not loaded.
+            // Load the model if it is not loaded.
             if (!modelData.isModelLoaded()) {
                 // Load the model
                 int[] handle = new int[] { INVALID_VALUE };
@@ -291,9 +315,6 @@
                 Slog.d(TAG, "Sound model loaded with handle:" + handle[0]);
             }
             modelData.setCallback(callback);
-            if (modelData.isKeyphraseModel()) {
-                mKeyphraseId = keyphraseId;
-            }
             modelData.setRequested(true);
             modelData.setRecognitionConfig(recognitionConfig);
             modelData.setSoundModel(soundModel);
@@ -322,8 +343,8 @@
                 return STATUS_ERROR;
             }
 
-            ModelData modelData = mGenericModelDataMap.get(modelId);
-            if (modelData == null) {
+            ModelData modelData = mModelDataMap.get(modelId);
+            if (modelData == null || !modelData.isGenericModel()) {
                 Slog.w(TAG, "Attempting stopRecognition on invalid model with id:" + modelId);
                 return STATUS_ERROR;
             }
@@ -355,21 +376,23 @@
                 return STATUS_ERROR;
             }
 
+            ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
+            if (modelData == null || !modelData.isKeyphraseModel()) {
+                Slog.e(TAG, "No model exists for given keyphrase Id.");
+                return STATUS_ERROR;
+            }
+
             if (DBG) {
                 Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" +
                         callback.asBinder());
-                Slog.d(TAG, "current callback=" + (mKeyphraseModelData == null ? "null" :
-                            mKeyphraseModelData.getCallback().asBinder()));
+                Slog.d(TAG, "current callback=" + (modelData == null ? "null" :
+                            modelData.getCallback().asBinder()));
             }
-            int status = stopRecognition(mKeyphraseModelData, callback);
+            int status = stopRecognition(modelData, callback);
             if (status != SoundTrigger.STATUS_OK) {
                 return status;
             }
 
-            // We leave the sound model loaded but not started, this helps us when we start
-            // back.
-            // Also clear the internal state once the recognition has been stopped.
-            internalClearKeyphraseStateLocked();
             return status;
         }
     }
@@ -424,9 +447,6 @@
                 internalClearGlobalStateLocked();
             }
 
-            if (modelData.isKeyphraseModel()) {
-                mKeyphraseId = INVALID_VALUE;
-            }
             return status;
         }
     }
@@ -475,25 +495,18 @@
                 return;
             }
 
-            // Stop Keyphrase recognition if one exists.
-            if (mKeyphraseModelData != null && mKeyphraseModelData.getHandle() != INVALID_VALUE) {
-                mKeyphraseModelData.setRequested(false);
-                int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
-                        false /* don't notify for synchronous calls */);
-                internalClearKeyphraseStateLocked();
-            }
-
-            // Stop all generic recognition models.
-            for (ModelData model : mGenericModelDataMap.values()) {
+            // Stop all recognition models.
+            for (ModelData model : mModelDataMap.values()) {
                 if (model.isModelStarted()) {
+                    model.setRequested(false);
                     int status = stopRecognitionLocked(model,
                             false /* do not notify for synchronous calls */);
                     if (status != STATUS_OK) {
-                        // What else can we do if there is an error here.
-                        Slog.w(TAG, "Error stopping generic model: " + model.getHandle());
+                        Slog.w(TAG, "Error stopping keyphrase model: " + model.getHandle());
                     }
                     model.clearState();
                     model.clearCallback();
+                    model.setRecognitionConfig(null);
                 }
             }
             internalClearGlobalStateLocked();
@@ -507,24 +520,27 @@
     int unloadKeyphraseSoundModel(int keyphraseId) {
         synchronized (mLock) {
             MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1);
-            if (mModule == null || mKeyphraseModelData == null ||
-                    mKeyphraseModelData.getHandle() == INVALID_VALUE) {
+            ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
+            if (mModule == null || modelData == null || modelData.getHandle() == INVALID_VALUE ||
+                    !modelData.isKeyphraseModel()) {
                 return STATUS_ERROR;
             }
 
             // Stop recognition if it's the current one.
-            mKeyphraseModelData.setRequested(false);
-            int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
+            modelData.setRequested(false);
+            int status = updateRecognitionLocked(modelData, isRecognitionAllowed(),
                     false /* don't notify */);
             if (status != SoundTrigger.STATUS_OK) {
                 Slog.w(TAG, "Stop recognition failed for keyphrase ID:" + status);
             }
 
-            status = mModule.unloadSoundModel(mKeyphraseModelData.getHandle());
+            status = mModule.unloadSoundModel(modelData.getHandle());
             if (status != SoundTrigger.STATUS_OK) {
                 Slog.w(TAG, "unloadKeyphraseSoundModel call failed with " + status);
             }
-            mKeyphraseModelData.clearState();
+
+            // Remove it from existence.
+            removeKeyphraseModelLocked(keyphraseId);
             return status;
         }
     }
@@ -535,8 +551,8 @@
             if (modelId == null || mModule == null) {
                 return STATUS_ERROR;
             }
-            ModelData modelData = mGenericModelDataMap.get(modelId);
-            if (modelData == null) {
+            ModelData modelData = mModelDataMap.get(modelId);
+            if (modelData == null || !modelData.isGenericModel()) {
                 Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" +
                         modelId);
                 return STATUS_ERROR;
@@ -559,8 +575,10 @@
                 Slog.w(TAG, "unloadGenericSoundModel() call failed with " + status);
                 Slog.w(TAG, "unloadGenericSoundModel() force-marking model as unloaded.");
             }
-            mGenericModelDataMap.remove(modelId);
-            if (DBG) dumpGenericModelStateLocked();
+
+            // Remove it from existence.
+            mModelDataMap.remove(modelId);
+            if (DBG) dumpModelStateLocked();
             return status;
         }
     }
@@ -612,7 +630,7 @@
             return;
         }
         ModelData model = getModelDataForLocked(event.soundModelHandle);
-        if (model == null) {
+        if (model == null || !model.isGenericModel()) {
             Slog.w(TAG, "Generic recognition event: Model does not exist for handle: " +
                     event.soundModelHandle);
             return;
@@ -723,67 +741,64 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onError", e);
         } finally {
-            internalClearKeyphraseStateLocked();
-            internalClearGenericModelStateLocked();
+            internalClearModelStateLocked();
             internalClearGlobalStateLocked();
         }
     }
 
+    private int getKeyphraseIdFromEvent(KeyphraseRecognitionEvent event) {
+        if (event == null) {
+            Slog.w(TAG, "Null RecognitionEvent received.");
+            return INVALID_VALUE;
+        }
+        KeyphraseRecognitionExtra[] keyphraseExtras =
+                ((KeyphraseRecognitionEvent) event).keyphraseExtras;
+        if (keyphraseExtras == null || keyphraseExtras.length == 0) {
+            Slog.w(TAG, "Invalid keyphrase recognition event!");
+            return INVALID_VALUE;
+        }
+        // TODO: Handle more than one keyphrase extras.
+        return keyphraseExtras[0].id;
+    }
+
     private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) {
         Slog.i(TAG, "Recognition success");
         MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1);
+        int keyphraseId = getKeyphraseIdFromEvent(event);
+        ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
 
-        if (mKeyphraseModelData == null) {
-            Slog.e(TAG, "Received onRecognition event for null keyphrase model data.");
+        if (modelData == null || !modelData.isKeyphraseModel()) {
+            Slog.e(TAG, "Keyphase model data does not exist for ID:" + keyphraseId);
             return;
         }
 
-        if (mKeyphraseModelData.getCallback() == null) {
-            Slog.w(TAG, "Received onRecognition event without any listener for it.");
-            return;
-        }
-
-        KeyphraseRecognitionExtra[] keyphraseExtras =
-                ((KeyphraseRecognitionEvent) event).keyphraseExtras;
-        if (keyphraseExtras == null || keyphraseExtras.length == 0) {
-            Slog.w(TAG, "Invalid keyphrase recognition event!");
-            return;
-        }
-        // TODO: Handle more than one keyphrase extras.
-        if (mKeyphraseId != keyphraseExtras[0].id) {
-            Slog.w(TAG, "received onRecognition event for a different keyphrase");
+        if (modelData.getCallback() == null) {
+            Slog.w(TAG, "Received onRecognition event without callback for keyphrase model.");
             return;
         }
 
         try {
-            mKeyphraseModelData.getCallback().onKeyphraseDetected(
-                    (KeyphraseRecognitionEvent) event);
+            modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onKeyphraseDetected", e);
         }
 
-        mKeyphraseModelData.setStopped();
+        modelData.setStopped();
 
-        RecognitionConfig config = mKeyphraseModelData.getRecognitionConfig();
+        RecognitionConfig config = modelData.getRecognitionConfig();
         if (config != null) {
             // Whether we should continue by starting this again.
-            mKeyphraseModelData.setRequested(config.allowMultipleTriggers);
+            modelData.setRequested(config.allowMultipleTriggers);
         }
         // TODO: Remove this block if the lower layer supports multiple triggers.
-        if (mKeyphraseModelData.getRequested()) {
-            updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
-                true /* notify */);
+        if (modelData.getRequested()) {
+            updateRecognitionLocked(modelData, isRecognitionAllowed(), true /* notify */);
         }
     }
 
     private void updateAllRecognitionsLocked(boolean notify) {
         boolean isAllowed = isRecognitionAllowed();
-        // Keyphrase model.
-        if (mKeyphraseModelData != null) {
-            updateRecognitionLocked(mKeyphraseModelData, isAllowed, notify);
-        }
-        for (UUID modelId : mGenericModelDataMap.keySet()) {
-            ModelData modelData = mGenericModelDataMap.get(modelId);
+        for (ModelData modelData : mModelDataMap.values()) {
             updateRecognitionLocked(modelData, isAllowed, notify);
         }
     }
@@ -809,11 +824,7 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onError", e);
         } finally {
-            if (mKeyphraseModelData != null) {
-                mKeyphraseModelData.clearState();
-            }
-            internalClearKeyphraseStateLocked();
-            internalClearGenericModelStateLocked();
+            internalClearModelStateLocked();
             internalClearGlobalStateLocked();
             if (mModule != null) {
                 mModule.detach();
@@ -822,10 +833,7 @@
         }
     }
 
-    // internalClearGlobalStateLocked() gets split into two routines. Cleanup that is
-    // specific to keyphrase sound models named as internalClearKeyphraseStateLocked() and
-    // internalClearGlobalStateLocked() for global state. The global cleanup routine will be used
-    // by the cleanup happening with the generic sound models.
+    // internalClearGlobalStateLocked() cleans up the telephony and power save listeners.
     private void internalClearGlobalStateLocked() {
         // Unregister from call state changes.
         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
@@ -837,20 +845,9 @@
         }
     }
 
-    private void internalClearKeyphraseStateLocked() {
-        if (mKeyphraseModelData != null) {
-            mKeyphraseModelData.setStopped();
-            mKeyphraseModelData.setRequested(false);
-            mKeyphraseModelData.setRecognitionConfig(null);
-            mKeyphraseModelData.setCallback(null);
-        }
-
-        mKeyphraseId = INVALID_VALUE;
-    }
-
-    private void internalClearGenericModelStateLocked() {
-        for (UUID modelId : mGenericModelDataMap.keySet()) {
-            ModelData modelData = mGenericModelDataMap.get(modelId);
+    // Clears state for all models (generic and keyphrase).
+    private void internalClearModelStateLocked() {
+        for (ModelData modelData : mModelDataMap.values()) {
             modelData.clearState();
             modelData.clearCallback();
         }
@@ -884,14 +881,10 @@
         synchronized (mLock) {
             pw.print("  module properties=");
             pw.println(mModuleProperties == null ? "null" : mModuleProperties);
-            pw.print("  keyphrase ID="); pw.println(mKeyphraseId);
 
             pw.print("  call active="); pw.println(mCallActive);
             pw.print("  power save mode active="); pw.println(mIsPowerSaveMode);
             pw.print("  service disabled="); pw.println(mServiceDisabled);
-            if (mKeyphraseModelData != null) {
-                pw.println(mKeyphraseModelData.toString());
-            }
         }
     }
 
@@ -914,34 +907,60 @@
 
     // Sends an error callback to all models with a valid registered callback.
     private void sendErrorCallbacksToAll(int errorCode) throws RemoteException {
-        IRecognitionStatusCallback keyphraseListener = mKeyphraseModelData.getCallback();
-        if (keyphraseListener != null) {
-            keyphraseListener.onError(STATUS_ERROR);
-        }
-        for (UUID modelId: mGenericModelDataMap.keySet()) {
-            ModelData modelData = mGenericModelDataMap.get(modelId);
-            IRecognitionStatusCallback keyphraseCallback = mKeyphraseModelData.getCallback();
-            if (keyphraseCallback != null) {
-                keyphraseCallback.onError(STATUS_ERROR);
+        for (ModelData modelData : mModelDataMap.values()) {
+            IRecognitionStatusCallback callback = modelData.getCallback();
+            if (callback != null) {
+                callback.onError(STATUS_ERROR);
             }
         }
     }
 
     private ModelData getOrCreateGenericModelDataLocked(UUID modelId) {
-        ModelData modelData = mGenericModelDataMap.get(modelId);
+        ModelData modelData = mModelDataMap.get(modelId);
         if (modelData == null) {
             modelData = ModelData.createGenericModelData(modelId);
-            mGenericModelDataMap.put(modelId, modelData);
+            mModelDataMap.put(modelId, modelData);
+        } else if (!modelData.isGenericModel()) {
+            Slog.e(TAG, "UUID already used for non-generic model.");
+            return null;
         }
         return modelData;
     }
 
+    private void removeKeyphraseModelLocked(int keyphraseId) {
+        UUID uuid = mKeyphraseUuidMap.get(keyphraseId);
+        if (uuid == null) {
+            return;
+        }
+        mModelDataMap.remove(uuid);
+        mKeyphraseUuidMap.remove(keyphraseId);
+    }
+
+    private ModelData getKeyphraseModelDataLocked(int keyphraseId) {
+        UUID uuid = mKeyphraseUuidMap.get(keyphraseId);
+        if (uuid == null) {
+            return null;
+        }
+        return mModelDataMap.get(uuid);
+    }
+
+    // Use this to create a new ModelData entry for a keyphrase Id. It will overwrite existing
+    // mapping if one exists.
+    private ModelData createKeyphraseModelDataLocked(UUID modelId, int keyphraseId) {
+        mKeyphraseUuidMap.remove(keyphraseId);
+        mModelDataMap.remove(modelId);
+        mKeyphraseUuidMap.put(keyphraseId, modelId);
+        ModelData modelData = ModelData.createKeyphraseModelData(modelId);
+        mModelDataMap.put(modelId, modelData);
+        return modelData;
+    }
+
     // Instead of maintaining a second hashmap of modelHandle -> ModelData, we just
     // iterate through to find the right object (since we don't expect 100s of models
     // to be stored).
     private ModelData getModelDataForLocked(int modelHandle) {
         // Fetch ModelData object corresponding to the model handle.
-        for (ModelData model : mGenericModelDataMap.values()) {
+        for (ModelData model : mModelDataMap.values()) {
             if (model.getHandle() == modelHandle) {
                 return model;
             }
@@ -1051,9 +1070,9 @@
         return status;
     }
 
-    private void dumpGenericModelStateLocked() {
-        for (UUID modelId : mGenericModelDataMap.keySet()) {
-            ModelData modelData = mGenericModelDataMap.get(modelId);
+    private void dumpModelStateLocked() {
+        for (UUID modelId : mModelDataMap.keySet()) {
+            ModelData modelData = mModelDataMap.get(modelId);
             Slog.i(TAG, "Model :" + modelData.toString());
         }
     }
@@ -1065,14 +1084,7 @@
             mRecognitionRunning = false;
             return mRecognitionRunning;
         }
-        if (mKeyphraseModelData != null && mKeyphraseModelData.getCallback() != null &&
-                mKeyphraseModelData.isModelStarted() &&
-            mKeyphraseModelData.getHandle() != INVALID_VALUE) {
-            mRecognitionRunning = true;
-            return mRecognitionRunning;
-        }
-        for (UUID modelId : mGenericModelDataMap.keySet()) {
-            ModelData modelData = mGenericModelDataMap.get(modelId);
+        for (ModelData modelData : mModelDataMap.values()) {
             if (modelData.isModelStarted()) {
                 mRecognitionRunning = true;
                 return mRecognitionRunning;
@@ -1233,6 +1245,10 @@
             return mModelType == SoundModel.TYPE_KEYPHRASE;
         }
 
+        synchronized boolean isGenericModel() {
+            return mModelType == SoundModel.TYPE_GENERIC_SOUND;
+        }
+
         synchronized String stateToString() {
             switch(mModelState) {
                 case MODEL_NOTLOADED: return "NOT_LOADED";
@@ -1259,7 +1275,17 @@
                     "ModelState: " + stateToString() + "\n" +
                     requestedToString() + "\n" +
                     callbackToString() + "\n" +
-                    uuidToString();
+                    uuidToString() + "\n" + modelTypeToString();
+        }
+
+        synchronized String modelTypeToString() {
+            String type = null;
+            switch (mModelType) {
+                case SoundModel.TYPE_GENERIC_SOUND: type = "Generic"; break;
+                case SoundModel.TYPE_UNKNOWN: type = "Unknown"; break;
+                case SoundModel.TYPE_KEYPHRASE: type = "Keyphrase"; break;
+            }
+            return "Model type: " + type + "\n";
         }
     }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 1544723..1e9db18 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -17,6 +17,7 @@
 package com.android.server.voiceinteraction;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
@@ -42,9 +43,11 @@
 
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
+import com.android.server.LocalServices;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
     final static String TAG = "VoiceInteractionServiceManager";
@@ -148,8 +151,14 @@
             mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
                     mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid, mHandler);
         }
+        List<IBinder> activityTokens = null;
+        if (activityToken == null) {
+            // Let's get top activities from all visible stacks
+            activityTokens = LocalServices.getService(ActivityManagerInternal.class)
+                    .getTopVisibleActivities();
+        }
         return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
-                activityToken);
+                activityToken, activityTokens);
     }
 
     public boolean hideSessionLocked() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index e04f312..0922a12 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -45,6 +45,7 @@
 import android.util.Slog;
 import android.view.IWindowManager;
 import android.view.WindowManager;
+
 import com.android.internal.app.IAssistScreenshotReceiver;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
@@ -55,10 +56,15 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 final class VoiceInteractionSessionConnection implements ServiceConnection {
+
     final static String TAG = "VoiceInteractionServiceManager";
 
+    private static final String KEY_RECEIVER_EXTRA_COUNT = "count";
+    private static final String KEY_RECEIVER_EXTRA_INDEX = "index";
+
     final IBinder mToken = new Binder();
     final Object mLock;
     final ComponentName mSessionComponentName;
@@ -82,11 +88,27 @@
     IVoiceInteractionSession mSession;
     IVoiceInteractor mInteractor;
     boolean mHaveAssistData;
-    Bundle mAssistData;
+    int mPendingAssistDataCount;
+    ArrayList<AssistDataForActivity> mAssistData = new ArrayList<>();
     boolean mHaveScreenshot;
     Bitmap mScreenshot;
     ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
 
+    static class AssistDataForActivity {
+        int activityIndex;
+        int activityCount;
+        Bundle data;
+
+        public AssistDataForActivity(Bundle data) {
+            this.data = data;
+            Bundle receiverExtras = data.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
+            if (receiverExtras != null) {
+                activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
+                activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
+            }
+        }
+    }
+
     IVoiceInteractionSessionShowCallback mShowCallback =
             new IVoiceInteractionSessionShowCallback.Stub() {
         @Override
@@ -125,7 +147,7 @@
             synchronized (mLock) {
                 if (mShown) {
                     mHaveAssistData = true;
-                    mAssistData = resultData;
+                    mAssistData.add(new AssistDataForActivity(resultData));
                     deliverSessionDataLocked();
                 }
             }
@@ -198,7 +220,8 @@
     }
 
     public boolean showLocked(Bundle args, int flags, int disabledContext,
-            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken,
+            List<IBinder> topActivities) {
         if (mBound) {
             if (!mFullyBound) {
                 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -220,30 +243,41 @@
             mShowArgs = args;
             mShowFlags = flags;
             mHaveAssistData = false;
+            mPendingAssistDataCount = 0;
             boolean needDisclosure = false;
             if ((flags&VoiceInteractionSession.SHOW_WITH_ASSIST) != 0) {
                 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
                         mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
                         && structureEnabled) {
-                    try {
-                        MetricsLogger.count(mContext, "assist_with_context", 1);
-                        if (mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
-                                mAssistReceiver, activityToken)) {
-                            needDisclosure = true;
-                        } else {
-                            // Wasn't allowed...  given that, let's not do the screenshot either.
-                            mHaveAssistData = true;
-                            mAssistData = null;
-                            screenshotEnabled = false;
+                    mAssistData.clear();
+                    final int count = activityToken != null ? 1 : topActivities.size();
+                    for (int i = 0; i < count; i++) {
+                        IBinder topActivity = count == 1 ? activityToken : topActivities.get(i);
+                        try {
+                            MetricsLogger.count(mContext, "assist_with_context", 1);
+                            Bundle receiverExtras = new Bundle();
+                            receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
+                            receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, count);
+                            if (mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
+                                    mAssistReceiver, receiverExtras, topActivity, i == 0)) {
+                                needDisclosure = true;
+                                mPendingAssistDataCount++;
+                            } else if (i == 0) {
+                                // Wasn't allowed... given that, let's not do the screenshot either.
+                                mHaveAssistData = true;
+                                mAssistData.clear();
+                                screenshotEnabled = false;
+                                break;
+                            }
+                        } catch (RemoteException e) {
                         }
-                    } catch (RemoteException e) {
                     }
                 } else {
                     mHaveAssistData = true;
-                    mAssistData = null;
+                    mAssistData.clear();
                 }
             } else {
-                mAssistData = null;
+                mAssistData.clear();
             }
             mHaveScreenshot = false;
             if ((flags&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0) {
@@ -335,41 +369,26 @@
             return;
         }
         if (mHaveAssistData) {
-            Bundle assistData;
-            AssistStructure structure;
-            AssistContent content;
-            if (mAssistData != null) {
-                assistData = mAssistData.getBundle("data");
-                structure = mAssistData.getParcelable("structure");
-                content = mAssistData.getParcelable("content");
-                int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1);
-                if (uid >= 0 && content != null) {
-                    Intent intent = content.getIntent();
-                    if (intent != null) {
-                        ClipData data = intent.getClipData();
-                        if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
-                            grantClipDataPermissions(data, intent.getFlags(), uid,
-                                    mCallingUid, mSessionComponentName.getPackageName());
-                        }
-                    }
-                    ClipData data = content.getClipData();
-                    if (data != null) {
-                        grantClipDataPermissions(data,
-                                Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                                uid, mCallingUid, mSessionComponentName.getPackageName());
-                    }
+            AssistDataForActivity assistData;
+            while (!mAssistData.isEmpty()) {
+                if (mPendingAssistDataCount <= 0) {
+                    Slog.e(TAG, "mPendingAssistDataCount is " + mPendingAssistDataCount);
                 }
-            } else {
-                assistData = null;
-                structure = null;
-                content = null;
+                mPendingAssistDataCount--;
+                assistData = mAssistData.remove(0);
+                if (assistData.data == null) {
+                    try {
+                        mSession.handleAssist(null, null, null, assistData.activityIndex,
+                                assistData.activityCount);
+                    } catch (RemoteException e) {
+                    }
+                } else {
+                    deliverSessionDataLocked(assistData);
+                }
             }
-            try {
-                mSession.handleAssist(assistData, structure, content);
-            } catch (RemoteException e) {
-            }
-            mAssistData = null;
-            mHaveAssistData = false;
+            if (mPendingAssistDataCount <= 0) {
+                mHaveAssistData = false;
+            } // else, more to come
         }
         if (mHaveScreenshot) {
             try {
@@ -381,6 +400,37 @@
         }
     }
 
+    private void deliverSessionDataLocked(AssistDataForActivity assistDataForActivity) {
+        Bundle assistData = assistDataForActivity.data.getBundle(
+                VoiceInteractionSession.KEY_DATA);
+        AssistStructure structure = assistDataForActivity.data.getParcelable(
+                VoiceInteractionSession.KEY_STRUCTURE);
+        AssistContent content = assistDataForActivity.data.getParcelable(
+                VoiceInteractionSession.KEY_CONTENT);
+        int uid = assistDataForActivity.data.getInt(Intent.EXTRA_ASSIST_UID, -1);
+        if (uid >= 0 && content != null) {
+            Intent intent = content.getIntent();
+            if (intent != null) {
+                ClipData data = intent.getClipData();
+                if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
+                    grantClipDataPermissions(data, intent.getFlags(), uid,
+                            mCallingUid, mSessionComponentName.getPackageName());
+                }
+            }
+            ClipData data = content.getClipData();
+            if (data != null) {
+                grantClipDataPermissions(data,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                        uid, mCallingUid, mSessionComponentName.getPackageName());
+            }
+        }
+        try {
+            mSession.handleAssist(assistData, structure, content,
+                    assistDataForActivity.activityIndex, assistDataForActivity.activityCount);
+        } catch (RemoteException e) {
+        }
+    }
+
     public boolean hideLocked() {
         if (mBound) {
             if (mShown) {
@@ -388,7 +438,7 @@
                 mShowArgs = null;
                 mShowFlags = 0;
                 mHaveAssistData = false;
-                mAssistData = null;
+                mAssistData.clear();
                 if (mSession != null) {
                     try {
                         mSession.hide();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index adc7c21..26730de 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. */
 
     /**
@@ -541,6 +548,27 @@
     public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
 
     /**
+     * List operator-specific error codes and indices of corresponding error strings in
+     * wfcOperatorErrorAlertMessages and wfcOperatorErrorNotificationMessages.
+     *
+     * Example: "REG09|0" specifies error code "REG09" and index "0". This index will be
+     * used to find alert and notification messages in wfcOperatorErrorAlertMessages and
+     * wfcOperatorErrorNotificationMessages.
+     *
+     * @hide
+     */
+    public static final String KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY =
+            "wfc_operator_error_codes_string_array";
+
+    /**
+     * Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats.
+     * @hide
+     */
+    public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
+    /** @hide */
+    public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
+
+    /**
      * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
      * prevent manual operator selection. If false, this SIM setting will be ignored and manual
      * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -686,8 +714,8 @@
         sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
         sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
         sDefaults.putString(KEY_VVM_TYPE_STRING, "");
-        sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN,false);
-        sDefaults.putBoolean(KEY_VVM_PREFETCH_BOOLEAN,true);
+        sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN, false);
+        sDefaults.putBoolean(KEY_VVM_PREFETCH_BOOLEAN, true);
         sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
@@ -705,6 +733,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"});
@@ -723,6 +752,9 @@
         sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL, false);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
+        sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
+        sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
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/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
new file mode 100644
index 0000000..ca7354f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -0,0 +1,234 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities for handling carrier applications.
+ * @hide
+ */
+public final class CarrierAppUtils {
+    private static final String TAG = "CarrierAppUtils";
+
+    private static final boolean DEBUG = false; // STOPSHIP if true
+
+    private CarrierAppUtils() {}
+
+    /**
+     * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
+     *
+     * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
+     * want to disable each such application which is present on the system image until the user
+     * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
+     * without interfering with the user if they opt to enable/disable the app explicitly.
+     *
+     * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
+     * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
+     * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
+     *
+     * When enabling a carrier app we also grant it default permissions.
+     *
+     * This method is idempotent and is safe to be called at any time; it should be called once at
+     * system startup prior to any application running, as well as any time the set of carrier
+     * privileged apps may have changed.
+     */
+    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+        }
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
+                systemCarrierAppsDisabledUntilUsed);
+    }
+
+    /**
+     * Like {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager,
+     * int)}, but assumes that no carrier apps have carrier privileges.
+     *
+     * This prevents a potential race condition on first boot - since the app's default state is
+     * enabled, we will initially disable it when the telephony stack is first initialized as it has
+     * not yet read the carrier privilege rules. However, since telephony is initialized later on
+     * late in boot, the app being disabled may have already been started in response to certain
+     * broadcasts. The app will continue to run (briefly) after being disabled, before the Package
+     * Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
+     */
+    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+        }
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
+                null /* telephonyManager */, userId, systemCarrierAppsDisabledUntilUsed);
+    }
+
+    // Must be public b/c framework unit tests can't access package-private methods.
+    @VisibleForTesting
+    public static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId,
+            String[] systemCarrierAppsDisabledUntilUsed) {
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
+                userId, systemCarrierAppsDisabledUntilUsed);
+        if (candidates == null || candidates.isEmpty()) {
+            return;
+        }
+
+        List<String> enabledCarrierPackages = new ArrayList<>();
+
+        try {
+            for (ApplicationInfo ai : candidates) {
+                String packageName = ai.packageName;
+                boolean hasPrivileges = telephonyManager != null &&
+                        telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+                                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+
+                // Only update enabled state for the app on /system. Once it has been updated we
+                // shouldn't touch it.
+                if (!ai.isUpdatedSystemApp()) {
+                    if (hasPrivileges
+                            && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            || ai.enabledSetting ==
+                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+                        Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+                                + userId);
+                        packageManager.setApplicationEnabledSetting(packageName,
+                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                                PackageManager.DONT_KILL_APP, userId, callingPackage);
+                    } else if (!hasPrivileges
+                            && ai.enabledSetting ==
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                        Slog.i(TAG, "Update state(" + packageName
+                                + "): DISABLED_UNTIL_USED for user " + userId);
+                        packageManager.setApplicationEnabledSetting(packageName,
+                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+                                userId, callingPackage);
+                    }
+                }
+
+                // Always re-grant default permissions to carrier apps w/ privileges.
+                if (hasPrivileges) {
+                    enabledCarrierPackages.add(ai.packageName);
+                }
+            }
+
+            if (!enabledCarrierPackages.isEmpty()) {
+                // Since we enabled at least one app, ensure we grant default permissions to those
+                // apps.
+                String[] packageNames = new String[enabledCarrierPackages.size()];
+                enabledCarrierPackages.toArray(packageNames);
+                packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+    }
+
+    /**
+     * Returns the list of "default" carrier apps.
+     *
+     * This is the subset of apps returned by
+     * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
+     * privileges per the SIM(s) inserted in the device.
+     */
+    public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
+            TelephonyManager telephonyManager, int userId) {
+        // Get all system apps from the default list.
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
+        if (candidates == null || candidates.isEmpty()) {
+            return null;
+        }
+
+        // Filter out apps without carrier privileges.
+        // Iterate from the end to avoid creating an Iterator object and because we will be removing
+        // elements from the list as we pass through it.
+        for (int i = candidates.size() - 1; i >= 0; i--) {
+            ApplicationInfo ai = candidates.get(i);
+            String packageName = ai.packageName;
+            boolean hasPrivileges =
+                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+                            TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+            if (!hasPrivileges) {
+                candidates.remove(i);
+            }
+        }
+
+        return candidates;
+    }
+
+    /**
+     * Returns the list of "default" carrier app candidates.
+     *
+     * These are the apps subject to the hiding/showing logic in
+     * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
+     * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
+     * when a matching SIM is inserted.
+     *
+     * Whether or not the app is actually considered a default app depends on whether the app has
+     * carrier privileges as determined by the SIMs in the device.
+     */
+    public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
+            IPackageManager packageManager, int userId) {
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
+                systemCarrierAppsDisabledUntilUsed);
+    }
+
+    private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
+            IPackageManager packageManager, int userId,
+            String[] systemCarrierAppsDisabledUntilUsed) {
+        if (systemCarrierAppsDisabledUntilUsed == null
+                || systemCarrierAppsDisabledUntilUsed.length == 0) {
+            return null;
+        }
+        List<ApplicationInfo> apps = null;
+        try {
+            apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
+            for (String packageName : systemCarrierAppsDisabledUntilUsed) {
+                ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
+                if (ai == null) {
+                    // No app found for packageName
+                    continue;
+                }
+                if (!ai.isSystemApp()) {
+                    continue;
+                }
+                apps.add(ai);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+        return apps;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index fcb967f..ccabace 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -102,6 +102,7 @@
     public static final int CMD_NET_STAT_POLL = BASE + 40;
     public static final int EVENT_DATA_RAT_CHANGED = BASE + 41;
     public static final int CMD_CLEAR_PROVISIONING_SPINNER = BASE + 42;
+    public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 43;
 
     /***** Constants *****/
 
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/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 8aa0e34..cfc5305 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -33,8 +33,6 @@
     // From the top of ril.cpp
     int RIL_ERRNO_INVALID_RESPONSE = -1;
 
-    int MAX_INT = 0x7FFFFFFF;
-
     // from RIL_Errno
     int SUCCESS = 0;
     int RADIO_NOT_AVAILABLE = 1;              /* If radio did not start or is resetting */
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/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 702be49..06949a0 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -60,29 +60,22 @@
         android:onClick="onUnEnrollButtonClicked"
         android:padding="20dp" />
 
+    <Button
+        android:id="@+id/play_trigger_id"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/play_trigger"
+        android:onClick="onPlayTriggerButtonClicked"
+        android:padding="20dp" />
+
 </LinearLayout>
 
 <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/model_group_id"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:padding="20dp"
-        android:checkedButton="@+id/model_one"
         android:orientation="vertical">
-   <RadioButton android:id="@+id/model_one"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/model_one"
-        android:onClick="onRadioButtonClicked"/>
-   <RadioButton android:id="@+id/model_two"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/model_two"
-        android:onClick="onRadioButtonClicked"/>
-   <RadioButton android:id="@+id/model_three"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/model_three"
-        android:onClick="onRadioButtonClicked"/>
 </RadioGroup>
 
 <ScrollView
@@ -93,7 +86,7 @@
        android:fillViewport="true">
 
       <TextView
-         android:id="@+id/console"
+        android:id="@+id/console"
         android:paddingTop="20pt"
         android:layout_height="fill_parent"
         android:layout_width="fill_parent"
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index b4ca71b..7c1f649 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -21,8 +21,6 @@
     <string name="unenroll">Un-load</string>
     <string name="start_recog">Start</string>
     <string name="stop_recog">Stop</string>
-    <string name="model_one">Model One</string>
-    <string name="model_two">Model Two</string>
-    <string name="model_three">Model Three</string>
+    <string name="play_trigger">Play Trigger Audio</string>
     <string name="none">Debug messages appear here:\n</string>
 </resources>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
index 3ca96d2..5fd38e9 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
@@ -16,24 +16,36 @@
 
 package com.android.test.soundtrigger;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
 import java.util.Random;
 import java.util.UUID;
 
 import android.app.Activity;
-import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
+import android.hardware.soundtrigger.SoundTrigger;
 import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
 import android.media.soundtrigger.SoundTriggerDetector;
 import android.media.soundtrigger.SoundTriggerManager;
-import android.text.Editable;
-import android.text.method.ScrollingMovementMethod;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.UserManager;
+import android.text.Editable;
+import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
 import android.view.View;
+import android.widget.Button;
 import android.widget.RadioButton;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
 import android.widget.ScrollView;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -44,20 +56,17 @@
 
     private SoundTriggerUtil mSoundTriggerUtil;
     private Random mRandom;
-    private UUID mModelUuid1 = UUID.randomUUID();
-    private UUID mModelUuid2 = UUID.randomUUID();
-    private UUID mModelUuid3 = UUID.randomUUID();
-    private UUID mVendorUuid = UUID.randomUUID();
 
-    private SoundTriggerDetector mDetector1 = null;
-    private SoundTriggerDetector mDetector2 = null;
-    private SoundTriggerDetector mDetector3 = null;
+    private Map<Integer, ModelInfo> mModelInfoMap;
+    private Map<View, Integer> mModelIdMap;
 
     private TextView mDebugView = null;
-    private int mSelectedModelId = 1;
+    private int mSelectedModelId = -1;
     private ScrollView mScrollView = null;
+    private Button mPlayTriggerButton = null;
     private PowerManager.WakeLock mScreenWakelock;
     private Handler mHandler;
+    private RadioGroup mRadioGroup;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -66,11 +75,108 @@
         setContentView(R.layout.main);
         mDebugView = (TextView) findViewById(R.id.console);
         mScrollView = (ScrollView) findViewById(R.id.scroller_id);
+        mRadioGroup = (RadioGroup) findViewById(R.id.model_group_id);
+        mPlayTriggerButton = (Button) findViewById(R.id.play_trigger_id);
         mDebugView.setText(mDebugView.getText(), TextView.BufferType.EDITABLE);
         mDebugView.setMovementMethod(new ScrollingMovementMethod());
         mSoundTriggerUtil = new SoundTriggerUtil(this);
         mRandom = new Random();
         mHandler = new Handler();
+
+        mModelInfoMap = new HashMap();
+        mModelIdMap = new HashMap();
+
+        setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+        // Load all the models in the data dir.
+        for (File file : getFilesDir().listFiles()) {
+            // Find meta-data in .properties files, ignore everything else.
+            if (!file.getName().endsWith(".properties")) {
+                continue;
+            }
+            try {
+                Properties properties = new Properties();
+                properties.load(new FileInputStream(file));
+                createModelInfoAndWidget(properties);
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to load properties file " + file.getName());
+            }
+        }
+
+        // Create a few dummy models if we didn't load anything.
+        if (mModelIdMap.isEmpty()) {
+            Properties dummyModelProperties = new Properties();
+            for (String name : new String[]{"One", "Two", "Three"}) {
+                dummyModelProperties.setProperty("name", "Model " + name);
+                createModelInfoAndWidget(dummyModelProperties);
+            }
+        }
+    }
+
+    private void createModelInfoAndWidget(Properties properties) {
+        try {
+            ModelInfo modelInfo = new ModelInfo();
+
+            if (!properties.containsKey("name")) {
+                throw new RuntimeException("must have a 'name' property");
+            }
+            modelInfo.name = properties.getProperty("name");
+
+            if (properties.containsKey("modelUuid")) {
+                modelInfo.modelUuid = UUID.fromString(properties.getProperty("modelUuid"));
+            } else {
+                modelInfo.modelUuid = UUID.randomUUID();
+            }
+
+            if (properties.containsKey("vendorUuid")) {
+                modelInfo.vendorUuid = UUID.fromString(properties.getProperty("vendorUuid"));
+            } else {
+                modelInfo.vendorUuid = UUID.randomUUID();
+            }
+
+            if (properties.containsKey("triggerAudio")) {
+                modelInfo.triggerAudioPlayer = MediaPlayer.create(this, Uri.parse(
+                        getFilesDir().getPath() + "/" + properties.getProperty("triggerAudio")));
+            }
+
+            if (properties.containsKey("dataFile")) {
+                File modelDataFile = new File(
+                        getFilesDir().getPath() + "/" + properties.getProperty("dataFile"));
+                modelInfo.modelData = new byte[(int) modelDataFile.length()];
+                FileInputStream input = new FileInputStream(modelDataFile);
+                input.read(modelInfo.modelData, 0, modelInfo.modelData.length);
+            } else {
+                modelInfo.modelData = new byte[1024];
+                mRandom.nextBytes(modelInfo.modelData);
+            }
+
+            // TODO: Add property support for keyphrase models when they're exposed by the
+            // service. Also things like how much audio they should record with the capture session
+            // provided in the callback.
+
+            // Add a widget into the radio group.
+            RadioButton button = new RadioButton(this);
+            mRadioGroup.addView(button);
+            button.setText(modelInfo.name);
+            button.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    onRadioButtonClicked(v);
+                }
+            });
+
+            // Update our maps containing the button -> id and id -> modelInfo.
+            int newModelId = mModelIdMap.size() + 1;
+            mModelIdMap.put(button, newModelId);
+            mModelInfoMap.put(newModelId, modelInfo);
+
+            // If we don't have something selected, select this first thing.
+            if (mSelectedModelId < 0) {
+                button.setChecked(true);
+                onRadioButtonClicked(button);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Error parsing properties for " + properties.getProperty("name"), e);
+        }
     }
 
     private void postMessage(String msg) {
@@ -91,27 +197,15 @@
     }
 
     private synchronized UUID getSelectedUuid() {
-        if (mSelectedModelId == 2) return mModelUuid2;
-        if (mSelectedModelId == 3) return mModelUuid3;
-        return mModelUuid1;  // Default.
+        return mModelInfoMap.get(mSelectedModelId).modelUuid;
     }
 
     private synchronized void setDetector(SoundTriggerDetector detector) {
-        if (mSelectedModelId == 2) {
-            mDetector2 = detector;
-            return;
-        }
-        if (mSelectedModelId == 3) {
-            mDetector3 = detector;
-            return;
-        }
-        mDetector1 = detector;
+        mModelInfoMap.get(mSelectedModelId).detector = detector;
     }
 
     private synchronized SoundTriggerDetector getDetector() {
-        if (mSelectedModelId == 2) return mDetector2;
-        if (mSelectedModelId == 3) return mDetector3;
-        return mDetector1;
+        return mModelInfoMap.get(mSelectedModelId).detector;
     }
 
     private void screenWakeup() {
@@ -127,25 +221,29 @@
         mScreenWakelock.release();
     }
 
+    /** TODO: Should return the abstract sound model that can be then sent to the service. */
+    private GenericSoundModel createNewSoundModel() {
+        ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
+        return new GenericSoundModel(modelInfo.modelUuid, modelInfo.vendorUuid,
+                modelInfo.modelData);
+    }
+
     /**
      * Called when the user clicks the enroll button.
      * Performs a fresh enrollment.
      */
     public void onEnrollButtonClicked(View v) {
         postMessage("Loading model: " + mSelectedModelId);
-        // Generate a fake model to push.
-        byte[] data = new byte[1024];
-        mRandom.nextBytes(data);
-        UUID modelUuid = getSelectedUuid();
-        GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
+
+        GenericSoundModel model = createNewSoundModel();
 
         boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(model);
         if (status) {
             Toast.makeText(
-                    this, "Successfully created sound trigger model UUID=" + modelUuid,
+                    this, "Successfully created sound trigger model UUID=" + model.uuid,
                     Toast.LENGTH_SHORT).show();
         } else {
-            Toast.makeText(this, "Failed to enroll!!!" + modelUuid, Toast.LENGTH_SHORT).show();
+            Toast.makeText(this, "Failed to enroll!!!" + model.uuid, Toast.LENGTH_SHORT).show();
         }
 
         // Test the SoundManager API.
@@ -185,11 +283,7 @@
             Toast.makeText(this, "Sound model not found!!!", Toast.LENGTH_SHORT).show();
             return;
         }
-        // Generate a fake model to push.
-        byte[] data = new byte[2048];
-        mRandom.nextBytes(data);
-        GenericSoundModel updated = new GenericSoundModel(soundModel.uuid,
-                soundModel.vendorUuid, data);
+        GenericSoundModel updated = createNewSoundModel();
         boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
         if (status) {
             Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.uuid,
@@ -237,29 +331,32 @@
     public synchronized void onRadioButtonClicked(View view) {
         // Is the button now checked?
         boolean checked = ((RadioButton) view).isChecked();
-        // Check which radio button was clicked
-        switch(view.getId()) {
-            case R.id.model_one:
-                if (checked) {
-                    mSelectedModelId = 1;
-                    postMessage("Selected model one.");
-                }
-                break;
-            case R.id.model_two:
-                if (checked) {
-                    mSelectedModelId = 2;
-                    postMessage("Selected model two.");
-                }
-                break;
-            case R.id.model_three:
-                if (checked) {
-                    mSelectedModelId = 3;
-                    postMessage("Selected model three.");
-                }
-                break;
+        if (checked) {
+            mSelectedModelId = mModelIdMap.get(view);
+            ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
+            postMessage("Selected " + modelInfo.name);
+
+            // Set the play trigger button to be enabled only if we actually have some audio.
+            mPlayTriggerButton.setEnabled(modelInfo.triggerAudioPlayer != null);
         }
     }
 
+    public synchronized void onPlayTriggerButtonClicked(View v) {
+        ModelInfo modelInfo = mModelInfoMap.get(mSelectedModelId);
+        modelInfo.triggerAudioPlayer.start();
+        postMessage("Playing trigger audio for " + modelInfo.name);
+    }
+
+    // Helper struct for holding information about a model.
+    private static class ModelInfo {
+      public String name;
+      public UUID modelUuid;
+      public UUID vendorUuid;
+      public MediaPlayer triggerAudioPlayer;
+      public SoundTriggerDetector detector;
+      public byte modelData[];
+    };
+
     // Implementation of SoundTriggerDetector.Callback.
     public class DetectorCallback extends SoundTriggerDetector.Callback {
         public void onAvailabilityChanged(int status) {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 6c8be39..a4aab7c 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -25,6 +25,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
+import android.media.AudioAttributes;
 import android.os.Bundle;
 import android.os.Vibrator;
 import android.os.Handler;
@@ -86,6 +87,28 @@
     }
 
     private Test[] mTests = new Test[] {
+            new Test("Phone call") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("phone call")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                                    getPackageName() + "/raw/ringer"),
+                                    new AudioAttributes.Builder().setUsage(
+                                            AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build())
+                            .setPriority(Notification.PRIORITY_MAX)
+                            .setVibrate(new long[] {
+                                    300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+                                    300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
+                                    300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 })
+                            .setFullScreenIntent(makeIntent2(), true)
+                            .build();
+                    mNM.notify(7001, n);
+                }
+            },
             new Test("Post a group") {
                 public void run()
                 {
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/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index 450334c..5767f11 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -194,6 +194,18 @@
     }
 
     @Override
+    public void onHandleAssistSecondary(final Bundle data, final AssistStructure structure,
+            final AssistContent content, int index, int count) {
+        Log.i(TAG, "Got secondary activity assist data " + index + " of " + count);
+        Log.i(TAG, "Showing assist structure after a few seconds...");
+        mContentView.postDelayed(new Runnable() {
+            public void run() {
+                onHandleAssist(data, structure, content);
+            }
+        }, 2000 * index);
+    }
+
+    @Override
     public void onHandleScreenshot(Bitmap screenshot) {
         if (screenshot != null) {
             mScreenshot.setImageBitmap(screenshot);
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..e0d8249 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -24,6 +24,7 @@
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.util.PropertiesMap;
 
 import android.view.View;
 import android.view.ViewGroup;
@@ -70,20 +71,8 @@
     }
 
     @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);
+    public Map<Object, PropertiesMap> getDefaultProperties() {
+        return mSession.getDefaultProperties();
     }
 
     @Override
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..80e230c 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
@@ -32,6 +32,8 @@
 import com.android.layoutlib.bridge.impl.Stack;
 import com.android.resources.ResourceType;
 import com.android.util.Pair;
+import com.android.util.PropertiesMap;
+import com.android.util.PropertiesMap.Property;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -152,7 +154,6 @@
     private IBinder mBinder;
     private PackageManager mPackageManager;
 
-
     /**
      * Some applications that target both pre API 17 and post API 17, set the newer attrs to
      * reference the older ones. For example, android:paddingStart will resolve to
@@ -275,8 +276,8 @@
         return mRenderResources;
     }
 
-    public Map<String, String> getDefaultPropMap(Object key) {
-        return mDefaultPropMaps.get(key);
+    public Map<Object, PropertiesMap> getDefaultProperties() {
+        return mDefaultPropMaps;
     }
 
     public Configuration getConfiguration() {
@@ -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()));
                 }
             }
         }
@@ -1858,7 +1862,6 @@
         return false;
     }
 
-
     /**
      * The cached value depends on
      * <ol>
@@ -1915,11 +1918,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/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 866b248..3ef568c 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
@@ -48,6 +48,7 @@
 import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.util.Pair;
+import com.android.util.PropertiesMap;
 
 import android.animation.AnimationThread;
 import android.animation.Animator;
@@ -1415,8 +1416,8 @@
         return mSystemViewInfoList;
     }
 
-    public Map<String, String> getDefaultProperties(Object viewObject) {
-        return getContext().getDefaultPropMap(viewObject);
+    public Map<Object, PropertiesMap> getDefaultProperties() {
+        return getContext().getDefaultProperties();
     }
 
     public void setScene(RenderSession 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/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0f84506..be5b8fc 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -149,7 +149,7 @@
     void setAllowScansWithTraffic(int enabled);
     int getAllowScansWithTraffic();
 
-    boolean enableAutoJoinWhenAssociated(boolean enabled);
+    boolean setEnableAutoJoinWhenAssociated(boolean enabled);
     boolean getEnableAutoJoinWhenAssociated();
 
     void enableWifiConnectivityManager(boolean enabled);
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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6984fe2..1cd32b6 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2700,9 +2700,9 @@
      * @return true -- if set successful false -- if set failed
      * @hide
      */
-    public boolean enableAutoJoinWhenAssociated(boolean enabled) {
+    public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
         try {
-            return mService.enableAutoJoinWhenAssociated(enabled);
+            return mService.setEnableAutoJoinWhenAssociated(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
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;
             }