Merge changes from topic 'vr-fixes' into nyc-dev

* changes:
  Suppress immersive mode confirmation in VR mode.
  Suppress heads-up notifications in VR mode.
diff --git a/api/current.txt b/api/current.txt
index 3444597..7d762fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3578,6 +3578,7 @@
     method public final deprecated void removeDialog(int);
     method public void reportFullyDrawn();
     method public android.view.DropPermissions requestDropPermissions(android.view.DragEvent);
+    method public final void requestKeyboardShortcutsHelper();
     method public final void requestPermissions(java.lang.String[], int);
     method public boolean requestVisibleBehind(boolean);
     method public final boolean requestWindowFeature(int);
@@ -9497,9 +9498,10 @@
   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 int getShortcutIconResId(android.content.pm.ShortcutInfo);
+    method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9511,6 +9513,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);
   }
 
@@ -9532,6 +9535,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
@@ -10045,6 +10049,7 @@
     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();
@@ -19852,8 +19857,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();
@@ -19878,8 +19883,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);
@@ -19913,8 +19918,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 {
@@ -19929,10 +19935,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);
   }
 
@@ -19952,8 +19958,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();
@@ -19985,8 +19991,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);
@@ -20040,8 +20046,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 {
@@ -20717,10 +20724,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;
   }
@@ -58033,6 +58042,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 {
@@ -58076,6 +58086,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();
   }
@@ -59057,6 +59068,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);
@@ -59158,6 +59170,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>);
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index a4927f2..362fa3f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3696,6 +3696,7 @@
     method public final deprecated void removeDialog(int);
     method public void reportFullyDrawn();
     method public android.view.DropPermissions requestDropPermissions(android.view.DragEvent);
+    method public final void requestKeyboardShortcutsHelper();
     method public final void requestPermissions(java.lang.String[], int);
     method public boolean requestVisibleBehind(boolean);
     method public final boolean requestWindowFeature(int);
@@ -9835,9 +9836,10 @@
   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 int getShortcutIconResId(android.content.pm.ShortcutInfo);
+    method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9849,6 +9851,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);
   }
 
@@ -9870,6 +9873,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
@@ -10445,6 +10449,7 @@
     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();
@@ -15490,6 +15495,7 @@
 
   public class NanoApp {
     ctor public NanoApp();
+    ctor public NanoApp(int, byte[]);
     method public int describeContents();
     method public byte[] getAppBinary();
     method public int getAppId();
@@ -21330,8 +21336,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();
@@ -21356,8 +21362,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);
@@ -21393,8 +21399,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 {
@@ -21409,10 +21416,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);
   }
 
@@ -21432,8 +21439,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();
@@ -21465,8 +21472,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);
@@ -21520,8 +21527,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 {
@@ -22197,10 +22205,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;
   }
@@ -61145,6 +61155,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 {
@@ -61188,6 +61199,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();
   }
@@ -62169,6 +62181,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);
@@ -62270,6 +62283,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>);
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index 4179948..8ee1655 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3578,6 +3578,7 @@
     method public final deprecated void removeDialog(int);
     method public void reportFullyDrawn();
     method public android.view.DropPermissions requestDropPermissions(android.view.DragEvent);
+    method public final void requestKeyboardShortcutsHelper();
     method public final void requestPermissions(java.lang.String[], int);
     method public boolean requestVisibleBehind(boolean);
     method public final boolean requestWindowFeature(int);
@@ -9504,11 +9505,13 @@
   }
 
   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 int getShortcutIconResId(android.content.pm.ShortcutInfo);
+    method public int getShortcutIconResId(java.lang.String, java.lang.String, android.os.UserHandle);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
@@ -9520,6 +9523,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);
   }
 
@@ -9541,6 +9545,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
@@ -10055,6 +10060,7 @@
     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();
@@ -10087,6 +10093,7 @@
   }
 
   public class ShortcutManager {
+    ctor public ShortcutManager(android.content.Context);
     method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
     method public void deleteAllDynamicShortcuts();
     method public void deleteDynamicShortcut(java.lang.String);
@@ -19918,8 +19925,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();
@@ -19944,8 +19951,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);
@@ -19979,8 +19986,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 {
@@ -19995,10 +20003,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);
   }
 
@@ -20018,8 +20026,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();
@@ -20051,8 +20059,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);
@@ -20106,8 +20114,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 {
@@ -20783,10 +20792,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;
   }
@@ -58109,6 +58120,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 {
@@ -58152,6 +58164,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();
   }
@@ -59133,6 +59146,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);
@@ -59234,6 +59248,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>);
   }
 
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 672a706..7652766 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1676,6 +1676,17 @@
     public void onProvideAssistContent(AssistContent outContent) {
     }
 
+    /**
+     * Request the Keyboard Shortcuts screen to show up. If it succeeds, this will trigger
+     * {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
+     */
+    public final void requestKeyboardShortcutsHelper() {
+        Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
+        intent.setComponent(new ComponentName("com.android.systemui",
+                "com.android.systemui.statusbar.KeyboardShortcutsReceiver"));
+        sendBroadcast(intent);
+    }
+
     @Override
     public void onProvideKeyboardShortcuts(
             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
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..784872c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1955,9 +1955,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
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bdc4404..d5d4ca7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -589,9 +589,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 +756,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 3f9629c8..96757bb 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) {
@@ -5853,7 +5763,6 @@
      */
     public void setShortSupportMessage(@NonNull ComponentName admin,
             @Nullable String message) {
-        throwIfParentInstance("setShortSupportMessage");
         if (mService != null) {
             try {
                 mService.setShortSupportMessage(admin, message);
@@ -5872,7 +5781,6 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public String getShortSupportMessage(@NonNull ComponentName admin) {
-        throwIfParentInstance("getShortSupportMessage");
         if (mService != null) {
             try {
                 return mService.getShortSupportMessage(admin);
@@ -5899,7 +5807,6 @@
      */
     public void setLongSupportMessage(@NonNull ComponentName admin,
             @Nullable String message) {
-        throwIfParentInstance("setLongSupportMessage");
         if (mService != null) {
             try {
                 mService.setLongSupportMessage(admin, message);
@@ -5918,7 +5825,6 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public String getLongSupportMessage(@NonNull ComponentName admin) {
-        throwIfParentInstance("getLongSupportMessage");
         if (mService != null) {
             try {
                 return mService.getLongSupportMessage(admin);
@@ -6015,7 +5921,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 +5947,6 @@
      * @see #retrieveSecurityLogs
      */
     public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
-        throwIfParentInstance("setSecurityLoggingEnabled");
         try {
             mService.setSecurityLoggingEnabled(admin, enabled);
         } catch (RemoteException re) {
@@ -6061,7 +5965,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 +5988,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 +6033,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();
@@ -6153,7 +6054,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationColor(@NonNull ComponentName admin, int color) {
-        throwIfParentInstance("setOrganizationColor");
         try {
             mService.setOrganizationColor(admin, color);
         } catch (RemoteException re) {
@@ -6189,7 +6089,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public int getOrganizationColor(@NonNull ComponentName admin) {
-        throwIfParentInstance("getOrganizationColor");
         try {
             return mService.getOrganizationColor(admin);
         } catch (RemoteException re) {
@@ -6225,7 +6124,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationName(@NonNull ComponentName admin, @Nullable String title) {
-        throwIfParentInstance("setOrganizationName");
         try {
             mService.setOrganizationName(admin, title);
         } catch (RemoteException re) {
@@ -6242,7 +6140,6 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public String getOrganizationName(@NonNull ComponentName admin) {
-        throwIfParentInstance("getOrganizationName");
         try {
             return mService.getOrganizationName(admin);
         } catch (RemoteException re) {
@@ -6368,10 +6265,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/content/Intent.java b/core/java/android/content/Intent.java
index 207b70a..7e67e8d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1404,6 +1404,16 @@
     public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
 
     /**
+     * Activity Action: Start the Keyboard Shortcuts Helper screen.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SHOW_KEYBOARD_SHORTCUTS =
+            "android.intent.action.SHOW_KEYBOARD_SHORTCUTS";
+
+    /**
      * Activity Action: Show settings for managing network data usage of a
      * specific application. Applications should define an activity that offers
      * options to control data usage.
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 6b3d4f1..46321a4 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -47,17 +47,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/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index d865f345..a0d2339 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;
@@ -205,6 +207,9 @@
         String mPackage;
 
         @Nullable
+        List<String> mShortcutIds;
+
+        @Nullable
         ComponentName mActivity;
 
         @QueryFlags
@@ -229,6 +234,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 +263,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.
@@ -429,7 +449,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 +458,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.
      *
@@ -490,11 +500,33 @@
      * #hasShortcutHostPermission()}.
      *
      * @param shortcut The target shortcut.
+     */
+    public int getShortcutIconResId(@NonNull ShortcutInfo shortcut) {
+        return getShortcutIconResId(shortcut.getPackageName(), shortcut.getId(),
+                shortcut.getUserId());
+    }
+
+    /**
+     * Return the icon resource ID, if {@code shortcut} has one
+     * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
+     *
+     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+     * #hasShortcutHostPermission()}.
+     *
+     * @param packageName The target package name.
+     * @param shortcutId The ID of the shortcut to lad rom.
      * @param user The UserHandle of the profile.
      */
-    public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+    public int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
+            @NonNull UserHandle user) {
+        return getShortcutIconResId(packageName, shortcutId, user.getIdentifier());
+    }
+
+    private int getShortcutIconResId(@NonNull String packageName, @NonNull String shortcutId,
+            int userId) {
         try {
-            return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
+            return mService.getShortcutIconResId(mContext.getPackageName(),
+                    packageName, shortcutId, userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -508,12 +540,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 +590,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/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index e85311d..8236f55 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -36,22 +36,21 @@
  * @hide
  */
 public class PackageUserState {
+    public long ceDataInode;
+    public boolean installed;
     public boolean stopped;
     public boolean notLaunched;
-    public boolean installed;
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
-    public int enabled;
     public boolean blockUninstall;
-
+    public int enabled;
     public String lastDisableAppCaller;
+    public int domainVerificationStatus;
+    public int appLinkGeneration;
 
     public ArraySet<String> disabledComponents;
     public ArraySet<String> enabledComponents;
 
-    public int domainVerificationStatus;
-    public int appLinkGeneration;
-
     public PackageUserState() {
         installed = true;
         hidden = false;
@@ -62,18 +61,19 @@
     }
 
     public PackageUserState(PackageUserState o) {
+        ceDataInode = o.ceDataInode;
         installed = o.installed;
         stopped = o.stopped;
         notLaunched = o.notLaunched;
-        enabled = o.enabled;
         hidden = o.hidden;
         suspended = o.suspended;
-        lastDisableAppCaller = o.lastDisableAppCaller;
-        disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
-        enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
         blockUninstall = o.blockUninstall;
+        enabled = o.enabled;
+        lastDisableAppCaller = o.lastDisableAppCaller;
         domainVerificationStatus = o.domainVerificationStatus;
         appLinkGeneration = o.appLinkGeneration;
+        disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
+        enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
     }
 
     /**
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 1812575a..7a807c4 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;
@@ -153,7 +154,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
@@ -191,6 +196,7 @@
      * Copy constructor.
      */
     private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
+        mUserId = source.mUserId;
         mId = source.mId;
         mPackageName = source.mPackageName;
         mFlags = source.mFlags;
@@ -238,6 +244,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");
@@ -362,8 +369,8 @@
          *
          * <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)}.
+         * need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo)}
+         * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo)}.
          */
         @NonNull
         public Builder setIcon(Icon icon) {
@@ -544,6 +551,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 +609,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 +618,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);
@@ -661,6 +680,7 @@
     private ShortcutInfo(Parcel source) {
         final ClassLoader cl = getClass().getClassLoader();
 
+        mUserId = source.readInt();
         mId = source.readString();
         mPackageName = source.readString();
         mActivityComponent = source.readParcelable(cl);
@@ -679,6 +699,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUserId);
         dest.writeString(mId);
         dest.writeString(mPackageName);
         dest.writeParcelable(mActivityComponent, flags);
@@ -784,11 +805,13 @@
     }
 
     /** @hide */
-    public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
+    public ShortcutInfo(
+            @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
             Icon icon, String title, String text, Intent intent,
             PersistableBundle intentPersistableExtras,
             int weight, PersistableBundle extras, long lastChangedTimestamp,
             int flags, int iconResId, String bitmapPath) {
+        mUserId = userId;
         mId = id;
         mPackageName = packageName;
         mActivityComponent = activityComponent;
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index e4a98b5..919ccda 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;
@@ -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.
      *
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/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/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/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a3c3fe..638be51 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6666,6 +6666,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
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/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index f44d4c1a..c4ed94f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -485,25 +485,15 @@
     }
 
     /**
-     * Halts any current rendering into the surface. Use this if it is unclear whether
+     * Stops any rendering into the surface. Use this if it is unclear whether
      * or not the surface used by the HardwareRenderer will be changing. It
-     * Suspends any rendering into the surface, but will not do any destruction.
-     *
-     * Any subsequent draws will override the pause, resuming normal operation.
+     * Suspends any rendering into the surface, but will not do any destruction
      */
     boolean pauseSurface(Surface surface) {
         return nPauseSurface(mNativeProxy, surface);
     }
 
     /**
-     * Hard stops or resumes rendering into the surface. This flag is used to
-     * determine whether or not it is safe to use the given surface *at all*
-     */
-    void setStopped(boolean stopped) {
-        nSetStopped(mNativeProxy, stopped);
-    }
-
-    /**
      * Destroys all hardware rendering resources associated with the specified
      * view hierarchy.
      *
@@ -903,6 +893,10 @@
         nSerializeDisplayListTree(mNativeProxy);
     }
 
+    public static boolean copySurfaceInto(Surface surface, Bitmap bitmap) {
+        return nCopySurfaceInto(surface, bitmap);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -998,7 +992,6 @@
     private static native void nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native boolean nPauseSurface(long nativeProxy, Surface window);
-    private static native void nSetStopped(long nativeProxy, boolean stopped);
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
     private static native void nSetLightCenter(long nativeProxy,
@@ -1040,4 +1033,6 @@
 
     private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
     private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
+
+    private static native boolean nCopySurfaceInto(Surface surface, Bitmap bitmap);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 94c4cef..a324767 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1079,16 +1079,13 @@
     void setWindowStopped(boolean stopped) {
         if (mStopped != stopped) {
             mStopped = stopped;
-            final ThreadedRenderer renderer = mAttachInfo.mHardwareRenderer;
-            if (renderer != null) {
-                if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
-                renderer.setStopped(mStopped);
-            }
             if (!mStopped) {
                 scheduleTraversals();
             } else {
-                if (renderer != null) {
-                    renderer.destroyHardwareResources(mView);
+                if (mAttachInfo.mHardwareRenderer != null) {
+                    if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
+                    mAttachInfo.mHardwareRenderer.updateSurface(null);
+                    mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
                 }
             }
         }
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index fd73432..24739bf 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import android.annotation.NonNull;
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.os.Parcel;
@@ -25,6 +26,7 @@
 import android.text.TextUtils;
 import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
 
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -36,6 +38,11 @@
  */
 public final class CursorAnchorInfo implements Parcelable {
     /**
+     * The pre-computed hash code.
+     */
+    private final int mHashCode;
+
+    /**
      * The index of the first character of the selected text (inclusive). {@code -1} when there is
      * no text selection.
      */
@@ -100,7 +107,8 @@
      * Transformation matrix that is applied to any positional information of this class to
      * transform local coordinates into screen coordinates.
      */
-    private final Matrix mMatrix;
+    @NonNull
+    private final float[] mMatrixValues;
 
     /**
      * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the
@@ -121,6 +129,7 @@
     public static final int FLAG_IS_RTL = 0x04;
 
     public CursorAnchorInfo(final Parcel source) {
+        mHashCode = source.readInt();
         mSelectionStart = source.readInt();
         mSelectionEnd = source.readInt();
         mComposingTextStart = source.readInt();
@@ -131,8 +140,7 @@
         mInsertionMarkerBaseline = source.readFloat();
         mInsertionMarkerBottom = source.readFloat();
         mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
-        mMatrix = new Matrix();
-        mMatrix.setValues(source.createFloatArray());
+        mMatrixValues = source.createFloatArray();
     }
 
     /**
@@ -143,6 +151,7 @@
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mHashCode);
         dest.writeInt(mSelectionStart);
         dest.writeInt(mSelectionEnd);
         dest.writeInt(mComposingTextStart);
@@ -153,27 +162,12 @@
         dest.writeFloat(mInsertionMarkerBaseline);
         dest.writeFloat(mInsertionMarkerBottom);
         dest.writeParcelable(mCharacterBoundsArray, flags);
-        final float[] matrixArray = new float[9];
-        mMatrix.getValues(matrixArray);
-        dest.writeFloatArray(matrixArray);
+        dest.writeFloatArray(mMatrixValues);
     }
 
     @Override
     public int hashCode(){
-        final float floatHash = mInsertionMarkerHorizontal + mInsertionMarkerTop
-                + mInsertionMarkerBaseline + mInsertionMarkerBottom;
-        int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash);
-        hash *= 31;
-        hash += mInsertionMarkerFlags;
-        hash *= 31;
-        hash += mSelectionStart + mSelectionEnd + mComposingTextStart;
-        hash *= 31;
-        hash += Objects.hashCode(mComposingText);
-        hash *= 31;
-        hash += Objects.hashCode(mCharacterBoundsArray);
-        hash *= 31;
-        hash += Objects.hashCode(mMatrix);
-        return hash;
+        return mHashCode;
     }
 
     /**
@@ -202,13 +196,13 @@
         if (hashCode() != that.hashCode()) {
             return false;
         }
+
+        // Check fields that are not covered by hashCode() first.
+
         if (mSelectionStart != that.mSelectionStart || mSelectionEnd != that.mSelectionEnd) {
             return false;
         }
-        if (mComposingTextStart != that.mComposingTextStart
-                || !Objects.equals(mComposingText, that.mComposingText)) {
-            return false;
-        }
+
         if (mInsertionMarkerFlags != that.mInsertionMarkerFlags
                 || !areSameFloatImpl(mInsertionMarkerHorizontal, that.mInsertionMarkerHorizontal)
                 || !areSameFloatImpl(mInsertionMarkerTop, that.mInsertionMarkerTop)
@@ -216,18 +210,35 @@
                 || !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) {
             return false;
         }
+
         if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) {
             return false;
         }
-        if (!Objects.equals(mMatrix, that.mMatrix)) {
+
+        // Following fields are (partially) covered by hashCode().
+
+        if (mComposingTextStart != that.mComposingTextStart
+                || !Objects.equals(mComposingText, that.mComposingText)) {
             return false;
         }
+
+        // We do not use Arrays.equals(float[], float[]) to keep the previous behavior regarding
+        // NaN, 0.0f, and -0.0f.
+        if (mMatrixValues.length != that.mMatrixValues.length) {
+            return false;
+        }
+        for (int i = 0; i < mMatrixValues.length; ++i) {
+            if (mMatrixValues[i] != that.mMatrixValues[i]) {
+                return false;
+            }
+        }
         return true;
     }
 
     @Override
     public String toString() {
-        return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd
+        return "CursorAnchorInfo{mHashCode=" + mHashCode
+                + " mSelection=" + mSelectionStart + "," + mSelectionEnd
                 + " mComposingTextStart=" + mComposingTextStart
                 + " mComposingText=" + Objects.toString(mComposingText)
                 + " mInsertionMarkerFlags=" + mInsertionMarkerFlags
@@ -236,7 +247,7 @@
                 + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
                 + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
                 + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray)
-                + " mMatrix=" + Objects.toString(mMatrix)
+                + " mMatrix=" + Arrays.toString(mMatrixValues)
                 + "}";
     }
 
@@ -254,7 +265,7 @@
         private float mInsertionMarkerBottom = Float.NaN;
         private int mInsertionMarkerFlags = 0;
         private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null;
-        private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX);
+        private float[] mMatrixValues = null;
         private boolean mMatrixInitialized = false;
 
         /**
@@ -349,7 +360,10 @@
          * is interpreted as an identity matrix.
          */
         public Builder setMatrix(final Matrix matrix) {
-            mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX);
+            if (mMatrixValues == null) {
+                mMatrixValues = new float[9];
+            }
+            (matrix != null ? matrix : Matrix.IDENTITY_MATRIX).getValues(mMatrixValues);
             mMatrixInitialized = true;
             return this;
         }
@@ -391,7 +405,6 @@
             mInsertionMarkerTop = Float.NaN;
             mInsertionMarkerBaseline = Float.NaN;
             mInsertionMarkerBottom = Float.NaN;
-            mMatrix.set(Matrix.IDENTITY_MATRIX);
             mMatrixInitialized = false;
             if (mCharacterBoundsArrayBuilder != null) {
                 mCharacterBoundsArrayBuilder.reset();
@@ -411,7 +424,18 @@
         mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
         mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ?
                 builder.mCharacterBoundsArrayBuilder.build() : null;
-        mMatrix = new Matrix(builder.mMatrix);
+        mMatrixValues = new float[9];
+        if (builder.mMatrixInitialized) {
+            System.arraycopy(builder.mMatrixValues, 0, mMatrixValues, 0, 9);
+        } else {
+            Matrix.IDENTITY_MATRIX.getValues(mMatrixValues);
+        }
+
+        // To keep hash function simple, we only use some complex objects for hash.
+        int hash = Objects.hashCode(mComposingText);
+        hash *= 31;
+        hash += Arrays.hashCode(mMatrixValues);
+        mHashCode = hash;
     }
 
     /**
@@ -527,7 +551,9 @@
      * @return a new instance (copy) of the transformation matrix.
      */
     public Matrix getMatrix() {
-        return new Matrix(mMatrix);
+        final Matrix matrix = new Matrix();
+        matrix.setValues(mMatrixValues);
+        return matrix;
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index a42f4d9..dc433b1 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -68,6 +68,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 +158,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 +269,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 +294,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) {
@@ -501,6 +505,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/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/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/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index cd4f7b6..76102af 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -629,7 +629,7 @@
      * this is an eng build.)
      */
     private static void logOrThrow(String message, Throwable th) {
-        Slog.wtf(TAG, message, th);
+        Slog.e(TAG, message, th);
         if (Build.TYPE.equals("eng")) {
             throw new RuntimeException(message, th);
         }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 85cc841..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) {
+                InputMethodInfo imi, @Nullable InputMethodSubtype subtype, boolean forward) {
             if (imi == null) {
                 return null;
             }
@@ -297,8 +312,9 @@
                 return null;
             }
             final int N = mImeSubtypeList.size();
-            for (int offset = 1; offset < N; ++offset) {
-                // Start searching the next IME/subtype from the next of the current index.
+            for (int i = 1; i < N; ++i) {
+                // 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);
                 // Skip if searching inside the current IME only, but the candidate is not
@@ -370,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) {
+                InputMethodInfo imi, @Nullable InputMethodSubtype subtype, boolean forward) {
             int currentUsageRank = getUsageRank(imi, subtype);
             if (currentUsageRank < 0) {
                 if (DEBUG) {
@@ -381,7 +411,8 @@
             }
             final int N = mUsageHistoryOfSubtypeListItemIndex.length;
             for (int i = 1; i < N; i++) {
-                final int subtypeListItemRank = (currentUsageRank + i) % N;
+                final int offset = forward ? i : N - i;
+                final int subtypeListItemRank = (currentUsageRank + offset) % N;
                 final int subtypeListItemIndex =
                         mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank];
                 final ImeSubtypeListItem subtypeListItem =
@@ -454,17 +485,31 @@
             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) {
+                @Nullable InputMethodSubtype subtype, boolean forward) {
             if (imi == null) {
                 return null;
             }
             if (imi.supportsSwitchingToNextInputMethod()) {
                 return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
-                        subtype);
+                        subtype, forward);
             } else {
                 return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
-                        subtype);
+                        subtype, forward);
             }
         }
 
@@ -532,14 +577,14 @@
     }
 
     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
-            InputMethodSubtype subtype) {
+            InputMethodSubtype subtype, boolean forward) {
         if (mController == null) {
             if (DEBUG) {
                 Log.e(TAG, "mController shouldn't be null.");
             }
             return null;
         }
-        return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
+        return mController.getNextInputMethod(onlyCurrentIme, imi, subtype, forward);
     }
 
     public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 2a9264d..47f2c70 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -103,21 +103,7 @@
         }
     }
 
-    public void execute(String cmd, Object... args) throws InstallerException {
-        final String resRaw = executeForResult(cmd, args);
-        int res = -1;
-        try {
-            res = Integer.parseInt(resRaw);
-        } catch (NumberFormatException ignored) {
-        }
-        if (res != 0) {
-            throw new InstallerException(
-                    "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
-        }
-    }
-
-    public String executeForResult(String cmd, Object... args)
-            throws InstallerException {
+    public String[] execute(String cmd, Object... args) throws InstallerException {
         final StringBuilder builder = new StringBuilder(cmd);
         for (Object arg : args) {
             String escaped;
@@ -135,7 +121,17 @@
             }
             builder.append(' ').append(escaped);
         }
-        return transact(builder.toString());
+        final String[] resRaw = transact(builder.toString()).split(" ");
+        int res = -1;
+        try {
+            res = Integer.parseInt(resRaw[0]);
+        } catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) {
+        }
+        if (res != 0) {
+            throw new InstallerException(
+                    "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
+        }
+        return resRaw;
     }
 
     public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
@@ -160,19 +156,15 @@
     }
 
     public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
-        String rawReply = executeForResult("merge_profiles", uid, pkgName);
-        if (rawReply == null) {
-            throw new IllegalStateException("Unexpected null reply");
-        }
-        final String res[] = rawReply.split(" ");
+        final String[] res = execute("merge_profiles", uid, pkgName);
 
         if ((res == null) || (res.length != 2)) {
-            throw new InstallerException("Invalid size result: " + rawReply);
+            throw new InstallerException("Invalid size result: " + Arrays.toString(res));
         }
 
         // Just as a sanity check. Anything != "true" will be interpreted as false by parseBoolean.
         if (!res[1].equals("true") && !res[1].equals("false")) {
-            throw new InstallerException("Invalid boolean result: " + rawReply);
+            throw new InstallerException("Invalid boolean result: " + Arrays.toString(res));
         }
 
         return Boolean.parseBoolean(res[1]);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index bed5a2e..18f715e 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -27,6 +27,7 @@
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -128,7 +129,7 @@
     /**
      * Checks if given array is null or has zero elements.
      */
-    public static boolean isEmpty(@Nullable List<?> array) {
+    public static boolean isEmpty(@Nullable Collection<?> array) {
         return array == null || array.isEmpty();
     }
 
@@ -451,7 +452,7 @@
         }
     }
 
-    public static <T> boolean contains(@Nullable ArrayList<T> cur, T val) {
+    public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
         return (cur != null) ? cur.contains(val) : false;
     }
 
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/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 25b487e..c4347f8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,14 +17,9 @@
 
 package com.android.internal.widget;
 
-import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -90,10 +85,6 @@
     private final float mMinFlingVelocity;
     private final OverScroller mScroller;
     private final VelocityTracker mVelocityTracker;
-    private final Drawable mScrollIndicatorDrawable;
-    private final Drawable mFakeForeground;
-
-    private View mButtonBar;
 
     private OnDismissedListener mOnDismissedListener;
     private RunOnDismissedListener mRunOnDismissedListener;
@@ -115,8 +106,6 @@
                 }
             };
 
-    private final int[] mTempOffset = new int[2];
-
     public ResolverDrawerLayout(Context context) {
         this(context, null);
     }
@@ -138,9 +127,6 @@
                 mMaxCollapsedHeight);
         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));
         mVelocityTracker = VelocityTracker.obtain();
@@ -152,13 +138,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();
@@ -223,7 +202,8 @@
             }
             final boolean isCollapsedNew = mCollapseOffset != 0;
             if (isCollapsedOld != isCollapsedNew) {
-                onCollapsedChanged(isCollapsedNew);
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         } else {
             // Start out collapsed at first unless we restored state for otherwise
@@ -462,7 +442,8 @@
             mTopOffset += dy;
             final boolean isCollapsedNew = newPos != 0;
             if (isCollapsedOld != isCollapsedNew) {
-                onCollapsedChanged(isCollapsedNew);
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
             }
             postInvalidateOnAnimation();
             return dy;
@@ -470,14 +451,6 @@
         return 0;
     }
 
-    private void onCollapsedChanged(boolean isCollapsed) {
-        notifyViewAccessibilityStateChangedIfNeeded(
-                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
-
-        // Set a fake foreground so that we receive onDrawForeground().
-        setForeground(isCollapsed ? mFakeForeground : null);
-    }
-
     void dispatchOnDismissed() {
         if (mOnDismissedListener != null) {
             mOnDismissedListener.onDismissed();
@@ -736,23 +709,6 @@
     }
 
     @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);
-            mScrollIndicatorDrawable.draw(canvas);
-        }
-
-        super.onDrawForeground(canvas);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec);
         int widthSize = sourceWidth;
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 91a3b4f..90d69d2 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;
 }
 
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index ef45c87..faa4192 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -479,12 +479,6 @@
     return proxy->pauseSurface(surface);
 }
 
-static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jboolean stopped) {
-    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setStopped(stopped);
-}
-
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
         jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -669,6 +663,14 @@
     proxy->setContentDrawBounds(left, top, right, bottom);
 }
 
+static jboolean android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
+        jobject clazz, jobject jsurface, jobject jbitmap) {
+    SkBitmap bitmap;
+    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+    return RenderProxy::copySurfaceInto(surface, &bitmap);
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -738,7 +740,6 @@
     { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
-    { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
     { "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
@@ -775,6 +776,8 @@
     { "nRemoveFrameMetricsObserver",
             "(JJ)V",
             (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
+    { "nCopySurfaceInto", "(Landroid/view/Surface;Landroid/graphics/Bitmap;)Z",
+                (void*)android_view_ThreadedRenderer_copySurfaceInto },
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index fe43e1c..4b8640c 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -30,37 +30,33 @@
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
         android:elevation="8dp"
-        android:background="@color/white">
-
-        <TextView
-            android:id="@+id/profile_button"
-            android:layout_width="wrap_content"
-            android:layout_height="48dp"
-            android:layout_marginEnd="8dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:visibility="gone"
-            style="?attr/borderlessButtonStyle"
-            android:textAppearance="?attr/textAppearanceButton"
-            android:textColor="@color/material_deep_teal_500"
-            android:gravity="center_vertical"
-            android:layout_alignParentTop="true"
-            android:layout_alignParentRight="true"
-            android:singleLine="true" />
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:minHeight="56dp"
-            android:textAppearance="?attr/textAppearanceMedium"
-            android:gravity="start|center_vertical"
-            android:paddingStart="?attr/dialogPreferredPadding"
-            android:paddingEnd="?attr/dialogPreferredPadding"
-            android:paddingTop="8dp"
-            android:layout_below="@id/profile_button"
-            android:layout_alignParentLeft="true"
-            android:paddingBottom="8dp" />
+        android:background="@color/white" >
+        <TextView android:id="@+id/profile_button"
+                  android:layout_width="wrap_content"
+                  android:layout_height="48dp"
+                  android:layout_marginEnd="8dp"
+                  android:paddingStart="8dp"
+                  android:paddingEnd="8dp"
+                  android:visibility="gone"
+                  style="?attr/borderlessButtonStyle"
+                  android:textAppearance="?attr/textAppearanceButton"
+                  android:textColor="@color/material_deep_teal_500"
+                  android:gravity="center_vertical"
+                  android:layout_alignParentTop="true"
+                  android:layout_alignParentRight="true"
+                  android:singleLine="true"/>
+        <TextView android:id="@+id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:minHeight="56dp"
+                  android:textAppearance="?attr/textAppearanceMedium"
+                  android:gravity="start|center_vertical"
+                  android:paddingStart="?attr/dialogPreferredPadding"
+                  android:paddingEnd="?attr/dialogPreferredPadding"
+                  android:paddingTop="8dp"
+                  android:layout_below="@id/profile_button"
+                  android:layout_alignParentLeft="true"
+                  android:paddingBottom="8dp" />
     </RelativeLayout>
 
     <ListView
@@ -72,23 +68,21 @@
         android:background="@color/white"
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
-        android:scrollIndicators="top|bottom"
         android:divider="@null" />
 
-    <TextView
-        android:id="@+id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_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="match_parent"
+              android:layout_alwaysShow="true"
+              android:text="@string/noApplications"
+              android:padding="32dp"
+              android:gravity="center"
+              android:visibility="gone" />
 
     <LinearLayout
         android:id="@+id/button_bar"
         android:visibility="gone"
-        style="?attr/buttonBarStyle"
+        style="?android:attr/buttonBarStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_ignoreOffset="true"
@@ -103,30 +97,26 @@
         android:paddingStart="12dp"
         android:paddingEnd="12dp"
         android:elevation="8dp">
-
-        <Button
-            android:id="@+id/button_once"
-            android:layout_width="wrap_content"
-            android:layout_gravity="start"
-            android:maxLines="2"
-            style="?attr/buttonBarNegativeButtonStyle"
-            android:minHeight="@dimen/alert_dialog_button_bar_height"
-            android:layout_height="wrap_content"
-            android:enabled="false"
-            android:text="@string/activity_resolver_use_once"
-            android:onClick="onButtonClick" />
-
-        <Button
-            android:id="@+id/button_always"
-            android:layout_width="wrap_content"
-            android:layout_gravity="end"
-            android:maxLines="2"
-            android:minHeight="@dimen/alert_dialog_button_bar_height"
-            style="?attr/buttonBarPositiveButtonStyle"
-            android:layout_height="wrap_content"
-            android:enabled="false"
-            android:text="@string/activity_resolver_use_always"
-            android:onClick="onButtonClick" />
+        <Button android:id="@+id/button_once"
+                android:layout_width="wrap_content"
+                android:layout_gravity="start"
+                android:maxLines="2"
+                style="?android:attr/buttonBarNegativeButtonStyle"
+                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/activity_resolver_use_once"
+                android:onClick="onButtonClick" />
+        <Button android:id="@+id/button_always"
+                android:layout_width="wrap_content"
+                android:layout_gravity="end"
+                android:maxLines="2"
+                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                style="?android:attr/buttonBarPositiveButtonStyle"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/activity_resolver_use_always"
+                android:onClick="onButtonClick" />
     </LinearLayout>
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index ed7ef5e..31361e5 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -22,7 +22,8 @@
     android:layout_height="match_parent"
     android:maxWidth="@dimen/resolver_max_width"
     android:maxCollapsedHeight="144dp"
-    android:id="@id/contentPanel">
+    android:id="@id/contentPanel"
+    >
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -30,75 +31,66 @@
         android:layout_alwaysShow="true"
         android:orientation="vertical"
         android:background="@color/white"
-        android:elevation="8dp">
+        android:elevation="8dp" >
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="64dp"
-            android:orientation="horizontal">
+            android:orientation="horizontal" >
 
-            <ImageView
-                android:id="@+id/icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_gravity="start|top"
-                android:layout_marginStart="16dp"
-                android:layout_marginEnd="16dp"
-                android:layout_marginTop="20dp"
-                android:scaleType="fitCenter" />
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="0dp"
-                android:layout_weight="1"
-                android:layout_height="?attr/listPreferredItemHeight"
-                android:layout_marginStart="16dp"
-                android:textAppearance="?attr/textAppearanceMedium"
-                android:gravity="start|center_vertical"
-                android:paddingEnd="16dp" />
-
-            <LinearLayout
-                android:id="@+id/profile_button"
-                android:layout_width="wrap_content"
-                android:layout_height="48dp"
-                android:layout_marginTop="4dp"
-                android:layout_marginEnd="4dp"
-                android:paddingStart="8dp"
-                android:paddingEnd="8dp"
-                android:paddingTop="4dp"
-                android:paddingBottom="4dp"
-                android:focusable="true"
-                android:visibility="gone"
-                style="?attr/borderlessButtonStyle">
-
-                <ImageView
-                    android:id="@+id/icon"
-                    android:layout_width="24dp"
-                    android:layout_height="24dp"
-                    android:layout_gravity="start|center_vertical"
-                    android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
-                    android:layout_marginTop="12dp"
-                    android:layout_marginBottom="12dp"
-                    android:scaleType="fitCenter" />
-
-                <TextView
-                    android:id="@id/text1"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="start|center_vertical"
-                    android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
-                    android:textAppearance="?attr/textAppearanceButton"
-                    android:textColor="?attr/textColorPrimary"
-                    android:minLines="1"
-                    android:maxLines="1"
-                    android:ellipsize="marquee" />
+            <ImageView android:id="@+id/icon"
+                       android:layout_width="24dp"
+                       android:layout_height="24dp"
+                       android:layout_gravity="start|top"
+                       android:layout_marginStart="16dp"
+                       android:layout_marginEnd="16dp"
+                       android:layout_marginTop="20dp"
+                       android:scaleType="fitCenter" />
+            <TextView android:id="@+id/title"
+                      android:layout_width="0dp"
+                      android:layout_weight="1"
+                      android:layout_height="?android:attr/listPreferredItemHeight"
+                      android:layout_marginStart="16dp"
+                      android:textAppearance="?android:attr/textAppearanceMedium"
+                      android:gravity="start|center_vertical"
+                      android:paddingEnd="16dp" />
+            <LinearLayout android:id="@+id/profile_button"
+                          android:layout_width="wrap_content"
+                          android:layout_height="48dp"
+                          android:layout_marginTop="4dp"
+                          android:layout_marginEnd="4dp"
+                          android:paddingStart="8dp"
+                          android:paddingEnd="8dp"
+                          android:paddingTop="4dp"
+                          android:paddingBottom="4dp"
+                          android:focusable="true"
+                          android:visibility="gone"
+                          style="?attr/borderlessButtonStyle">
+                <ImageView android:id="@+id/icon"
+                           android:layout_width="24dp"
+                           android:layout_height="24dp"
+                           android:layout_gravity="start|center_vertical"
+                           android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                           android:layout_marginTop="12dp"
+                           android:layout_marginBottom="12dp"
+                           android:scaleType="fitCenter" />
+                <TextView android:id="@id/text1"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_gravity="start|center_vertical"
+                          android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                          android:textAppearance="?attr/textAppearanceButton"
+                          android:textColor="?attr/textColorPrimary"
+                          android:minLines="1"
+                          android:maxLines="1"
+                          android:ellipsize="marquee" />
             </LinearLayout>
         </LinearLayout>
 
         <LinearLayout
             android:id="@+id/button_bar"
             android:visibility="gone"
-            style="?attr/buttonBarStyle"
+            style="?android:attr/buttonBarStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_alwaysShow="true"
@@ -112,36 +104,30 @@
             android:paddingEnd="12dp"
             android:background="@color/white"
             android:elevation="8dp">
-
-            <Button
-                android:id="@+id/button_once"
-                android:layout_width="wrap_content"
-                android:layout_gravity="start"
-                android:maxLines="2"
-                style="?attr/buttonBarNegativeButtonStyle"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="@string/activity_resolver_use_once"
-                android:onClick="onButtonClick" />
-
-            <Button
-                android:id="@+id/button_always"
-                android:layout_width="wrap_content"
-                android:layout_gravity="end"
-                android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?attr/buttonBarPositiveButtonStyle"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="@string/activity_resolver_use_always"
-                android:onClick="onButtonClick" />
+            <Button android:id="@+id/button_once"
+                    android:layout_width="wrap_content"
+                    android:layout_gravity="start"
+                    android:maxLines="2"
+                    style="?android:attr/buttonBarNegativeButtonStyle"
+                    android:minHeight="@dimen/alert_dialog_button_bar_height"
+                    android:layout_height="wrap_content"
+                    android:enabled="false"
+                    android:text="@string/activity_resolver_use_once"
+                    android:onClick="onButtonClick" />
+            <Button android:id="@+id/button_always"
+                    android:layout_width="wrap_content"
+                    android:layout_gravity="end"
+                    android:maxLines="2"
+                    android:minHeight="@dimen/alert_dialog_button_bar_height"
+                    style="?android:attr/buttonBarPositiveButtonStyle"
+                    android:layout_height="wrap_content"
+                    android:enabled="false"
+                    android:text="@string/activity_resolver_use_always"
+                    android:onClick="onButtonClick" />
         </LinearLayout>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:background="?attr/dividerVertical" />
+        <View android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:background="?android:attr/dividerVertical" />
     </LinearLayout>
 
     <ListView
@@ -154,6 +140,6 @@
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
         android:divider="@null"
-        android:scrollIndicators="top|bottom" />
+        />
 
 </com.android.internal.widget.ResolverDrawerLayout>
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/config.xml b/core/res/res/values/config.xml
index dfa5143b..cb551e8 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>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8d8b832..29c6951 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" />
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/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/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index ec5220f..ba5206a 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -27,7 +27,6 @@
 
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
-import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -68,7 +67,7 @@
         ri.serviceInfo = si;
         List<InputMethodSubtype> subtypes = null;
         if (subtypeLocales != null) {
-            subtypes = new ArrayList<InputMethodSubtype>();
+            subtypes = new ArrayList<>();
             for (String subtypeLocale : subtypeLocales) {
                 subtypes.add(createDummySubtype(subtypeLocale));
             }
@@ -89,7 +88,7 @@
     }
 
     private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
-        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+        final List<ImeSubtypeListItem> items = new ArrayList<>();
         addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
                 true /* supportsSwitchingToNextInputMethod*/);
         addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
@@ -105,7 +104,7 @@
     }
 
     private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
-        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+        final List<ImeSubtypeListItem> items = new ArrayList<>();
         addDummyImeSubtypeListItems(items,
                 "UnknownIme", "UnknownIme",
                 Arrays.asList("en_US", "hi"),
@@ -121,15 +120,18 @@
     }
 
     private void assertNextInputMethod(final ControllerImpl controller,
-            final boolean onlyCurrentIme,
-            final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
+            final boolean onlyCurrentIme, final ImeSubtypeListItem currentItem,
+            final ImeSubtypeListItem nextItem, final ImeSubtypeListItem prevItem) {
         InputMethodSubtype subtype = null;
         if (currentItem.mSubtypeName != null) {
             subtype = createDummySubtype(currentItem.mSubtypeName.toString());
         }
         final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
-                currentItem.mImi, subtype);
+                currentItem.mImi, subtype, true /* forward */);
         assertEquals(nextItem, nextIme);
+        final ImeSubtypeListItem prevIme = controller.getNextInputMethod(onlyCurrentIme,
+                currentItem.mImi, subtype, false /* forward */);
+        assertEquals(prevItem, prevIme);
     }
 
     private void assertRotationOrder(final ControllerImpl controller,
@@ -138,11 +140,13 @@
         final int N = expectedRotationOrderOfImeSubtypeList.length;
         for (int i = 0; i < N; i++) {
             final int currentIndex = i;
+            final int prevIndex = (currentIndex + N - 1) % N;
             final int nextIndex = (currentIndex + 1) % N;
             final ImeSubtypeListItem currentItem =
                     expectedRotationOrderOfImeSubtypeList[currentIndex];
             final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
-            assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem);
+            final ImeSubtypeListItem prevItem = expectedRotationOrderOfImeSubtypeList[prevIndex];
+            assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem, prevItem);
         }
     }
 
@@ -190,29 +194,29 @@
         assertRotationOrder(controller, true /* onlyCurrentIme */,
                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                subtypeUnawareIme, null);
+                subtypeUnawareIme, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                japaneseIme_ja_JP, null);
+                japaneseIme_ja_JP, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                switchUnawareJapaneseIme_ja_JP, null);
+                switchUnawareJapaneseIme_ja_JP, null, null);
 
         // Make sure that disabled IMEs are not accepted.
         assertNextInputMethod(controller, false /* onlyCurrentIme */,
-                disabledIme_en_US, null);
+                disabledIme_en_US, null, null);
         assertNextInputMethod(controller, false /* onlyCurrentIme */,
-                disabledIme_hi, null);
+                disabledIme_hi, null, null);
         assertNextInputMethod(controller, false /* onlyCurrentIme */,
-                disabledSwitchingUnawareIme, null);
+                disabledSwitchingUnawareIme, null, null);
         assertNextInputMethod(controller, false /* onlyCurrentIme */,
-                disabledSubtypeUnawareIme, null);
+                disabledSubtypeUnawareIme, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                disabledIme_en_US, null);
+                disabledIme_en_US, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                disabledIme_hi, null);
+                disabledIme_hi, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                disabledSwitchingUnawareIme, null);
+                disabledSwitchingUnawareIme, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                disabledSubtypeUnawareIme, null);
+                disabledSubtypeUnawareIme, null, null);
     }
 
     @SmallTest
@@ -246,7 +250,7 @@
                 japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
         // Check onlyCurrentIme == true.
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                japaneseIme_ja_JP, null);
+                japaneseIme_ja_JP, null, null);
         assertRotationOrder(controller, true /* onlyCurrentIme */,
                 latinIme_fr, latinIme_en_US);
         assertRotationOrder(controller, true /* onlyCurrentIme */,
@@ -270,9 +274,9 @@
         assertRotationOrder(controller, true /* onlyCurrentIme */,
                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                subtypeUnawareIme, null);
+                subtypeUnawareIme, null, null);
         assertNextInputMethod(controller, true /* onlyCurrentIme */,
-                switchUnawareJapaneseIme_ja_JP, null);
+                switchUnawareJapaneseIme_ja_JP, null, null);
 
         // Rotation order should be preserved when created with the same subtype list.
         final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
@@ -298,7 +302,7 @@
 
     @SmallTest
     public void testImeSubtypeListItem() throws Exception {
-        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+        final List<ImeSubtypeListItem> items = new ArrayList<>();
         addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme",
                 Arrays.asList("en_US", "fr", "en", "en_uk", "enn", "e", "EN_US"),
                 true /* supportsSwitchingToNextInputMethod*/);
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/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/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/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/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 0000000..d7df77c
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+#include "Caches.h"
+#include "Image.h"
+#include "GlopBuilder.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "utils/GLUtils.h"
+
+#include <GLES2/gl2.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+bool Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
+        Surface& surface, SkBitmap* bitmap) {
+    // TODO: Clean this up and unify it with LayerRenderer::copyLayer,
+    // of which most of this is copied from.
+    renderThread.eglManager().initialize();
+
+    Caches& caches = Caches::getInstance();
+    RenderState& renderState = renderThread.renderState();
+    int destWidth = bitmap->width();
+    int destHeight = bitmap->height();
+    if (destWidth > caches.maxTextureSize
+                || destHeight > caches.maxTextureSize) {
+        ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d",
+                destWidth, destHeight, caches.maxTextureSize);
+        return false;
+    }
+    GLuint fbo = renderState.createFramebuffer();
+    if (!fbo) {
+        ALOGW("Could not obtain an FBO");
+        return false;
+    }
+
+    SkAutoLockPixels alp(*bitmap);
+
+    GLuint texture;
+
+    GLenum format;
+    GLenum type;
+
+    switch (bitmap->colorType()) {
+        case kAlpha_8_SkColorType:
+            format = GL_ALPHA;
+            type = GL_UNSIGNED_BYTE;
+            break;
+        case kRGB_565_SkColorType:
+            format = GL_RGB;
+            type = GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case kARGB_4444_SkColorType:
+            format = GL_RGBA;
+            type = GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case kN32_SkColorType:
+        default:
+            format = GL_RGBA;
+            type = GL_UNSIGNED_BYTE;
+            break;
+    }
+
+    renderState.bindFramebuffer(fbo);
+
+    // TODO: Use layerPool or something to get this maybe? But since we
+    // need explicit format control we can't currently.
+
+    // Setup the rendertarget
+    glGenTextures(1, &texture);
+    caches.textureState().activateTexture(0);
+    caches.textureState().bindTexture(texture);
+    glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight,
+            0, format, type, nullptr);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+            GL_TEXTURE_2D, texture, 0);
+
+    // Setup the source
+    sp<GraphicBuffer> sourceBuffer;
+    sp<Fence> sourceFence;
+    // FIXME: Waiting on an API from libgui for this
+    // surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
+    if (!sourceBuffer.get()) {
+        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+        return false;
+    }
+    int err = sourceFence->wait(500 /* ms */);
+    if (err != NO_ERROR) {
+        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+        return false;
+    }
+    Image sourceImage(sourceBuffer);
+    if (!sourceImage.getTexture()) {
+        ALOGW("Failed to make an EGLImage from the GraphicBuffer");
+        return false;
+    }
+    Texture sourceTexture(caches);
+    sourceTexture.wrap(sourceImage.getTexture(),
+            sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
+
+    {
+        // Draw & readback
+        renderState.setViewport(destWidth, destHeight);
+        renderState.scissor().setEnabled(false);
+        renderState.blend().syncEnabled();
+        renderState.stencil().disable();
+
+        Rect destRect(destWidth, destHeight);
+        Glop glop;
+        GlopBuilder(renderState, caches, &glop)
+                .setRoundRectClipState(nullptr)
+                .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
+                .setFillLayer(sourceTexture, nullptr, 1.0f, SkXfermode::kSrc_Mode,
+                        Blend::ModeOrderSwap::NoSwap)
+                .setTransform(Matrix4::identity(), TransformFlags::None)
+                .setModelViewMapUnitToRect(destRect)
+                .build();
+        Matrix4 ortho;
+        ortho.loadOrtho(destWidth, destHeight);
+        renderState.render(glop, ortho);
+
+        glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+                type, bitmap->getPixels());
+    }
+
+    // Cleanup
+    caches.textureState().deleteTexture(texture);
+    renderState.deleteFramebuffer(fbo);
+
+    GL_CHECKPOINT(MODERATE);
+
+    return true;
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
new file mode 100644
index 0000000..ea03c82
--- /dev/null
+++ b/libs/hwui/Readback.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "renderthread/RenderThread.h"
+
+#include <SkBitmap.h>
+#include <gui/Surface.h>
+
+namespace android {
+namespace uirenderer {
+
+class Readback {
+public:
+    static bool copySurfaceInto(renderthread::RenderThread& renderThread,
+            Surface& surface, SkBitmap* bitmap);
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/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 14c8f392..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/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/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 890d4a1..ab66b2a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -113,11 +113,18 @@
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
         mHaveNewSurface = true;
         mSwapHistory.clear();
+        makeCurrent();
     } else {
         mRenderThread.removeFrameCallback(this);
     }
 }
 
+void CanvasContext::requireSurface() {
+    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+            "requireSurface() called but no surface set!");
+    makeCurrent();
+}
+
 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
     mSwapBehavior = swapBehavior;
 }
@@ -139,18 +146,6 @@
     return mRenderThread.removeFrameCallback(this);
 }
 
-void CanvasContext::setStopped(bool stopped) {
-    if (mStopped != stopped) {
-        mStopped = stopped;
-        if (mStopped) {
-            mRenderThread.removeFrameCallback(this);
-            if (mEglManager.isCurrent(mEglSurface)) {
-                mEglManager.makeCurrent(EGL_NO_SURFACE);
-            }
-        }
-    }
-}
-
 // TODO: don't pass viewport size, it's automatic via EGL
 void CanvasContext::setup(int width, int height, float lightRadius,
         uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
@@ -177,9 +172,7 @@
     mOpaque = opaque;
 }
 
-bool CanvasContext::makeCurrent() {
-    if (mStopped) return false;
-
+void CanvasContext::makeCurrent() {
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
     EGLint error = 0;
@@ -187,7 +180,6 @@
     if (error) {
         setSurface(nullptr);
     }
-    return !error;
 }
 
 static bool wasSkipped(FrameInfo* info) {
@@ -679,7 +671,7 @@
 }
 
 Layer* CanvasContext::createTextureLayer() {
-    mEglManager.initialize();
+    requireSurface();
     return LayerRenderer::createTextureLayer(mRenderThread.renderState());
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 52df3abe..9350114 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -82,14 +82,13 @@
     void initialize(Surface* surface);
     void updateSurface(Surface* surface);
     bool pauseSurface(Surface* surface);
-    void setStopped(bool stopped);
     bool hasSurface() { return mNativeSurface.get(); }
 
     void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightCenter(const Vector3& lightCenter);
     void setOpaque(bool opaque);
-    bool makeCurrent();
+    void makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
             int64_t syncQueued, RenderNode* target);
     void draw();
@@ -173,6 +172,7 @@
     friend class android::uirenderer::RenderState;
 
     void setSurface(Surface* window);
+    void requireSurface();
 
     void freePrefetchedLayers(TreeObserver* observer);
 
@@ -185,7 +185,6 @@
     EglManager& mEglManager;
     sp<Surface> mNativeSurface;
     EGLSurface mEglSurface = EGL_NO_SURFACE;
-    bool mStopped = false;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
     struct SwapHistory {
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index ed472ac..651aaa2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -115,7 +115,7 @@
     ATRACE_CALL();
     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     mRenderThread->timeLord().vsyncReceived(vsync);
-    bool canDraw = mContext->makeCurrent();
+    mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse(mContext);
 
     for (size_t i = 0; i < mLayers.size(); i++) {
@@ -126,9 +126,8 @@
 
     // This is after the prepareTree so that any pending operations
     // (RenderNode tree state, prefetched layers, etc...) will be flushed.
-    if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
+    if (CC_UNLIKELY(!mContext->hasSurface())) {
         mSyncResult |= kSync_LostSurfaceRewardIfFound;
-        info.out.canDrawThisFrame = false;
     }
 
     if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index ac6a28f..8def7ad 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -270,6 +270,12 @@
         // Ensure we always have a valid surface & context
         surface = mPBufferSurface;
     }
+    // TODO: Temporary to help diagnose b/27286867
+    if (mCurrentSurface == mPBufferSurface || surface == mPBufferSurface) {
+        ALOGD("Switching from surface %p%s to %p%s", mCurrentSurface,
+                mCurrentSurface == mPBufferSurface ? " (pbuffer)" : "",
+                        surface, surface == mPBufferSurface ? " (pbuffer)" : "");
+    }
     if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
         if (errOut) {
             *errOut = eglGetError();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c5a2dc7..096093c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -19,6 +19,7 @@
 #include "DeferredLayerUpdater.h"
 #include "DisplayList.h"
 #include "LayerRenderer.h"
+#include "Readback.h"
 #include "Rect.h"
 #include "renderthread/CanvasContext.h"
 #include "renderthread/RenderTask.h"
@@ -167,18 +168,6 @@
     return (bool) postAndWait(task);
 }
 
-CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) {
-    args->context->setStopped(args->stopped);
-    return nullptr;
-}
-
-void RenderProxy::setStopped(bool stopped) {
-    SETUP_TASK(setStopped);
-    args->context = mContext;
-    args->stopped = stopped;
-    postAndWait(task);
-}
-
 CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height,
         float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
     args->context->setup(args->width, args->height, args->lightRadius,
@@ -616,6 +605,20 @@
     post(task);
 }
 
+CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread,
+        Surface* surface, SkBitmap* bitmap) {
+    return (void*) Readback::copySurfaceInto(*args->thread,
+            *args->surface, args->bitmap);
+}
+
+bool RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
+    SETUP_TASK(copySurfaceInto);
+    args->bitmap = bitmap;
+    args->surface = surface.get();
+    args->thread = &RenderThread::getInstance();
+    return (bool) staticPostAndWait(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 32d3283b..98aace0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -79,7 +79,6 @@
     ANDROID_API void initialize(const sp<Surface>& surface);
     ANDROID_API void updateSurface(const sp<Surface>& surface);
     ANDROID_API bool pauseSurface(const sp<Surface>& surface);
-    ANDROID_API void setStopped(bool stopped);
     ANDROID_API void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     ANDROID_API void setLightCenter(const Vector3& lightCenter);
@@ -127,6 +126,8 @@
     ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     ANDROID_API long getDroppedFrameReportCount();
 
+    ANDROID_API static bool copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/libs/hwui/tests/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/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..a5550ec 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
@@ -1320,6 +1322,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 +1341,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 +1350,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 +1377,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 +1396,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 +1441,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 +1470,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 +1517,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 +1555,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 +1576,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 +1617,6 @@
      * (potentially) handled in a different thread
      */
     private class NativeEventHandler extends Handler {
-
         private final AudioRecord mAudioRecord;
 
         NativeEventHandler(AudioRecord recorder, Looper looper) {
@@ -1714,8 +1647,7 @@
                 break;
             }
         }
-    };
-
+    }
 
     //---------------------------------------------------------
     // Java methods called from the native side
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/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index e623353..d718a7e 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -20,9 +20,12 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
+
 /**
  * Encapsulates the format of tracks played in {@link TvInputService}.
  */
@@ -245,6 +248,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
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/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index bc2133e..aa9f356 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -833,6 +833,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.
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/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 246b95de..6ed4ea1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -33,7 +33,6 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
 
@@ -118,9 +117,10 @@
     synchronized @Nullable LoaderTask getNextTaskOrReleaseBackgroundThread() {
         Preconditions.checkState(mBackgroundThread != null);
 
-        final LoaderTask task = mTaskList.findRunningTask();
-        if (task != null) {
-            return task;
+        for (final LoaderTask task : mTaskList) {
+            if (task.getState() == LoaderTask.STATE_LOADING) {
+                return task;
+            }
         }
 
         final Identifier identifier = mDatabase.getUnmappedDocumentsParent(mDevice.deviceId);
@@ -161,8 +161,21 @@
         mTaskList.clearCompletedTasks();
     }
 
-    synchronized void clearTask(Identifier parentIdentifier) {
-        mTaskList.clearTask(parentIdentifier);
+    /**
+     * Cancels the task for |parentIdentifier|.
+     *
+     * Task is removed from the cached list and it will create new task when |parentIdentifier|'s
+     * children are queried next.
+     */
+    void cancelTask(Identifier parentIdentifier) {
+        final LoaderTask task;
+        synchronized (this) {
+            task = mTaskList.findTask(parentIdentifier);
+        }
+        if (task != null) {
+            task.cancel();
+            mTaskList.remove(task);
+        }
     }
 
     /**
@@ -183,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);
                 }
@@ -205,14 +219,6 @@
             return null;
         }
 
-        LoaderTask findRunningTask() {
-            for (int i = 0; i < size(); i++) {
-                if (get(i).getState() == LoaderTask.STATE_LOADING)
-                    return get(i);
-            }
-            return null;
-        }
-
         void clearCompletedTasks() {
             int i = 0;
             while (i < size()) {
@@ -223,17 +229,6 @@
                 }
             }
         }
-
-        void clearTask(Identifier parentIdentifier) {
-            for (int i = 0; i < size(); i++) {
-                final LoaderTask task = get(i);
-                if (task.mIdentifier.mDeviceId == parentIdentifier.mDeviceId &&
-                        task.mIdentifier.mObjectHandle == parentIdentifier.mObjectHandle) {
-                    remove(i);
-                    return;
-                }
-            }
-        }
     }
 
     /**
@@ -245,6 +240,7 @@
         static final int STATE_LOADING = 1;
         static final int STATE_COMPLETED = 2;
         static final int STATE_ERROR = 3;
+        static final int STATE_CANCELLED = 4;
 
         final MtpManager mManager;
         final MtpDatabase mDatabase;
@@ -272,6 +268,7 @@
 
         synchronized void loadObjectHandles() {
             assert mState == STATE_START;
+            mPosition = 0;
             int parentHandle = mIdentifier.mObjectHandle;
             // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
             // getObjectHandles if we would like to obtain children under the root.
@@ -303,12 +300,10 @@
                 case STATE_ERROR:
                     throw mError;
             }
-
             final Cursor cursor =
                     mDatabase.queryChildDocuments(columnNames, mIdentifier.mDocumentId);
+            cursor.setExtras(extras);
             cursor.setNotificationUri(resolver, createUri());
-            cursor.respond(extras);
-
             return cursor;
         }
 
@@ -374,6 +369,10 @@
                 }
             }
             synchronized (this) {
+                // Check if the task is cancelled or not.
+                if (mState != STATE_LOADING) {
+                    return;
+                }
                 try {
                     mDatabase.getMapper().putChildDocuments(
                             mIdentifier.mDeviceId,
@@ -403,6 +402,14 @@
         }
 
         /**
+         * Cancels the task.
+         */
+        synchronized void cancel() {
+            mDatabase.getMapper().cancelAddingDocuments(mIdentifier.mDocumentId);
+            mState = STATE_CANCELLED;
+        }
+
+        /**
          * Returns a state of the task.
          */
         int getState() {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index adc71ae..63f18f3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -363,6 +363,41 @@
     }
 
     /**
+     * Cancels adding documents.
+     * @param parentId
+     */
+    void cancelAddingDocuments(@Nullable String parentId) {
+        final String selection;
+        final String[] args;
+        if (parentId != null) {
+            selection = COLUMN_PARENT_DOCUMENT_ID + " = ?";
+            args = strings(parentId);
+        } else {
+            selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
+            args = EMPTY_ARGS;
+        }
+
+        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
+        database.beginTransaction();
+        try {
+            if (!mInMappingIds.contains(parentId)) {
+                return;
+            }
+            mInMappingIds.remove(parentId);
+            final ContentValues values = new ContentValues();
+            values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
+            mDatabase.getSQLiteDatabase().update(
+                    TABLE_DOCUMENTS,
+                    values,
+                    selection + " AND " + COLUMN_ROW_STATE + " = ?",
+                    DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_INVALIDATED)));
+            database.setTransactionSuccessful();
+        } finally {
+            database.endTransaction();
+        }
+    }
+
+    /**
      * Queries candidate for each mappingKey, and returns the first cursor that includes a
      * candidate.
      *
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 50781bf..1823711 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -308,7 +308,7 @@
             final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId);
             mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
             mDatabase.deleteDocument(documentId);
-            getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
+            getDocumentLoader(parentIdentifier).cancelTask(parentIdentifier);
             notifyChildDocumentsChange(parentIdentifier.mDocumentId);
             if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) {
                 // If the parent is storage, the object might be appeared as child of device because
@@ -402,7 +402,7 @@
             final String documentId = mDatabase.putNewDocument(
                     parentId.mDeviceId, parentDocumentId, record.operationsSupported,
                     infoWithHandle, 0l);
-            getDocumentLoader(parentId).clearTask(parentId);
+            getDocumentLoader(parentId).cancelTask(parentId);
             notifyChildDocumentsChange(parentDocumentId);
             return documentId;
         } catch (FileNotFoundException | RuntimeException error) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index 45f89e4..a3c6bd7 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -21,6 +21,7 @@
 import android.mtp.MtpObjectInfo;
 import android.net.Uri;
 import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 
@@ -28,6 +29,7 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeoutException;
 
 @MediumTest
 public class DocumentLoaderTest extends AndroidTestCase {
@@ -141,6 +143,38 @@
         }
     }
 
+    public void testCancelTask() throws IOException, InterruptedException, TimeoutException {
+        setUpDocument(mManager,
+                DocumentLoader.NUM_INITIAL_ENTRIES + 1);
+
+        // Block the first iteration in the background thread.
+        mManager.blockDocument(
+                0, DocumentLoader.NUM_INITIAL_ENTRIES + 1);
+        setUpLoader();
+        try (final Cursor cursor = mLoader.queryChildDocuments(
+                MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
+            assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
+        }
+
+        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);
+        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() {
         mLoader = new DocumentLoader(
                 new MtpDeviceRecord(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5621642..640399f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1831,14 +1831,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/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index e1e0c3b..3b53055 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -179,42 +179,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 +228,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 +333,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 +370,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);
@@ -571,13 +566,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) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 936435d..bd3c6ba 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -491,5 +491,12 @@
                 <action android:name="com.android.systemui.action.CLEAR_TUNER" />
             </intent-filter>
         </receiver>
+
+        <receiver
+            android:name=".statusbar.KeyboardShortcutsReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.SHOW_KEYBOARD_SHORTCUTS" />
+            </intent-filter>
+        </receiver>
     </application>
 </manifest>
diff --git a/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml
new file mode 100644
index 0000000..33bceaa
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.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.
+-->
+
+<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="350" />
diff --git a/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml
new file mode 100644
index 0000000..a12ddff
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.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.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:propertyName="alpha"
+    android:valueTo="0"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="500" />
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/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/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..b1b2f1e 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -26,7 +26,9 @@
             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"
@@ -45,6 +47,17 @@
                 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" />
     </FrameLayout>
 </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..2df57bf 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -31,40 +31,19 @@
         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" />
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/move_task"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d9fcf42..18fc419 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -166,11 +166,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/strings.xml b/packages/SystemUI/res/values/strings.xml
index a33b7a3..dc9ffa9 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,7 @@
     <!-- 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>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 66754a7..f892fd6 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);
             }
         }
     }
@@ -1482,9 +1480,8 @@
                 final UserHandle currentUser = new UserHandle(KeyguardUpdateMonitor.getCurrentUser());
                 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));
                 }
             } 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..5cb46ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -118,6 +118,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/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/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/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 287bb22..82daaa6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -435,7 +435,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;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b1d9555..6b476ee 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,8 +59,10 @@
 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;
@@ -68,12 +71,12 @@
 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,6 +93,7 @@
     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;
@@ -101,6 +105,7 @@
     // Top level views
     private RecentsView mRecentsView;
     private SystemBarScrimViews mScrimViews;
+    private View mIncompatibleAppOverlay;
 
     // Runnables to finish the Recents activity
     private Intent mHomeIntent;
@@ -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();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
new file mode 100644
index 0000000..d6ef636
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.recents.events.ui;
+
+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/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
new file mode 100644
index 0000000..3a4350e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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.recents.events.ui;
+
+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/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 69d98af..44f220b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.annotation.FloatRange;
+import android.app.Activity;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -30,6 +31,7 @@
 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;
@@ -220,6 +222,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) {
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..22acb88 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;
@@ -166,8 +164,7 @@
         if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
                 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
             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 +181,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/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 6e585ae..6be8a4a 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;
@@ -157,6 +158,7 @@
     @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
     TaskViewHeader mHeaderView;
     View mActionButtonView;
+    View mIncompatibleAppToastView;
     TaskViewCallbacks mCb;
 
     @ViewDebug.ExportedProperty(category="recents")
@@ -345,6 +347,9 @@
         mActionButtonView.setScaleY(1f);
         mActionButtonView.setAlpha(0f);
         mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
+        if (mIncompatibleAppToastView != null) {
+            mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
+        }
     }
 
     /**
@@ -536,6 +541,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 +563,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 +605,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..16d8e53 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();
     }
@@ -305,8 +298,7 @@
                 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);
+        updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
         if (mAppOverlayView != null) {
             updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
         }
@@ -462,13 +454,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 +476,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 +623,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/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3005535..98e0dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -31,12 +31,13 @@
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
+import android.os.Vibrator;
 import android.util.AttributeSet;
 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 +46,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;
@@ -866,6 +866,9 @@
         mEntranceAnimationRunning = true;
         resizeStack(position, mSnapAlgorithm.getMiddleTarget().position,
                 mSnapAlgorithm.getMiddleTarget());
+
+        // Vibrate after docking
+        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
     public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
@@ -894,6 +897,9 @@
             // 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);
+
+            // 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/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index fff1491..c2521b3 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;
@@ -76,11 +78,32 @@
     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();
         }
     };
+    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;
@@ -255,10 +278,7 @@
     }
 
     public void toggleKeyboardShortcuts(int deviceId) {
-        InputDevice inputDevice = InputManager.getInstance().getInputDevice(deviceId);
-        if (inputDevice != null) {
-            mKeyCharacterMap = inputDevice.getKeyCharacterMap();
-        }
+        retrieveKeyCharacterMap(deviceId);
         if (mKeyboardShortcutsDialog == null) {
             Recents.getSystemServices().requestKeyboardShortcuts(mContext,
                 new KeyboardShortcutsReceiver() {
@@ -278,6 +298,35 @@
         }
     }
 
+    /**
+     * 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
+     * and if there is a full keyboard it uses that map, otherwise falls back to the Virtual
+     * Keyboard with its default map.
+     */
+    private void retrieveKeyCharacterMap(int deviceId) {
+        final InputManager inputManager = InputManager.getInstance();
+        if (deviceId != -1) {
+            final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+            if (inputDevice != null) {
+                mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+                return;
+            }
+        }
+        final int[] deviceIds = inputManager.getInputDeviceIds();
+        for (int i = 0; i < deviceIds.length; ++i) {
+            final InputDevice inputDevice = inputManager.getInputDevice(deviceIds[i]);
+            // -1 is the Virtual Keyboard, with the default key map. Use that one only as last
+            // resort.
+            if (inputDevice.getId() != -1 && inputDevice.isFullKeyboard()) {
+                mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+                return;
+            }
+        }
+        final InputDevice inputDevice = inputManager.getInputDevice(-1);
+        mKeyCharacterMap = inputDevice.getKeyCharacterMap();
+    }
+
     public void dismissKeyboardShortcutsDialog() {
         if (mKeyboardShortcutsDialog != null) {
             mKeyboardShortcutsDialog.dismiss();
@@ -320,9 +369,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);
@@ -340,7 +387,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,
@@ -350,7 +397,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,
@@ -361,7 +408,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,
@@ -371,7 +418,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,
@@ -381,7 +428,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,
@@ -391,7 +438,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,
@@ -401,14 +448,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) {
@@ -459,7 +516,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();
@@ -471,8 +528,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(
@@ -488,12 +552,6 @@
             final int itemsSize = group.getItems().size();
             for (int j = 0; j < itemsSize; j++) {
                 KeyboardShortcutInfo info = group.getItems().get(j);
-                if (info.getKeycode() != KeyEvent.KEYCODE_UNKNOWN
-                        && !KeyCharacterMap.deviceHasKey(info.getKeycode())) {
-                    // The user can't achieve this shortcut, so skipping.
-                    Log.w(TAG, "Keyboard Shortcut contains key not on device, skipping.");
-                    continue;
-                }
                 List<StringOrDrawable> shortcutKeys = getHumanReadableShortcutKeys(info);
                 if (shortcutKeys == null) {
                     // Ignore shortcuts we can't display keys for.
@@ -529,8 +587,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());
@@ -541,6 +599,7 @@
                         TextView shortcutKeyTextView = (TextView) inflater.inflate(
                                 R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
                                 false);
+                        shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
                         shortcutKeyTextView.setText(shortcutRepresentation.string);
                         shortcutItemsContainer.addView(shortcutKeyTextView);
                     }
@@ -557,16 +616,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) {
@@ -585,9 +634,7 @@
             if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
                 return shortcutKeys;
             }
-            // TODO: Have a generic map for when we don't have the device's.
-            char displayLabel = mKeyCharacterMap == null
-                    ? 0 : mKeyCharacterMap.getDisplayLabel(info.getKeycode());
+            char displayLabel = mKeyCharacterMap.getDisplayLabel(info.getKeycode());
             if (displayLabel != 0) {
                 displayLabelString = String.valueOf(displayLabel);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
new file mode 100644
index 0000000..5d22faf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * 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.statusbar;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receiver for the Keyboard Shortcuts Helper.
+ */
+public class KeyboardShortcutsReceiver extends BroadcastReceiver {
+    @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 */);
+        }
+    }
+}
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 f194355..c8a2ec0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3474,7 +3474,7 @@
             @Override
             public void run() {
                 mLeaveOpenOnKeyguardHide = true;
-                executeRunnableDismissingKeyguard(runnable, null, false, true);
+                executeRunnableDismissingKeyguard(runnable, null, false, false);
             }
         });
     }
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/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/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/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/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f93fb1b..6ca3af8 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);
             }
         }
     }
@@ -3458,33 +3455,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..428e192 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,17 +2014,19 @@
                     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")) {
+                    if (isLiveNetworkAgent(nai, msg.what)) {
                         final boolean valid =
                                 (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                         if (DBG) log(nai.name() + " validation " + (valid ? " passed" : "failed"));
@@ -2070,7 +2049,7 @@
                 }
                 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 +2081,14 @@
                     break;
                 }
             }
+            return true;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
+                maybeHandleNetworkAgentMessage(msg);
+            }
         }
     }
 
@@ -2321,7 +2308,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 +2321,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 +2432,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 +2482,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 +3343,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 +3369,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 +3812,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 +3977,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 +3995,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 +4200,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 +4211,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 +4222,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 +4238,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());
@@ -4521,14 +4506,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);
@@ -4858,7 +4843,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 +4921,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);
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 ac7872a..22cc066 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
@@ -2546,7 +2541,8 @@
                 return false;
             }
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
-                    onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+                    onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype,
+                    true /* forward */);
             if (nextSubtype == null) {
                 return false;
             }
@@ -2569,7 +2565,8 @@
                 return false;
             }
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
-                    false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+                    false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype,
+                    true /* forward */);
             if (nextSubtype == null) {
                 return false;
             }
@@ -2963,9 +2960,8 @@
 
     private void handleSwitchInputMethod(final boolean forwardDirection) {
         synchronized (mMethodMap) {
-            // TODO: Support forwardDirection.
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
-                    false, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+                    false, mMethodMap.get(mCurMethodId), mCurrentSubtype, forwardDirection);
             if (nextSubtype == null) {
                 return;
             }
@@ -3648,6 +3644,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";
@@ -3741,6 +3738,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());
@@ -3819,7 +3820,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)
@@ -3827,9 +3828,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/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..7db9be2 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 {
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/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 810270d..05e4245 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -85,8 +85,10 @@
 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.google.android.collect.Lists;
@@ -266,10 +268,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);
             }
         }
     }
@@ -539,7 +541,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 +554,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 +568,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,7 +652,8 @@
         }
     }
 
-    private void onUserUnlocked(Intent intent) {
+    @VisibleForTesting
+    void onUserUnlocked(Intent intent) {
         int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "onUserUnlocked " + userId);
@@ -799,7 +821,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 +1559,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 +4050,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 +4077,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 +4111,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 +4122,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 +4226,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 +4367,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 +4433,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 +4446,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 +4455,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 +4508,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 +4533,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 +4622,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 +4836,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 +5381,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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0f48d21..3ec51e3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13164,6 +13164,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 +13193,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 +17144,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 +17153,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);
                 }
             }
         }
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/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/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/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 3d8bf51..e3f3849 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -541,10 +541,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;
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f20d0a1..2765578 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2459,10 +2459,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;
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/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 7e25632..a11ee74 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -28,6 +28,8 @@
 
 import dalvik.system.VMRuntime;
 
+import java.util.Arrays;
+
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
 
@@ -90,14 +92,14 @@
         mInstaller.execute("migrate_app_data", uuid, pkgname, userid, flags);
     }
 
-    public void clearAppData(String uuid, String pkgname, int userid, int flags)
+    public void clearAppData(String uuid, String pkgname, int userid, int flags, long ceDataInode)
             throws InstallerException {
-        mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags);
+        mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags, ceDataInode);
     }
 
-    public void destroyAppData(String uuid, String pkgname, int userid, int flags)
+    public void destroyAppData(String uuid, String pkgname, int userid, int flags, long ceDataInode)
             throws InstallerException {
-        mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags);
+        mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags, ceDataInode);
     }
 
     public void moveCompleteApp(String from_uuid, String to_uuid, String package_name,
@@ -107,31 +109,26 @@
                 data_app_name, appid, seinfo, targetSdkVersion);
     }
 
-    public void getAppSize(String uuid, String pkgname, int userid, int flags, String apkPath,
-            String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets,
-            PackageStats pStats) throws InstallerException {
-        for (String instructionSet : instructionSets) {
-            assertValidInstructionSet(instructionSet);
-        }
-
-        // TODO: Extend getSizeInfo to look at the full subdirectory tree,
-        // not just the first level.
-        // TODO: Extend getSizeInfo to look at *all* instrution sets, not
-        // just the primary.
-        final String rawRes = mInstaller.executeForResult("get_app_size", uuid, pkgname, userid,
-                flags, apkPath, libDirPath, fwdLockApkPath, asecPath, instructionSets[0]);
-        final String res[] = rawRes.split(" ");
-
-        if ((res == null) || (res.length != 5)) {
-            throw new InstallerException("Invalid size result: " + rawRes);
-        }
+    public void getAppSize(String uuid, String pkgname, int userid, int flags, long ceDataInode,
+            String codePath, PackageStats stats) throws InstallerException {
+        final String[] res = mInstaller.execute("get_app_size", uuid, pkgname, userid, flags,
+                ceDataInode, codePath);
         try {
-            pStats.codeSize = Long.parseLong(res[1]);
-            pStats.dataSize = Long.parseLong(res[2]);
-            pStats.cacheSize = Long.parseLong(res[3]);
-            pStats.externalCodeSize = Long.parseLong(res[4]);
-        } catch (NumberFormatException e) {
-            throw new InstallerException("Invalid size result: " + rawRes);
+            stats.codeSize += Long.parseLong(res[1]);
+            stats.dataSize += Long.parseLong(res[2]);
+            stats.cacheSize += Long.parseLong(res[3]);
+        } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
+            throw new InstallerException("Invalid size result: " + Arrays.toString(res));
+        }
+    }
+
+    public long getAppDataInode(String uuid, String pkgname, int userid, int flags)
+            throws InstallerException {
+        final String[] res = mInstaller.execute("get_app_data_inode", uuid, pkgname, userid, flags);
+        try {
+            return Long.parseLong(res[1]);
+        } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
+            throw new InstallerException("Invalid inode result: " + Arrays.toString(res));
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 4c18e15..5ee7cc9 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);
@@ -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 bee276e..3b07fe1 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;
     }
@@ -2388,9 +2393,9 @@
                     if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                         psit.remove();
                         logCriticalInfo(Log.WARN, "System package " + ps.name
-                                + " no longer exists; wiping its data");
-                        // No apps are running this early, so no need to freeze
-                        removeDataDirsLIF(null, ps.name);
+                                + " no longer exists; it's data will be wiped");
+                        // Actual deletion of code and data will be handled by later
+                        // reconciliation step
                     } else {
                         final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
                         if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
@@ -2403,8 +2408,13 @@
             //look for any incomplete package installations
             ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
             for (int i = 0; i < deletePkgsList.size(); i++) {
-                // No apps are running this early, so no need to freeze
-                cleanupInstallFailedPackageLIF(deletePkgsList.get(i));
+                // Actual deletion of code and data will be handled by later
+                // reconciliation step
+                final String packageName = deletePkgsList.get(i).name;
+                logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + packageName);
+                synchronized (mPackages) {
+                    mSettings.removePackageLPw(packageName);
+                }
             }
 
             //delete tmp files
@@ -2437,9 +2447,9 @@
                     String msg;
                     if (deletedPkg == null) {
                         msg = "Updated system package " + deletedAppName
-                                + " no longer exists; wiping its data";
-                        // No apps are running this early, so no need to freeze
-                        removeDataDirsLIF(null, deletedAppName);
+                                + " no longer exists; it's data will be wiped";
+                        // Actual deletion of code and data will be handled by later
+                        // reconciliation step
                     } else {
                         msg = "Updated system app + " + deletedAppName
                                 + " no longer present; removing system privileges for "
@@ -2585,7 +2595,7 @@
             } else {
                 storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             }
-            reconcileAppsData(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM,
+            reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM,
                     storageFlags);
 
             // If this is first boot after an OTA, and a normal boot, then
@@ -2596,8 +2606,11 @@
                     final PackageSetting ps = mSettings.mPackages.valueAt(i);
                     if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
                         // No apps are running this early, so no need to freeze
-                        deleteCodeCacheDirsLIF(ps.volumeUuid, ps.name);
+                        clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
+                                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
+                                        | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                     }
+                    clearAppProfilesLIF(ps.pkg);
                 }
                 ver.fingerprint = Build.FINGERPRINT;
             }
@@ -2981,24 +2994,6 @@
         }
     }
 
-    void cleanupInstallFailedPackageLIF(PackageSetting ps) {
-        logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + ps.name);
-
-        removeDataDirsLIF(ps.volumeUuid, ps.name);
-        if (ps.codePath != null) {
-            removeCodePathLI(ps.codePath);
-        }
-        if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
-            if (ps.resourcePath.isDirectory()) {
-                FileUtils.deleteContents(ps.resourcePath);
-            }
-            ps.resourcePath.delete();
-        }
-        synchronized (mPackages) {
-            mSettings.removePackageLPw(ps.name);
-        }
-    }
-
     static int[] appendInts(int[] cur, int[] add) {
         if (add == null) return cur;
         if (cur == null) return add;
@@ -4726,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
@@ -5193,8 +5200,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,
@@ -7157,6 +7170,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;
@@ -7177,7 +7192,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);
@@ -7321,23 +7336,6 @@
         return true;
     }
 
-    private boolean removeDataDirsLIF(String volumeUuid, String packageName) {
-        // TODO: triage flags as part of 26466827
-        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
-
-        boolean res = true;
-        final int[] userIds = sUserManager.getUserIds();
-        for (int userId : userIds) {
-            try {
-                mInstaller.destroyAppData(volumeUuid, packageName, userId, flags);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to delete data directory", e);
-                res = false;
-            }
-        }
-        return res;
-    }
-
     void removeCodePathLI(File codePath) {
         if (codePath.isDirectory()) {
             try {
@@ -7350,77 +7348,106 @@
         }
     }
 
-    void destroyAppDataLI(String volumeUuid, String packageName, int userId, int flags) {
-        try (PackageFreezer freezer = freezePackage(packageName, "destroyAppDataLI")) {
-            try {
-                mInstaller.destroyAppData(volumeUuid, packageName, userId, flags);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to destroy app data", e);
-            }
-        }
+    private int[] resolveUserIds(int userId) {
+        return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId };
     }
 
-    void restoreconAppDataLI(String volumeUuid, String packageName, int userId, int flags,
-            int appId, String seinfo) {
-        try {
-            mInstaller.restoreconAppData(volumeUuid, packageName, userId, flags, appId, seinfo);
-        } catch (InstallerException e) {
-            Slog.e(TAG, "Failed to restorecon for " + packageName + ": " + e);
-        }
-    }
-
-    private void deleteProfilesLIF(PackageParser.Package pkg, boolean destroy) {
-        try {
-            if (destroy) {
-                mInstaller.destroyAppProfiles(pkg.packageName);
-            } else {
-                mInstaller.clearAppProfiles(pkg.packageName);
-            }
-        } catch (InstallerException ex) {
-            Log.e(TAG, "Could not delete profiles for package " + pkg.packageName);
-        }
-    }
-
-    private void deleteCodeCacheDirsLIF(String volumeUuid, String packageName) {
-        final PackageParser.Package pkg;
-        synchronized (mPackages) {
-            pkg = mPackages.get(packageName);
-        }
+    private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
         if (pkg == null) {
-            Slog.w(TAG, "Failed to delete code cache directory. No package: " + packageName);
+            Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
-        deleteCodeCacheDirsLIF(pkg);
+        clearAppDataLeafLIF(pkg, userId, flags);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
+        }
     }
 
-    private void deleteCodeCacheDirsLIF(PackageParser.Package pkg) {
-        // TODO: triage flags as part of 26466827
-        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
-
-        int[] users = sUserManager.getUserIds();
-        int res = 0;
-        for (int user : users) {
-            // Remove the parent code cache
+    private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+        final PackageSetting ps;
+        synchronized (mPackages) {
+            ps = mSettings.mPackages.get(pkg.packageName);
+        }
+        for (int realUserId : resolveUserIds(userId)) {
+            final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
             try {
-                mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, user,
-                        flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+                mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,
+                        ceDataInode);
             } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to delete code cache directory", e);
+                Slog.w(TAG, String.valueOf(e));
             }
-            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                PackageParser.Package childPkg = pkg.childPackages.get(i);
-                // Remove the child code cache
-                try {
-                    mInstaller.clearAppData(childPkg.volumeUuid, childPkg.packageName,
-                            user, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-                } catch (InstallerException e) {
-                    Slog.w(TAG, "Failed to delete code cache directory", e);
-                }
+        }
+    }
+
+    private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
+        if (pkg == null) {
+            Slog.wtf(TAG, "Package was null!", new Throwable());
+            return;
+        }
+        destroyAppDataLeafLIF(pkg, userId, flags);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
+        }
+    }
+
+    private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+        final PackageSetting ps;
+        synchronized (mPackages) {
+            ps = mSettings.mPackages.get(pkg.packageName);
+        }
+        for (int realUserId : resolveUserIds(userId)) {
+            final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
+            try {
+                mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,
+                        ceDataInode);
+            } catch (InstallerException e) {
+                Slog.w(TAG, String.valueOf(e));
             }
         }
     }
 
+    private void destroyAppProfilesLIF(PackageParser.Package pkg) {
+        if (pkg == null) {
+            Slog.wtf(TAG, "Package was null!", new Throwable());
+            return;
+        }
+        destroyAppProfilesLeafLIF(pkg);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            destroyAppProfilesLeafLIF(pkg.childPackages.get(i));
+        }
+    }
+
+    private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) {
+        try {
+            mInstaller.destroyAppProfiles(pkg.packageName);
+        } catch (InstallerException e) {
+            Slog.w(TAG, String.valueOf(e));
+        }
+    }
+
+    private void clearAppProfilesLIF(PackageParser.Package pkg) {
+        if (pkg == null) {
+            Slog.wtf(TAG, "Package was null!", new Throwable());
+            return;
+        }
+        clearAppProfilesLeafLIF(pkg);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            clearAppProfilesLeafLIF(pkg.childPackages.get(i));
+        }
+    }
+
+    private void clearAppProfilesLeafLIF(PackageParser.Package pkg) {
+        try {
+            mInstaller.clearAppProfiles(pkg.packageName);
+        } catch (InstallerException e) {
+            Slog.w(TAG, String.valueOf(e));
+        }
+    }
+
     private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime,
             long lastUpdateTime) {
         // Set parent install/update time
@@ -7607,7 +7634,9 @@
         } finally {
             if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
                 // DELETE_DATA_ON_FAILURES is only used by frozen paths
-                removeDataDirsLIF(pkg.volumeUuid, pkg.packageName);
+                destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+                        StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+                destroyAppProfilesLIF(pkg);
             }
         }
     }
@@ -11239,7 +11268,10 @@
 
             if (installed) {
                 if (pkgSetting.pkg != null) {
-                    prepareAppDataAfterInstall(pkgSetting.pkg);
+                    synchronized (mInstallLock) {
+                        // We don't need to freeze for a brand new install
+                        prepareAppDataAfterInstallLIF(pkgSetting.pkg);
+                    }
                 }
                 sendPackageAddedForUser(packageName, pkgSetting, userId);
             }
@@ -13365,7 +13397,12 @@
             synchronized (mInstallLock) {
                 // Clean up both app data and code
                 // All package moves are frozen until finished
-                removeDataDirsLIF(volumeUuid, move.packageName);
+                try {
+                    mInstaller.destroyAppData(volumeUuid, move.packageName, UserHandle.USER_ALL,
+                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+                } catch (InstallerException e) {
+                    Slog.w(TAG, String.valueOf(e));
+                }
                 removeCodePathLI(codeFile);
             }
             return true;
@@ -13546,7 +13583,7 @@
             updateSettingsLI(newPackage, installerPackageName, null, res, user);
 
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                prepareAppDataAfterInstall(newPackage);
+                prepareAppDataAfterInstallLIF(newPackage);
 
             } else {
                 // Remove package from internal structures, but keep around any
@@ -13634,6 +13671,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();
         }
@@ -13732,8 +13779,9 @@
                 sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
             }
 
-            deleteCodeCacheDirsLIF(pkg);
-            deleteProfilesLIF(pkg, /*destroy*/ false);
+            clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+                    | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+            clearAppProfilesLIF(pkg);
 
             try {
                 final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
@@ -13760,7 +13808,7 @@
                         childPs.oldCodePaths = ps.oldCodePaths;
                     }
                 }
-                prepareAppDataAfterInstall(newPackage);
+                prepareAppDataAfterInstallLIF(newPackage);
                 addedPkg = true;
             } catch (PackageManagerException e) {
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
@@ -13864,8 +13912,9 @@
         }
 
         // Successfully disabled the old package. Now proceed with re-installation
-        deleteCodeCacheDirsLIF(pkg);
-        deleteProfilesLIF(pkg, /*destroy*/ false);
+        clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
+                | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+        clearAppProfilesLIF(pkg);
 
         res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
         pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
@@ -13881,15 +13930,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
@@ -13921,7 +13961,7 @@
                 }
 
                 updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
-                prepareAppDataAfterInstall(newPackage);
+                prepareAppDataAfterInstallLIF(newPackage);
             }
         } catch (PackageManagerException e) {
             res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
@@ -14500,7 +14540,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.
@@ -14508,10 +14547,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)) {
@@ -15074,11 +15118,12 @@
             PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = ps.name;
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
-        removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
         // Retrieve object to delete permissions for shared user later on
+        final PackageParser.Package deletedPkg;
         final PackageSetting deletedPs;
         // reader
         synchronized (mPackages) {
+            deletedPkg = mPackages.get(packageName);
             deletedPs = mSettings.mPackages.get(packageName);
             if (outInfo != null) {
                 outInfo.removedPackage = packageName;
@@ -15087,13 +15132,19 @@
                         : null;
             }
         }
-        if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
-            removeDataDirsLIF(ps.volumeUuid, ps.name);
+
+        removePackageLI(ps, (flags & REMOVE_CHATTY) != 0);
+
+        if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+            destroyAppDataLIF(deletedPkg, UserHandle.USER_ALL,
+                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+            destroyAppProfilesLIF(deletedPkg);
             if (outInfo != null) {
                 outInfo.dataRemoved = true;
             }
             schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
         }
+
         // writer
         synchronized (mPackages) {
             if (deletedPs != null) {
@@ -15268,7 +15319,7 @@
             return false;
         }
 
-        prepareAppDataAfterInstall(newPkg);
+        prepareAppDataAfterInstallLIF(newPkg);
 
         // writer
         synchronized (mPackages) {
@@ -15594,7 +15645,7 @@
             if (DEBUG_REMOVE) {
                 Slog.d(TAG, "Marking package:" + ps.name + " uninstalled for user:" + nextUserId);
             }
-            ps.setUserState(nextUserId, COMPONENT_ENABLED_STATE_DEFAULT,
+            ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
                     false /*installed*/, true /*stopped*/, true /*notLaunched*/,
                     false /*hidden*/, false /*suspended*/, null, null, null,
                     false /*blockUninstall*/,
@@ -15604,6 +15655,11 @@
 
     private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId,
             PackageRemovedInfo outInfo) {
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(ps.name);
+        }
+
         final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
                 : new int[] {userId};
         for (int nextUserId : userIds) {
@@ -15611,13 +15667,9 @@
                 Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
                         + nextUserId);
             }
-            final int flags =  StorageManager.FLAG_STORAGE_CE|  StorageManager.FLAG_STORAGE_DE;
-            try {
-                mInstaller.destroyAppData(ps.volumeUuid, ps.name, nextUserId, flags);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Couldn't remove cache files for package " + ps.name, e);
-                return false;
-            }
+
+            destroyAppDataLIF(pkg, userId,
+                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             removeKeystoreDataIfNeeded(nextUserId, ps.appId);
             schedulePackageCleaning(ps.name, nextUserId, false);
             synchronized (mPackages) {
@@ -15654,6 +15706,8 @@
     }
 
     private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) {
+        if (DEFAULT_CONTAINER_PACKAGE.equals(packageName)) return;
+
         final boolean mounted;
         if (Environment.isExternalStorageEmulated()) {
             mounted = true;
@@ -15713,14 +15767,15 @@
     @Override
     public void clearApplicationProfileData(String packageName) {
         enforceSystemOrRoot("Only the system can clear all profile data");
-        synchronized (mInstallLock) {
-            try (PackageFreezer freezer = freezePackage(packageName,
-                    "clearApplicationProfileData")) {
-                try {
-                    mInstaller.clearAppProfiles(packageName);
-                } catch (InstallerException ex) {
-                    Log.e(TAG, "Could not clear profile data of package " + packageName);
-                }
+
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+
+        try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
+            synchronized (mInstallLock) {
+                clearAppProfilesLIF(pkg);
             }
         }
     }
@@ -15744,13 +15799,13 @@
             public void run() {
                 mHandler.removeCallbacks(this);
                 final boolean succeeded;
-                synchronized (mInstallLock) {
-                    try (PackageFreezer freezer = freezePackage(packageName,
-                            "clearApplicationUserData")) {
+                try (PackageFreezer freezer = freezePackage(packageName,
+                        "clearApplicationUserData")) {
+                    synchronized (mInstallLock) {
                         succeeded = clearApplicationUserDataLIF(packageName, userId);
                     }
+                    clearExternalStorageDataSync(packageName, userId, true);
                 }
-                clearExternalStorageDataSync(packageName, userId, true);
                 if (succeeded) {
                     // invoke DeviceStorageMonitor's update method to clear any notifications
                     DeviceStorageMonitorInternal dsm = LocalServices
@@ -15796,35 +15851,22 @@
             resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
         }
 
-        // Always delete data directories for package, even if we found no other
-        // record of app. This helps users recover from UID mismatches without
-        // resorting to a full data wipe.
-        // TODO: triage flags as part of 26466827
-        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
-        try {
-            mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags);
-        } catch (InstallerException e) {
-            Slog.w(TAG, "Couldn't remove cache files for package " + packageName, e);
-            return false;
-        }
+        clearAppDataLIF(pkg, userId,
+                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
 
         final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
         removeKeystoreDataIfNeeded(userId, appId);
 
-        // Create a native library symlink only if we have native libraries
-        // and if the native libraries are 32 bit libraries. We do not provide
-        // this symlink for 64 bit libraries.
-        if (pkg.applicationInfo.primaryCpuAbi != null &&
-                !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
-            final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
-            try {
-                mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
-                        nativeLibPath, userId);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed linking native library dir", e);
-                return false;
-            }
+        final UserManager um = mContext.getSystemService(UserManager.class);
+        final int flags;
+        if (um.isUserUnlocked(userId)) {
+            flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
+        } else if (um.isUserRunning(userId)) {
+            flags = StorageManager.FLAG_STORAGE_DE;
+        } else {
+            flags = 0;
         }
+        prepareAppDataContentsLIF(pkg, userId, flags);
 
         return true;
     }
@@ -15994,56 +16036,35 @@
                 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();
+
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+
         mHandler.post(new Runnable() {
             public void run() {
-                mHandler.removeCallbacks(this);
-                final boolean succeded;
-                synchronized (mInstallLock) {
-                    succeded = deleteApplicationCacheFilesLI(packageName, userId);
+                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);
                 }
-                clearExternalStorageDataSync(packageName, userId, false);
                 if (observer != null) {
                     try {
-                        observer.onRemoveCompleted(packageName, succeded);
+                        observer.onRemoveCompleted(packageName, true);
                     } catch (RemoteException e) {
                         Log.i(TAG, "Observer no longer exists.");
                     }
-                } //end if observer
-            } //end run
+                }
+            }
         });
     }
 
-    private boolean deleteApplicationCacheFilesLI(String packageName, int userId) {
-        if (packageName == null) {
-            Slog.w(TAG, "Attempt to delete null packageName.");
-            return false;
-        }
-        PackageParser.Package p;
-        synchronized (mPackages) {
-            p = mPackages.get(packageName);
-        }
-        if (p == null) {
-            Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
-            return false;
-        }
-        final ApplicationInfo applicationInfo = p.applicationInfo;
-        if (applicationInfo == null) {
-            Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
-            return false;
-        }
-        // TODO: triage flags as part of 26466827
-        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
-        try {
-            mInstaller.clearAppData(p.volumeUuid, packageName, userId,
-                    flags | Installer.FLAG_CLEAR_CACHE_ONLY);
-        } catch (InstallerException e) {
-            Slog.w(TAG, "Couldn't remove cache files for package "
-                    + packageName + " u" + userId, e);
-            return false;
-        }
-        return true;
-    }
-
     @Override
     public void getPackageSizeInfo(final String packageName, int userHandle,
             final IPackageStatsObserver observer) {
@@ -16064,90 +16085,24 @@
         mHandler.sendMessage(msg);
     }
 
-    private boolean getPackageSizeInfoLI(String packageName, int userHandle,
-            PackageStats pStats) {
-        if (packageName == null) {
-            Slog.w(TAG, "Attempt to get size of null packageName.");
-            return false;
-        }
-        PackageParser.Package p;
-        boolean dataOnly = false;
-        String libDirRoot = null;
-        String asecPath = null;
-        PackageSetting ps = null;
+    private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
+        final PackageSetting ps;
         synchronized (mPackages) {
-            p = mPackages.get(packageName);
             ps = mSettings.mPackages.get(packageName);
-            if(p == null) {
-                dataOnly = true;
-                if((ps == null) || (ps.pkg == null)) {
-                    Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
-                    return false;
-                }
-                p = ps.pkg;
-            }
-            if (ps != null) {
-                libDirRoot = ps.legacyNativeLibraryPathString;
-            }
-            if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
-                    if (secureContainerId != null) {
-                        asecPath = PackageHelper.getSdFilesystem(secureContainerId);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-            }
-        }
-        String publicSrcDir = null;
-        if(!dataOnly) {
-            final ApplicationInfo applicationInfo = p.applicationInfo;
-            if (applicationInfo == null) {
-                Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
+            if (ps == null) {
+                Slog.w(TAG, "Failed to find settings for " + packageName);
                 return false;
             }
-            if (p.isForwardLocked()) {
-                publicSrcDir = applicationInfo.getBaseResourcePath();
-            }
         }
-        // TODO: extend to measure size of split APKs
-        // TODO(multiArch): Extend getSizeInfo to look at the full subdirectory tree,
-        // not just the first level.
-        // TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not
-        // just the primary.
-        String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
-
-        String apkPath;
-        File packageDir = new File(p.codePath);
-
-        if (packageDir.isDirectory() && p.canHaveOatDir()) {
-            apkPath = packageDir.getAbsolutePath();
-            // If libDirRoot is inside a package dir, set it to null to avoid it being counted twice
-            if (libDirRoot != null && libDirRoot.startsWith(apkPath)) {
-                libDirRoot = null;
-            }
-        } else {
-            apkPath = p.baseCodePath;
-        }
-
-        // TODO: triage flags as part of 26466827
-        final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
         try {
-            mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath,
-                    libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
+            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;
         }
-
-        // Fix-up for forward-locked applications in ASEC containers.
-        if (!isExternal(p)) {
-            pStats.codeSize += pStats.externalCodeSize;
-            pStats.externalCodeSize = 0L;
-        }
-
-        return true;
     }
 
     private int getUidTargetSdkVersionLockedLPr(int uid) {
@@ -17107,8 +17062,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);
@@ -18553,7 +18513,9 @@
                 }
 
                 if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
-                    deleteCodeCacheDirsLIF(ps.volumeUuid, ps.name);
+                    clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
+                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
+                                    | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                 }
             }
         }
@@ -18572,7 +18534,9 @@
             }
 
             sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
-            reconcileAppsData(volumeUuid, user.id, flags);
+            synchronized (mInstallLock) {
+                reconcileAppsDataLI(volumeUuid, user.id, flags);
+            }
         }
 
         synchronized (mPackages) {
@@ -18764,7 +18728,9 @@
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final String volumeUuid = vol.getFsUuid();
-            reconcileAppsData(volumeUuid, userId, flags);
+            synchronized (mInstallLock) {
+                reconcileAppsDataLI(volumeUuid, userId, flags);
+            }
         }
     }
 
@@ -18777,7 +18743,7 @@
      * Verifies that directories exist and that ownership and labeling is
      * correct for all installed apps.
      */
-    private void reconcileAppsData(String volumeUuid, int userId, int flags) {
+    private void reconcileAppsDataLI(String volumeUuid, int userId, int flags) {
         Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
                 + Integer.toHexString(flags));
 
@@ -18804,9 +18770,11 @@
                     assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
                 } catch (PackageManagerException e) {
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
-                    synchronized (mInstallLock) {
-                        destroyAppDataLI(volumeUuid, packageName, userId,
-                                StorageManager.FLAG_STORAGE_CE);
+                    try {
+                        mInstaller.destroyAppData(volumeUuid, packageName, userId,
+                                StorageManager.FLAG_STORAGE_CE, 0);
+                    } catch (InstallerException e2) {
+                        logCriticalInfo(Log.WARN, "Failed to destroy: " + e2);
                     }
                 }
             }
@@ -18821,9 +18789,11 @@
                     assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
                 } catch (PackageManagerException e) {
                     logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
-                    synchronized (mInstallLock) {
-                        destroyAppDataLI(volumeUuid, packageName, userId,
-                                StorageManager.FLAG_STORAGE_DE);
+                    try {
+                        mInstaller.destroyAppData(volumeUuid, packageName, userId,
+                                StorageManager.FLAG_STORAGE_DE, 0);
+                    } catch (InstallerException e2) {
+                        logCriticalInfo(Log.WARN, "Failed to destroy: " + e2);
                     }
                 }
             }
@@ -18846,12 +18816,12 @@
             }
 
             if (ps.getInstalled(userId)) {
-                prepareAppData(volumeUuid, userId, flags, ps.pkg, restoreconNeeded);
+                prepareAppDataLIF(ps.pkg, userId, flags, restoreconNeeded);
 
-                if (maybeMigrateAppData(volumeUuid, userId, ps.pkg)) {
+                if (maybeMigrateAppDataLIF(ps.pkg, userId)) {
                     // We may have just shuffled around app data directories, so
                     // prepare them one more time
-                    prepareAppData(volumeUuid, userId, flags, ps.pkg, restoreconNeeded);
+                    prepareAppDataLIF(ps.pkg, userId, flags, restoreconNeeded);
                 }
 
                 preparedCount++;
@@ -18883,16 +18853,7 @@
      * <p>
      * <em>Note: To avoid a deadlock, do not call this method with {@code mPackages} lock held</em>
      */
-    private void prepareAppDataAfterInstall(PackageParser.Package pkg) {
-        prepareAppDataAfterInstallInternal(pkg);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPackage = pkg.childPackages.get(i);
-            prepareAppDataAfterInstallInternal(childPackage);
-        }
-    }
-
-    private void prepareAppDataAfterInstallInternal(PackageParser.Package pkg) {
+    private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
         final PackageSetting ps;
         synchronized (mPackages) {
             ps = mSettings.mPackages.get(pkg.packageName);
@@ -18913,7 +18874,7 @@
             if (ps.getInstalled(user.id)) {
                 // Whenever an app changes, force a restorecon of its data
                 // TODO: when user data is locked, mark that we're still dirty
-                prepareAppData(pkg.volumeUuid, user.id, flags, pkg, true);
+                prepareAppDataLIF(pkg, user.id, flags, true);
             }
         }
     }
@@ -18926,57 +18887,112 @@
      * will try recovering system apps by wiping data; third-party app data is
      * left intact.
      */
-    private void prepareAppData(String volumeUuid, int userId, int flags,
-            PackageParser.Package pkg, boolean restoreconNeeded) {
+    private void prepareAppDataLIF(PackageParser.Package pkg, int userId, int flags,
+            boolean restoreconNeeded) {
+        if (pkg == null) {
+            Slog.wtf(TAG, "Package was null!", new Throwable());
+            return;
+        }
+        prepareAppDataLeafLIF(pkg, userId, flags, restoreconNeeded);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags, restoreconNeeded);
+        }
+    }
+
+    private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags,
+            boolean restoreconNeeded) {
         if (DEBUG_APP_DATA) {
             Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
                     + Integer.toHexString(flags) + (restoreconNeeded ? " restoreconNeeded" : ""));
         }
 
+        final String volumeUuid = pkg.volumeUuid;
         final String packageName = pkg.packageName;
         final ApplicationInfo app = pkg.applicationInfo;
         final int appId = UserHandle.getAppId(app.uid);
 
         Preconditions.checkNotNull(app.seinfo);
 
-        synchronized (mInstallLock) {
-            try {
-                mInstaller.createAppData(volumeUuid, packageName, userId, flags,
-                        appId, app.seinfo, app.targetSdkVersion);
-            } catch (InstallerException e) {
-                if (app.isSystemApp()) {
-                    logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
-                            + ", but trying to recover: " + e);
-                    destroyAppDataLI(volumeUuid, packageName, userId, flags);
-                    try {
-                        mInstaller.createAppData(volumeUuid, packageName, userId, flags,
-                                appId, app.seinfo, app.targetSdkVersion);
-                        logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
-                    } catch (InstallerException e2) {
-                        logCriticalInfo(Log.DEBUG, "Recovery failed!");
-                    }
-                } else {
-                    Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
+        try {
+            mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+                    appId, app.seinfo, app.targetSdkVersion);
+        } catch (InstallerException e) {
+            if (app.isSystemApp()) {
+                logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+                        + ", but trying to recover: " + e);
+                destroyAppDataLeafLIF(pkg, userId, flags);
+                try {
+                    mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+                            appId, app.seinfo, app.targetSdkVersion);
+                    logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
+                } catch (InstallerException e2) {
+                    logCriticalInfo(Log.DEBUG, "Recovery failed!");
                 }
+            } else {
+                Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
             }
+        }
 
-            if (restoreconNeeded) {
-                restoreconAppDataLI(volumeUuid, packageName, userId, flags, appId, app.seinfo);
+        if (restoreconNeeded) {
+            try {
+                mInstaller.restoreconAppData(volumeUuid, packageName, userId, flags, appId,
+                        app.seinfo);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Failed to restorecon for " + packageName + ": " + e);
             }
+        }
 
-            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
-                // Create a native library symlink only if we have native libraries
-                // and if the native libraries are 32 bit libraries. We do not provide
-                // this symlink for 64 bit libraries.
-                if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
-                    final String nativeLibPath = app.nativeLibraryDir;
-                    try {
-                        mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
-                                nativeLibPath, userId);
-                    } catch (InstallerException e) {
-                        Slog.e(TAG, "Failed to link native for " + packageName + ": " + e);
+        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+            try {
+                // CE storage is unlocked right now, so read out the inode and
+                // remember for use later when it's locked
+                // TODO: mark this structure as dirty so we persist it!
+                final long ceDataInode = mInstaller.getAppDataInode(volumeUuid, packageName, userId,
+                        StorageManager.FLAG_STORAGE_CE);
+                synchronized (mPackages) {
+                    final PackageSetting ps = mSettings.mPackages.get(packageName);
+                    if (ps != null) {
+                        ps.setCeDataInode(ceDataInode, userId);
                     }
                 }
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Failed to find inode for " + packageName + ": " + e);
+            }
+        }
+
+        prepareAppDataContentsLeafLIF(pkg, userId, flags);
+    }
+
+    private void prepareAppDataContentsLIF(PackageParser.Package pkg, int userId, int flags) {
+        if (pkg == null) {
+            Slog.wtf(TAG, "Package was null!", new Throwable());
+            return;
+        }
+        prepareAppDataContentsLeafLIF(pkg, userId, flags);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            prepareAppDataContentsLeafLIF(pkg.childPackages.get(i), userId, flags);
+        }
+    }
+
+    private void prepareAppDataContentsLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+        final String volumeUuid = pkg.volumeUuid;
+        final String packageName = pkg.packageName;
+        final ApplicationInfo app = pkg.applicationInfo;
+
+        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+            // Create a native library symlink only if we have native libraries
+            // and if the native libraries are 32 bit libraries. We do not provide
+            // this symlink for 64 bit libraries.
+            if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
+                final String nativeLibPath = app.nativeLibraryDir;
+                try {
+                    mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
+                            nativeLibPath, userId);
+                } catch (InstallerException e) {
+                    Slog.e(TAG, "Failed to link native for " + packageName + ": " + e);
+                }
             }
         }
     }
@@ -18986,18 +19002,17 @@
      * CE/DE data to match the {@code defaultToDeviceProtectedStorage} flag
      * requested by the app.
      */
-    private boolean maybeMigrateAppData(String volumeUuid, int userId, PackageParser.Package pkg) {
+    private boolean maybeMigrateAppDataLIF(PackageParser.Package pkg, int userId) {
         if (pkg.isSystemApp() && !StorageManager.isFileEncryptedNativeOrEmulated()
                 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
             final int storageTarget = pkg.applicationInfo.isDefaultToDeviceProtectedStorage()
                     ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
-            synchronized (mInstallLock) {
-                try {
-                    mInstaller.migrateAppData(volumeUuid, pkg.packageName, userId, storageTarget);
-                } catch (InstallerException e) {
-                    logCriticalInfo(Log.WARN,
-                            "Failed to migrate " + pkg.packageName + ": " + e.getMessage());
-                }
+            try {
+                mInstaller.migrateAppData(pkg.volumeUuid, pkg.packageName, userId,
+                        storageTarget);
+            } catch (InstallerException e) {
+                logCriticalInfo(Log.WARN,
+                        "Failed to migrate " + pkg.packageName + ": " + e.getMessage());
             }
             return true;
         } else {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 2a96562..9d04472 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -323,6 +323,14 @@
         return res;
     }
 
+    long getCeDataInode(int userId) {
+        return readUserState(userId).ceDataInode;
+    }
+
+    void setCeDataInode(long ceDataInode, int userId) {
+        modifyUserState(userId).ceDataInode = ceDataInode;
+    }
+
     boolean getStopped(int userId) {
         return readUserState(userId).stopped;
     }
@@ -363,12 +371,13 @@
         modifyUserState(userId).blockUninstall = blockUninstall;
     }
 
-    void setUserState(int userId, int enabled, boolean installed, boolean stopped,
+    void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden, boolean suspended,
             String lastDisableAppCaller, ArraySet<String> enabledComponents,
             ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
             int linkGeneration) {
         PackageUserState state = modifyUserState(userId);
+        state.ceDataInode = ceDataInode;
         state.enabled = enabled;
         state.installed = installed;
         state.stopped = stopped;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 8f7cd5e..847f993 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -195,23 +195,26 @@
     private static final String ATTR_NAME = "name";
     private static final String ATTR_USER = "user";
     private static final String ATTR_CODE = "code";
-    private static final String ATTR_NOT_LAUNCHED = "nl";
-    private static final String ATTR_ENABLED = "enabled";
     private static final String ATTR_GRANTED = "granted";
     private static final String ATTR_FLAGS = "flags";
-    private static final String ATTR_ENABLED_CALLER = "enabledCaller";
+
+    private static final String ATTR_CE_DATA_INODE = "ceDataInode";
+    private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_STOPPED = "stopped";
+    private static final String ATTR_NOT_LAUNCHED = "nl";
     // Legacy, here for reading older versions of the package-restrictions.
     private static final String ATTR_BLOCKED = "blocked";
     // New name for the above attribute.
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_SUSPENDED = "suspended";
-    private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+    private static final String ATTR_ENABLED = "enabled";
+    private static final String ATTR_ENABLED_CALLER = "enabledCaller";
     private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
+    private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
+
     private static final String ATTR_PACKAGE_NAME = "packageName";
     private static final String ATTR_FINGERPRINT = "fingerprint";
-    private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
     private static final String ATTR_VOLUME_UUID = "volumeUuid";
     private static final String ATTR_SDK_VERSION = "sdkVersion";
     private static final String ATTR_DATABASE_VERSION = "databaseVersion";
@@ -790,7 +793,7 @@
                                     || (installUserId == UserHandle.USER_ALL
                                         && !isAdbInstallDisallowed(userManager, user.id))
                                     || installUserId == user.id;
-                            p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
+                            p.setUserState(user.id, 0, COMPONENT_ENABLED_STATE_DEFAULT,
                                     installed,
                                     true, // stopped,
                                     true, // notLaunched
@@ -1574,7 +1577,7 @@
                     // in the stopped state, but not at first boot.  Also
                     // consider all applications to be installed.
                     for (PackageSetting pkg : mPackages.values()) {
-                        pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT,
+                        pkg.setUserState(userId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
                                 true,   // installed
                                 false,  // stopped
                                 false,  // notLaunched
@@ -1626,17 +1629,16 @@
                         XmlUtils.skipCurrentTag(parser);
                         continue;
                     }
-                    final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
-                    final int enabled = enabledStr == null
-                            ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr);
-                    final String enabledCaller = parser.getAttributeValue(null,
-                            ATTR_ENABLED_CALLER);
-                    final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED);
-                    final boolean installed = installedStr == null
-                            ? true : Boolean.parseBoolean(installedStr);
-                    final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
-                    final boolean stopped = stoppedStr == null
-                            ? false : Boolean.parseBoolean(stoppedStr);
+
+                    final long ceDataInode = XmlUtils.readLongAttribute(parser, ATTR_CE_DATA_INODE,
+                            0);
+                    final boolean installed = XmlUtils.readBooleanAttribute(parser, ATTR_INSTALLED,
+                            true);
+                    final boolean stopped = XmlUtils.readBooleanAttribute(parser, ATTR_STOPPED,
+                            false);
+                    final boolean notLaunched = XmlUtils.readBooleanAttribute(parser,
+                            ATTR_NOT_LAUNCHED, false);
+
                     // For backwards compatibility with the previous name of "blocked", which
                     // now means hidden, read the old attribute as well.
                     final String blockedStr = parser.getAttributeValue(null, ATTR_BLOCKED);
@@ -1645,25 +1647,21 @@
                     final String hiddenStr = parser.getAttributeValue(null, ATTR_HIDDEN);
                     hidden = hiddenStr == null
                             ? hidden : Boolean.parseBoolean(hiddenStr);
-                    final String suspendedStr = parser.getAttributeValue(null, ATTR_SUSPENDED);
-                    final boolean suspended = suspendedStr == null
-                            ? false : Boolean.parseBoolean(suspendedStr);
-                    final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
-                    final boolean notLaunched = stoppedStr == null
-                            ? false : Boolean.parseBoolean(notLaunchedStr);
-                    final String blockUninstallStr = parser.getAttributeValue(null,
-                            ATTR_BLOCK_UNINSTALL);
-                    final boolean blockUninstall = blockUninstallStr == null
-                            ? false : Boolean.parseBoolean(blockUninstallStr);
 
-                    final String verifStateStr =
-                            parser.getAttributeValue(null, ATTR_DOMAIN_VERIFICATON_STATE);
-                    final int verifState = (verifStateStr == null) ?
-                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
-                            Integer.parseInt(verifStateStr);
+                    final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
+                            false);
+                    final boolean blockUninstall = XmlUtils.readBooleanAttribute(parser,
+                            ATTR_BLOCK_UNINSTALL, false);
+                    final int enabled = XmlUtils.readIntAttribute(parser, ATTR_ENABLED,
+                            COMPONENT_ENABLED_STATE_DEFAULT);
+                    final String enabledCaller = parser.getAttributeValue(null,
+                            ATTR_ENABLED_CALLER);
 
-                    final String linkGenStr = parser.getAttributeValue(null, ATTR_APP_LINK_GENERATION);
-                    final int linkGeneration = linkGenStr == null ? 0 : Integer.parseInt(linkGenStr);
+                    final int verifState = XmlUtils.readIntAttribute(parser,
+                            ATTR_DOMAIN_VERIFICATON_STATE,
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+                    final int linkGeneration = XmlUtils.readIntAttribute(parser,
+                            ATTR_APP_LINK_GENERATION, 0);
                     if (linkGeneration > maxAppLinkGeneration) {
                         maxAppLinkGeneration = linkGeneration;
                     }
@@ -1687,8 +1685,8 @@
                         }
                     }
 
-                    ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
-                            suspended, enabledCaller, enabledComponents, disabledComponents,
+                    ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
+                            hidden, suspended, enabledCaller, enabledComponents, disabledComponents,
                             blockUninstall, verifState, linkGeneration);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
@@ -1927,80 +1925,69 @@
             serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
 
             for (final PackageSetting pkg : mPackages.values()) {
-                PackageUserState ustate = pkg.readUserState(userId);
-                if (ustate.stopped || ustate.notLaunched || !ustate.installed
-                        || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
-                        || ustate.hidden
-                        || ustate.suspended
-                        || (ustate.enabledComponents != null
-                                && ustate.enabledComponents.size() > 0)
-                        || (ustate.disabledComponents != null
-                                && ustate.disabledComponents.size() > 0)
-                        || ustate.blockUninstall
-                        || (ustate.domainVerificationStatus !=
-                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
-                    serializer.startTag(null, TAG_PACKAGE);
-                    serializer.attribute(null, ATTR_NAME, pkg.name);
-                    if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);
+                final PackageUserState ustate = pkg.readUserState(userId);
+                if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);
 
-                    if (!ustate.installed) {
-                        serializer.attribute(null, ATTR_INSTALLED, "false");
-                    }
-                    if (ustate.stopped) {
-                        serializer.attribute(null, ATTR_STOPPED, "true");
-                    }
-                    if (ustate.notLaunched) {
-                        serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
-                    }
-                    if (ustate.hidden) {
-                        serializer.attribute(null, ATTR_HIDDEN, "true");
-                    }
-                    if (ustate.suspended) {
-                        serializer.attribute(null, ATTR_SUSPENDED, "true");
-                    }
-                    if (ustate.blockUninstall) {
-                        serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
-                    }
-                    if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
-                        serializer.attribute(null, ATTR_ENABLED,
-                                Integer.toString(ustate.enabled));
-                        if (ustate.lastDisableAppCaller != null) {
-                            serializer.attribute(null, ATTR_ENABLED_CALLER,
-                                    ustate.lastDisableAppCaller);
-                        }
-                    }
-                    if (ustate.domainVerificationStatus !=
-                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
-                        serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
-                                Integer.toString(ustate.domainVerificationStatus));
-                    }
-                    if (ustate.appLinkGeneration != 0) {
-                        serializer.attribute(null, ATTR_APP_LINK_GENERATION,
-                                Integer.toString(ustate.appLinkGeneration));
-                    }
-                    if (ustate.enabledComponents != null
-                            && ustate.enabledComponents.size() > 0) {
-                        serializer.startTag(null, TAG_ENABLED_COMPONENTS);
-                        for (final String name : ustate.enabledComponents) {
-                            serializer.startTag(null, TAG_ITEM);
-                            serializer.attribute(null, ATTR_NAME, name);
-                            serializer.endTag(null, TAG_ITEM);
-                        }
-                        serializer.endTag(null, TAG_ENABLED_COMPONENTS);
-                    }
-                    if (ustate.disabledComponents != null
-                            && ustate.disabledComponents.size() > 0) {
-                        serializer.startTag(null, TAG_DISABLED_COMPONENTS);
-                        for (final String name : ustate.disabledComponents) {
-                            serializer.startTag(null, TAG_ITEM);
-                            serializer.attribute(null, ATTR_NAME, name);
-                            serializer.endTag(null, TAG_ITEM);
-                        }
-                        serializer.endTag(null, TAG_DISABLED_COMPONENTS);
-                    }
-
-                    serializer.endTag(null, TAG_PACKAGE);
+                serializer.startTag(null, TAG_PACKAGE);
+                serializer.attribute(null, ATTR_NAME, pkg.name);
+                if (ustate.ceDataInode != 0) {
+                    XmlUtils.writeLongAttribute(serializer, ATTR_CE_DATA_INODE, ustate.ceDataInode);
                 }
+                if (!ustate.installed) {
+                    serializer.attribute(null, ATTR_INSTALLED, "false");
+                }
+                if (ustate.stopped) {
+                    serializer.attribute(null, ATTR_STOPPED, "true");
+                }
+                if (ustate.notLaunched) {
+                    serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
+                }
+                if (ustate.hidden) {
+                    serializer.attribute(null, ATTR_HIDDEN, "true");
+                }
+                if (ustate.suspended) {
+                    serializer.attribute(null, ATTR_SUSPENDED, "true");
+                }
+                if (ustate.blockUninstall) {
+                    serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
+                }
+                if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+                    serializer.attribute(null, ATTR_ENABLED,
+                            Integer.toString(ustate.enabled));
+                    if (ustate.lastDisableAppCaller != null) {
+                        serializer.attribute(null, ATTR_ENABLED_CALLER,
+                                ustate.lastDisableAppCaller);
+                    }
+                }
+                if (ustate.domainVerificationStatus !=
+                        PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                    XmlUtils.writeIntAttribute(serializer, ATTR_DOMAIN_VERIFICATON_STATE,
+                            ustate.domainVerificationStatus);
+                }
+                if (ustate.appLinkGeneration != 0) {
+                    XmlUtils.writeIntAttribute(serializer, ATTR_APP_LINK_GENERATION,
+                            ustate.appLinkGeneration);
+                }
+                if (!ArrayUtils.isEmpty(ustate.enabledComponents)) {
+                    serializer.startTag(null, TAG_ENABLED_COMPONENTS);
+                    for (final String name : ustate.enabledComponents) {
+                        serializer.startTag(null, TAG_ITEM);
+                        serializer.attribute(null, ATTR_NAME, name);
+                        serializer.endTag(null, TAG_ITEM);
+                    }
+                    serializer.endTag(null, TAG_ENABLED_COMPONENTS);
+                }
+                if (!ArrayUtils.isEmpty(ustate.disabledComponents)) {
+                    serializer.startTag(null, TAG_DISABLED_COMPONENTS);
+                    for (final String name : ustate.disabledComponents) {
+                        serializer.startTag(null, TAG_ITEM);
+                        serializer.attribute(null, ATTR_NAME, name);
+                        serializer.endTag(null, TAG_ITEM);
+                    }
+                    serializer.endTag(null, TAG_DISABLED_COMPONENTS);
+                }
+
+                serializer.endTag(null, TAG_PACKAGE);
             }
 
             writePreferredActivitiesLPr(serializer, userId, true);
@@ -4532,6 +4519,8 @@
 
         for (UserInfo user : users) {
             pw.print(prefix); pw.print("  User "); pw.print(user.id); pw.print(": ");
+            pw.print("ceDataInode=");
+            pw.print(ps.getCeDataInode(user.id));
             pw.print(" installed=");
             pw.print(ps.getInstalled(user.id));
             pw.print(" hidden=");
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1076a7a..58559a5 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;
@@ -522,7 +523,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 +535,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;
@@ -586,7 +587,7 @@
             throw ShortcutService.throwForInvalidTag(depth, tag);
         }
         return new ShortcutInfo(
-                id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
+                userId, id, packageName, activityComponent, /* icon =*/ null, title, text, 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..1641e1d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -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);
+        mMaxDailyUpdates = Math.max(0, (int) parser.getLong(
+                ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES));
 
-        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) {
@@ -1419,6 +1425,8 @@
 
     @Override
     public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
+        verifyCaller(packageName, userId);
+
         synchronized (mLock) {
             return mMaxIconDimension;
         }
@@ -1439,7 +1447,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 +1538,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 +1602,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 +1620,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 +1636,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 +1665,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 +1744,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 +1763,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 +1879,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 +2068,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("    maxDailyUpdates:");
+            pw.println(mMaxDailyUpdates);
+            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 +2173,9 @@
                     case "reset-throttling":
                         handleResetThrottling();
                         break;
+                    case "reset-all-throttling":
+                        handleResetAllThrottling();
+                        break;
                     case "override-config":
                         handleOverrideConfig();
                         break;
@@ -2162,6 +2194,9 @@
                     case "unload-user":
                         handleUnloadUser();
                         break;
+                    case "clear-shortcuts":
+                        handleClearShortcuts();
+                        break;
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -2181,9 +2216,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 +2241,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 +2265,8 @@
 
             final String packageName = getNextArgRequired();
 
+            Slog.i(TAG, "cmd: handleResetPackageThrottling: " + packageName);
+
             synchronized (mLock) {
                 getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
                 saveUserLocked(mUserId);
@@ -2226,6 +2276,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 +2286,8 @@
         }
 
         private void handleResetConfig() {
+            Slog.i(TAG, "cmd: handleResetConfig");
+
             synchronized (mLock) {
                 loadConfigurationLocked();
             }
@@ -2278,8 +2332,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 ===
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/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/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/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index fb3c6ec..fe32104 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -49,6 +49,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -265,70 +266,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);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 9486cfd..a5d68da 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;
 
@@ -67,6 +67,7 @@
     @Override
     public WebViewProviderInfo[] getWebViewPackages() {
         int numFallbackPackages = 0;
+        int numAvailableByDefaultPackages = 0;
         XmlResourceParser parser = null;
         List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
         try {
@@ -82,12 +83,12 @@
                 if (element.equals(TAG_WEBVIEW_PROVIDER)) {
                     String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
                     if (packageName == null) {
-                        throw new MissingWebViewPackageException(
+                        throw new AndroidRuntimeException(
                                 "WebView provider in framework resources missing package name");
                     }
                     String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
                     if (description == null) {
-                        throw new MissingWebViewPackageException(
+                        throw new AndroidRuntimeException(
                                 "WebView provider in framework resources missing description");
                     }
                     boolean availableByDefault = "true".equals(
@@ -99,22 +100,33 @@
                             readSignatures(parser));
                     if (currentProvider.isFallback) {
                         numFallbackPackages++;
+                        if (!currentProvider.availableByDefault) {
+                            throw new AndroidRuntimeException(
+                                    "Each WebView fallback package must be available by default.");
+                        }
                         if (numFallbackPackages > 1) {
                             throw new AndroidRuntimeException(
-                                    "There can be at most one webview fallback package.");
+                                    "There can be at most one WebView fallback package.");
                         }
                     }
+                    if (currentProvider.availableByDefault) {
+                        numAvailableByDefaultPackages++;
+                    }
                     webViewProviders.add(currentProvider);
                 }
                 else {
-                    Log.e(TAG, "Found an element that is not a webview provider");
+                    Log.e(TAG, "Found an element that is not a WebView provider");
                 }
             }
         } catch (XmlPullParserException | IOException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+            throw new AndroidRuntimeException("Error when parsing WebView config " + e);
         } finally {
             if (parser != null) parser.close();
         }
+        if (numAvailableByDefaultPackages == 0) {
+            throw new AndroidRuntimeException("There must be at least one WebView package "
+                    + "that is available by default");
+        }
         return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
     }
 
@@ -187,7 +199,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 +227,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..bbb4951 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -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..cd976e7 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -160,6 +160,8 @@
             isFallbackEnabled = isEnabledPackage(
                     mSystemInterface.getPackageInfoForProvider(fallbackProvider));
         } catch (NameNotFoundException e) {
+            // No fallback package installed -> early out.
+            return;
         }
 
         if (existsValidNonFallbackProvider
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 72eebb5..d229633 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;
@@ -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);
         }
     }
 
@@ -3968,9 +3963,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 +6703,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 +6882,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);
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/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..2fd11da 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. */
@@ -598,14 +609,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 +792,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 +889,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 +909,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 +918,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 +927,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 +953,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 +1010,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);
@@ -1684,7 +1509,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 +1553,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 +1572,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.
@@ -2034,9 +1898,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 +1981,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 +2033,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
     }
 
@@ -3224,20 +3132,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();
 
@@ -3607,18 +3501,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 +3533,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 +3564,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 +3595,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 +3623,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 +3651,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 +3679,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),
@@ -4935,7 +4829,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 +4908,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 +4924,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 +4940,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 +4957,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());
@@ -5109,9 +5006,11 @@
     }
 
     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/"))
@@ -5127,7 +5026,9 @@
 
         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());
@@ -5144,7 +5045,7 @@
 
         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());
@@ -5161,7 +5062,7 @@
 
         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());
@@ -5177,7 +5078,7 @@
 
         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());
@@ -5267,7 +5168,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));
@@ -5293,11 +5194,13 @@
         // 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());
@@ -5367,7 +5270,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/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/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/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/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/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();
         }