Merge "Support direct-boot tests." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 9939286..07a4f6d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -71,6 +71,7 @@
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+    field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
     field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
@@ -5900,6 +5901,7 @@
     method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
+    method public boolean isManagedProfile(android.content.ComponentName);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean isProfileOwnerApp(java.lang.String);
@@ -9915,6 +9917,7 @@
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+    field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -13503,6 +13506,7 @@
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getId();
     method public int getMaxDelay();
     method public float getMaximumRange();
     method public int getMinDelay();
@@ -13512,9 +13516,10 @@
     method public float getResolution();
     method public java.lang.String getStringType();
     method public int getType();
-    method public java.util.UUID getUuid();
     method public java.lang.String getVendor();
     method public int getVersion();
+    method public boolean isAdditionalInfoSupported();
+    method public boolean isDynamicSensor();
     method public boolean isWakeUpSensor();
     field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
     field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -13633,8 +13638,9 @@
     method public static void getRotationMatrixFromVector(float[], float[]);
     method public java.util.List<android.hardware.Sensor> getSensorList(int);
     method public deprecated int getSensors();
-    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
-    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
+    method public boolean isDynamicSensorDiscoverySupported();
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13643,7 +13649,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
-    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13708,8 +13714,8 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
-  public static abstract class SensorManager.DynamicSensorConnectionCallback {
-    ctor public SensorManager.DynamicSensorConnectionCallback();
+  public static abstract class SensorManager.DynamicSensorCallback {
+    ctor public SensorManager.DynamicSensorCallback();
     method public void onDynamicSensorConnected(android.hardware.Sensor);
     method public void onDynamicSensorDisconnected(android.hardware.Sensor);
   }
@@ -19806,7 +19812,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
+    method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -20096,7 +20102,7 @@
     field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
     field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
-    field public static final java.lang.String TAG_APERTURE = "FNumber";
+    field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
     field public static final java.lang.String TAG_ARTIST = "Artist";
     field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
@@ -20167,7 +20173,7 @@
     field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
     field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
     field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
-    field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+    field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings";
     field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
     field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
     field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
@@ -20575,12 +20581,13 @@
     field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
     field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
     field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
-    field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
-    field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
-    field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
-    field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
-    field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
-    field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+    field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+    field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+    field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+    field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+    field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+    field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+    field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
     field public static final int H263Level10 = 1; // 0x1
     field public static final int H263Level20 = 2; // 0x2
     field public static final int H263Level30 = 4; // 0x4
@@ -36723,6 +36730,7 @@
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
     field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+    field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
     field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
diff --git a/api/system-current.txt b/api/system-current.txt
index a34f6c6..fe562bb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -103,6 +103,7 @@
     field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
     field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+    field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED";
     field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
     field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
@@ -6047,6 +6048,7 @@
     method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
+    method public boolean isManagedProfile(android.content.ComponentName);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean isProfileOwnerApp(java.lang.String);
@@ -10314,6 +10316,7 @@
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+    field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -13902,6 +13905,7 @@
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getId();
     method public int getMaxDelay();
     method public float getMaximumRange();
     method public int getMinDelay();
@@ -13914,7 +13918,9 @@
     method public java.util.UUID getUuid();
     method public java.lang.String getVendor();
     method public int getVersion();
+    method public boolean isAdditionalInfoSupported();
     method public boolean isDataInjectionSupported();
+    method public boolean isDynamicSensor();
     method public boolean isWakeUpSensor();
     field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
     field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -14039,8 +14045,9 @@
     method public deprecated int getSensors();
     method public boolean initDataInjection(boolean);
     method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
-    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
-    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
+    method public boolean isDynamicSensorDiscoverySupported();
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -14049,7 +14056,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
-    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -14114,8 +14121,8 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
-  public static abstract class SensorManager.DynamicSensorConnectionCallback {
-    ctor public SensorManager.DynamicSensorConnectionCallback();
+  public static abstract class SensorManager.DynamicSensorCallback {
+    ctor public SensorManager.DynamicSensorCallback();
     method public void onDynamicSensorConnected(android.hardware.Sensor);
     method public void onDynamicSensorDisconnected(android.hardware.Sensor);
   }
@@ -15264,6 +15271,7 @@
     ctor public ContextHubInfo();
     method public int describeContents();
     method public int getId();
+    method public int getMaxPacketLengthBytes();
     method public android.hardware.location.MemoryRegion[] getMemoryRegions();
     method public java.lang.String getName();
     method public float getPeakMips();
@@ -15291,10 +15299,6 @@
     method public int sendMessage(int, int, android.hardware.location.ContextHubMessage);
     method public int unloadNanoApp(int);
     method public int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
-    field public static final int ANY_HUB = -1; // 0xffffffff
-    field public static final int MSG_DATA_SEND = 3; // 0x3
-    field public static final int MSG_LOAD_NANO_APP = 1; // 0x1
-    field public static final int MSG_UNLOAD_NANO_APP = 2; // 0x2
   }
 
   public static abstract class ContextHubManager.Callback {
@@ -15488,7 +15492,7 @@
   public class NanoAppInstanceInfo {
     ctor public NanoAppInstanceInfo();
     method public int describeContents();
-    method public int getAppId();
+    method public long getAppId();
     method public int getAppVersion();
     method public int getContexthubId();
     method public int getHandle();
@@ -15499,17 +15503,6 @@
     method public int getNeededWriteMemBytes();
     method public int[] getOutputEvents();
     method public java.lang.String getPublisher();
-    method public void setAppId(int);
-    method public void setAppVersion(int);
-    method public void setContexthubId(int);
-    method public void setHandle(int);
-    method public void setName(java.lang.String);
-    method public void setNeededExecMemBytes(int);
-    method public void setNeededReadMemBytes(int);
-    method public void setNeededSensors(int[]);
-    method public void setNeededWriteMemBytes(int);
-    method public void setOutputEvents(int[]);
-    method public void setPublisher(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR;
   }
@@ -21295,7 +21288,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
+    method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21588,7 +21581,7 @@
     field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
     field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
-    field public static final java.lang.String TAG_APERTURE = "FNumber";
+    field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
     field public static final java.lang.String TAG_ARTIST = "Artist";
     field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
@@ -21659,7 +21652,7 @@
     field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
     field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
     field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
-    field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+    field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings";
     field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
     field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
     field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
@@ -22067,12 +22060,13 @@
     field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
     field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
     field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
-    field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
-    field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
-    field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
-    field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
-    field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
-    field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+    field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+    field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+    field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+    field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+    field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+    field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+    field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
     field public static final int H263Level10 = 1; // 0x1
     field public static final int H263Level20 = 2; // 0x2
     field public static final int H263Level30 = 4; // 0x4
@@ -24624,6 +24618,7 @@
     method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
     method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon, int);
+    method public android.media.tv.TvInputInfo.Builder setLabel(java.lang.CharSequence);
     method public android.media.tv.TvInputInfo.Builder setLabel(int);
     method public android.media.tv.TvInputInfo.Builder setParentId(java.lang.String);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
@@ -31327,8 +31322,8 @@
     field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
     field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
     field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
-    field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
     field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
+    field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
     field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
     field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
     field public static final int USER_ACTIVITY_EVENT_TOUCH = 2; // 0x2
@@ -32671,7 +32666,6 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
     method public abstract void onDisconnected();
-    method public final boolean onUnbind(android.content.Intent);
     method public final void updateRecommendations(java.util.List<android.printservice.recommendation.RecommendationInfo>);
   }
 
@@ -39412,6 +39406,7 @@
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
     field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+    field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
     field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -48855,6 +48850,24 @@
     method public abstract boolean shouldDelayChildPressedState();
   }
 
+  public final class WebViewProviderInfo implements android.os.Parcelable {
+    ctor public WebViewProviderInfo(java.lang.String, java.lang.String, boolean, boolean, java.lang.String[]);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.webkit.WebViewProviderInfo> CREATOR;
+    field public final boolean availableByDefault;
+    field public final java.lang.String description;
+    field public final boolean isFallback;
+    field public final java.lang.String packageName;
+    field public final java.lang.String[] signatures;
+  }
+
+  public final class WebViewUpdateService {
+    method public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages();
+    method public static java.lang.String getCurrentWebViewPackageName();
+    method public static android.webkit.WebViewProviderInfo[] getValidWebViewPackages();
+  }
+
 }
 
 package android.widget {
diff --git a/api/test-current.txt b/api/test-current.txt
index 56b7586..09c55b9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -71,6 +71,7 @@
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+    field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
     field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
@@ -5904,6 +5905,7 @@
     method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
+    method public boolean isManagedProfile(android.content.ComponentName);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean isProfileOwnerApp(java.lang.String);
@@ -9925,6 +9927,7 @@
     field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
     field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
     field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+    field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
     field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -13513,6 +13516,7 @@
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getId();
     method public int getMaxDelay();
     method public float getMaximumRange();
     method public int getMinDelay();
@@ -13522,9 +13526,10 @@
     method public float getResolution();
     method public java.lang.String getStringType();
     method public int getType();
-    method public java.util.UUID getUuid();
     method public java.lang.String getVendor();
     method public int getVersion();
+    method public boolean isAdditionalInfoSupported();
+    method public boolean isDynamicSensor();
     method public boolean isWakeUpSensor();
     field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
     field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -13643,8 +13648,9 @@
     method public static void getRotationMatrixFromVector(float[], float[]);
     method public java.util.List<android.hardware.Sensor> getSensorList(int);
     method public deprecated int getSensors();
-    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
-    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
+    method public boolean isDynamicSensorDiscoverySupported();
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13653,7 +13659,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
-    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13718,8 +13724,8 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
-  public static abstract class SensorManager.DynamicSensorConnectionCallback {
-    ctor public SensorManager.DynamicSensorConnectionCallback();
+  public static abstract class SensorManager.DynamicSensorCallback {
+    ctor public SensorManager.DynamicSensorCallback();
     method public void onDynamicSensorConnected(android.hardware.Sensor);
     method public void onDynamicSensorDisconnected(android.hardware.Sensor);
   }
@@ -19871,7 +19877,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
+    method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -20161,7 +20167,7 @@
     field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
     field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
-    field public static final java.lang.String TAG_APERTURE = "FNumber";
+    field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
     field public static final java.lang.String TAG_ARTIST = "Artist";
     field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
@@ -20232,7 +20238,7 @@
     field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
     field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
     field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
-    field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+    field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings";
     field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
     field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
     field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
@@ -20640,12 +20646,13 @@
     field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
     field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
     field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
-    field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
-    field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
-    field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
-    field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
-    field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
-    field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+    field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+    field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+    field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+    field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+    field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+    field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+    field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
     field public static final int H263Level10 = 1; // 0x1
     field public static final int H263Level20 = 2; // 0x2
     field public static final int H263Level30 = 4; // 0x4
@@ -36795,6 +36802,7 @@
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
     field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+    field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
     field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index e520b40..7465ed9 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -2798,6 +2798,15 @@
         if (account == null) {
             throw new IllegalArgumentException("account is null");
         }
+
+        // Always include the calling package name. This just makes life easier
+        // down stream.
+        final Bundle optionsIn = new Bundle();
+        if (options != null) {
+            optionsIn.putAll(options);
+        }
+        optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
         return new AmsTask(activity, handler, callback) {
             @Override
             public void doWork() throws RemoteException {
@@ -2806,7 +2815,7 @@
                         account,
                         authTokenType,
                         activity != null,
-                        options);
+                        optionsIn);
             }
         }.start();
     }
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index d3ca7ee..4a1aff7 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -239,9 +239,6 @@
     public void onResume(Activity activity, boolean isTopOfTask) {
         // After orientation change, the onResume can come in before the top Activity has
         // left, so if the Activity is not top, wait a second for the top Activity to exit.
-        if (mCalledExitCoordinator == null) {
-            return; // This is the called activity
-        }
         if (isTopOfTask || mEnterTransitionCoordinator == null) {
             restoreExitedViews();
             restoreReenteringViews();
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index cb2130c4..e4fff9d 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -905,10 +905,12 @@
 
         ListenerWrapper wrapper = null;
         synchronized (AlarmManager.class) {
-            final WeakReference<ListenerWrapper> wrapperRef;
-            wrapperRef = sWrappers.get(listener);
-            if (wrapperRef != null) {
-                wrapper = wrapperRef.get();
+            if (sWrappers != null) {
+                final WeakReference<ListenerWrapper> wrapperRef;
+                wrapperRef = sWrappers.get(listener);
+                if (wrapperRef != null) {
+                    wrapper = wrapperRef.get();
+                }
             }
         }
 
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 83dc506..bd55a06 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -58,7 +58,7 @@
      * @param context the parent context
      */
     public DatePickerDialog(@NonNull Context context) {
-        this(context, 0);
+        this(context, 0, null, Calendar.getInstance(), -1, -1, -1);
     }
 
     /**
@@ -70,24 +70,7 @@
      *                   {@code context}'s default alert dialog theme
      */
     public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) {
-        super(context, resolveDialogTheme(context, themeResId));
-
-        final Context themeContext = getContext();
-        final LayoutInflater inflater = LayoutInflater.from(themeContext);
-        final View view = inflater.inflate(R.layout.date_picker_dialog, null);
-        setView(view);
-
-        setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
-        setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
-        setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
-
-        final Calendar calendar = Calendar.getInstance();
-        final int year = calendar.get(Calendar.YEAR);
-        final int monthOfYear = calendar.get(Calendar.MONTH);
-        final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
-        mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
-        mDatePicker.init(year, monthOfYear, dayOfMonth, this);
-        mDatePicker.setValidationCallback(mValidationCallback);
+        this(context, themeResId, null, Calendar.getInstance(), -1, -1, -1);
     }
 
     /**
@@ -104,7 +87,7 @@
      */
     public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener,
             int year, int month, int dayOfMonth) {
-        this(context, 0, listener, year, month, dayOfMonth);
+        this(context, 0, listener, null, year, month, dayOfMonth);
     }
 
     /**
@@ -116,16 +99,40 @@
      *                   {@code context}'s default alert dialog theme
      * @param listener the listener to call when the user sets the date
      * @param year the initially selected year
-     * @param month the initially selected month (0-11 for compatibility with
-     *              {@link Calendar#MONTH})
+     * @param monthOfYear the initially selected month of the year (0-11 for
+     *                    compatibility with {@link Calendar#MONTH})
      * @param dayOfMonth the initially selected day of month (1-31, depending
      *                   on month)
      */
     public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
-            @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) {
-        this(context, themeResId);
+            @Nullable OnDateSetListener listener, int year, int monthOfYear, int dayOfMonth) {
+        this(context, themeResId, listener, null, year, monthOfYear, dayOfMonth);
+    }
 
-        mDatePicker.updateDate(year, month, dayOfMonth);
+    private DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
+            @Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year,
+            int monthOfYear, int dayOfMonth) {
+        super(context, resolveDialogTheme(context, themeResId));
+
+        final Context themeContext = getContext();
+        final LayoutInflater inflater = LayoutInflater.from(themeContext);
+        final View view = inflater.inflate(R.layout.date_picker_dialog, null);
+        setView(view);
+
+        setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
+        setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
+        setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
+
+        if (calendar != null) {
+            year = calendar.get(Calendar.YEAR);
+            monthOfYear = calendar.get(Calendar.MONTH);
+            dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
+        }
+
+        mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+        mDatePicker.init(year, monthOfYear, dayOfMonth, this);
+        mDatePicker.setValidationCallback(mValidationCallback);
+
         mDateSetListener = listener;
     }
 
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 0bb1097..85a0403 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,6 +16,10 @@
 
 package android.app;
 
+import com.android.internal.R;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.PhoneWindow;
+
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
@@ -43,7 +47,6 @@
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -57,12 +60,7 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.internal.R;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.PhoneWindow;
-
 import java.lang.ref.WeakReference;
-import java.util.List;
 
 /**
  * Base class for Dialogs.
@@ -94,11 +92,14 @@
         KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
     private static final String TAG = "Dialog";
     private Activity mOwnerActivity;
-    
+
+    private final WindowManager mWindowManager;
+
     final Context mContext;
-    final WindowManager mWindowManager;
-    Window mWindow;
+    final Window mWindow;
+
     View mDecor;
+
     private ActionBar mActionBar;
     /**
      * This field should be made private, so it is hidden from the SDK.
@@ -123,7 +124,7 @@
     private static final int CANCEL = 0x44;
     private static final int SHOW = 0x45;
 
-    private Handler mListenersHandler;
+    private final Handler mListenersHandler;
 
     private SearchEvent mSearchEvent;
 
@@ -131,11 +132,7 @@
 
     private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
 
-    private final Runnable mDismissAction = new Runnable() {
-        public void run() {
-            dismissDialog();
-        }
-    };
+    private final Runnable mDismissAction = this::dismissDialog;
 
     /**
      * Creates a dialog window that uses the default dialog theme.
@@ -198,14 +195,15 @@
      * @hide
      */
     @Deprecated
-    protected Dialog(@NonNull Context context, boolean cancelable, Message cancelCallback) {
+    protected Dialog(@NonNull Context context, boolean cancelable,
+            @Nullable Message cancelCallback) {
         this(context);
         mCancelable = cancelable;
         mCancelMessage = cancelCallback;
     }
 
     protected Dialog(@NonNull Context context, boolean cancelable,
-            OnCancelListener cancelListener) {
+            @Nullable OnCancelListener cancelListener) {
         this(context);
         mCancelable = cancelable;
         setOnCancelListener(cancelListener);
@@ -216,8 +214,7 @@
      * 
      * @return Context The Context used by the Dialog.
      */
-    @NonNull
-    public final Context getContext() {
+    public final @NonNull Context getContext() {
         return mContext;
     }
 
@@ -226,7 +223,7 @@
      *
      * @return The ActionBar attached to the dialog or null if no ActionBar is present.
      */
-    public ActionBar getActionBar() {
+    public @Nullable ActionBar getActionBar() {
         return mActionBar;
     }
 
@@ -236,7 +233,7 @@
      * 
      * @param activity The Activity that owns this dialog.
      */
-    public final void setOwnerActivity(Activity activity) {
+    public final void setOwnerActivity(@NonNull Activity activity) {
         mOwnerActivity = activity;
         
         getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream());
@@ -250,7 +247,7 @@
      * 
      * @return The Activity that owns this Dialog.
      */
-    public final Activity getOwnerActivity() {
+    public final @Nullable Activity getOwnerActivity() {
         return mOwnerActivity;
     }
     
@@ -316,13 +313,10 @@
             l = nl;
         }
 
-        try {
-            mWindowManager.addView(mDecor, l);
-            mShowing = true;
-    
-            sendShowMessage();
-        } finally {
-        }
+        mWindowManager.addView(mDecor, l);
+        mShowing = true;
+
+        sendShowMessage();
     }
     
     /**
@@ -388,7 +382,7 @@
         }
     }
 
-    // internal method to make sure mcreated is set properly without requiring
+    // internal method to make sure mCreated is set properly without requiring
     // users to call through to super in onCreate
     void dispatchOnCreate(Bundle savedInstanceState) {
         if (!mCreated) {
@@ -400,7 +394,7 @@
     /**
      * Similar to {@link Activity#onCreate}, you should initialize your dialog
      * in this method, including calling {@link #setContentView}.
-     * @param savedInstanceState If this dialog is being reinitalized after a
+     * @param savedInstanceState If this dialog is being reinitialized after a
      *     the hosting activity was previously shut down, holds the result from
      *     the most recent call to {@link #onSaveInstanceState}, or null if this
      *     is the first time.
@@ -433,7 +427,7 @@
      * state.
      * @return A bundle with the state of the dialog.
      */
-    public Bundle onSaveInstanceState() {
+    public @NonNull Bundle onSaveInstanceState() {
         Bundle bundle = new Bundle();
         bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
         if (mCreated) {
@@ -452,7 +446,7 @@
      * @param savedInstanceState The state of the dialog previously saved by
      *     {@link #onSaveInstanceState()}.
      */
-    public void onRestoreInstanceState(Bundle savedInstanceState) {
+    public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
         final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
         if (dialogHierarchyState == null) {
             // dialog has never been shown, or onCreated, nothing to restore.
@@ -473,7 +467,7 @@
      * @return Window The current window, or null if the activity is not
      *         visual.
      */
-    public Window getWindow() {
+    public @Nullable Window getWindow() {
         return mWindow;
     }
 
@@ -486,7 +480,7 @@
      * @see #getWindow
      * @see android.view.Window#getCurrentFocus
      */
-    public View getCurrentFocus() {
+    public @Nullable View getCurrentFocus() {
         return mWindow != null ? mWindow.getCurrentFocus() : null;
     }
 
@@ -498,8 +492,7 @@
      * @param id the identifier of the view to find
      * @return The view with the given id or null.
      */
-    @Nullable
-    public View findViewById(@IdRes int id) {
+    public @Nullable View findViewById(@IdRes int id) {
         return mWindow.findViewById(id);
     }
 
@@ -520,19 +513,19 @@
      * 
      * @param view The desired content to display.
      */
-    public void setContentView(View view) {
+    public void setContentView(@NonNull View view) {
         mWindow.setContentView(view);
     }
 
     /**
      * Set the screen content to an explicit view.  This view is placed
      * directly into the screen's view hierarchy.  It can itself be a complex
-     * view hierarhcy.
+     * view hierarchy.
      * 
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
      */
-    public void setContentView(View view, ViewGroup.LayoutParams params) {
+    public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
         mWindow.setContentView(view, params);
     }
 
@@ -543,7 +536,7 @@
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
      */
-    public void addContentView(View view, ViewGroup.LayoutParams params) {
+    public void addContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
         mWindow.addContentView(view, params);
     }
 
@@ -552,7 +545,7 @@
      * 
      * @param title The new text to display in the title.
      */
-    public void setTitle(CharSequence title) {
+    public void setTitle(@Nullable CharSequence title) {
         mWindow.setTitle(title);
         mWindow.getAttributes().setTitle(title);
     }
@@ -578,7 +571,8 @@
      * @see #onKeyUp
      * @see android.view.KeyEvent
      */
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
+    @Override
+    public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
             event.startTracking();
             return true;
@@ -592,7 +586,8 @@
      * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
      * the event).
      */
-    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+    @Override
+    public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
         return false;
     }
 
@@ -605,7 +600,8 @@
      * @see #onKeyDown
      * @see KeyEvent
      */
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
+    @Override
+    public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
                 && !event.isCanceled()) {
             onBackPressed();
@@ -619,7 +615,8 @@
      * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
      * the event).
      */
-    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+    @Override
+    public boolean onKeyMultiple(int keyCode, int repeatCount, @NonNull KeyEvent event) {
         return false;
     }
     
@@ -644,7 +641,7 @@
      * @param event Description of the key event.
      * @return True if the key shortcut was handled.
      */
-    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+    public boolean onKeyShortcut(int keyCode, @NonNull KeyEvent event) {
         return false;
     }
 
@@ -658,7 +655,7 @@
      *         The default implementation will cancel the dialog when a touch
      *         happens outside of the window bounds.
      */
-    public boolean onTouchEvent(MotionEvent event) {
+    public boolean onTouchEvent(@NonNull MotionEvent event) {
         if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) {
             cancel();
             return true;
@@ -681,7 +678,7 @@
      * @return Return true if you have consumed the event, false if you haven't.
      * The default implementation always returns false.
      */
-    public boolean onTrackballEvent(MotionEvent event) {
+    public boolean onTrackballEvent(@NonNull MotionEvent event) {
         return false;
     }
 
@@ -710,25 +707,30 @@
      * @return Return true if you have consumed the event, false if you haven't.
      * The default implementation always returns false.
      */
-    public boolean onGenericMotionEvent(MotionEvent event) {
+    public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
         return false;
     }
 
+    @Override
     public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
         if (mDecor != null) {
             mWindowManager.updateViewLayout(mDecor, params);
         }
     }
 
+    @Override
     public void onContentChanged() {
     }
 
+    @Override
     public void onWindowFocusChanged(boolean hasFocus) {
     }
 
+    @Override
     public void onAttachedToWindow() {
     }
 
+    @Override
     public void onDetachedFromWindow() {
     }
 
@@ -747,7 +749,8 @@
      *
      * @return boolean Return true if this event was consumed.
      */
-    public boolean dispatchKeyEvent(KeyEvent event) {
+    @Override
+    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
         if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) {
             return true;
         }
@@ -767,7 +770,8 @@
      * @param event The key shortcut event.
      * @return True if this event was consumed.
      */
-    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+    @Override
+    public boolean dispatchKeyShortcutEvent(@NonNull KeyEvent event) {
         if (mWindow.superDispatchKeyShortcutEvent(event)) {
             return true;
         }
@@ -784,7 +788,8 @@
      * 
      * @return boolean Return true if this event was consumed.
      */
-    public boolean dispatchTouchEvent(MotionEvent ev) {
+    @Override
+    public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
         if (mWindow.superDispatchTouchEvent(ev)) {
             return true;
         }
@@ -801,7 +806,8 @@
      * 
      * @return boolean Return true if this event was consumed.
      */
-    public boolean dispatchTrackballEvent(MotionEvent ev) {
+    @Override
+    public boolean dispatchTrackballEvent(@NonNull MotionEvent ev) {
         if (mWindow.superDispatchTrackballEvent(ev)) {
             return true;
         }
@@ -818,14 +824,16 @@
      *
      * @return boolean Return true if this event was consumed.
      */
-    public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+    @Override
+    public boolean dispatchGenericMotionEvent(@NonNull MotionEvent ev) {
         if (mWindow.superDispatchGenericMotionEvent(ev)) {
             return true;
         }
         return onGenericMotionEvent(ev);
     }
 
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(@NonNull AccessibilityEvent event) {
         event.setClassName(getClass().getName());
         event.setPackageName(mContext.getPackageName());
 
@@ -840,6 +848,7 @@
     /**
      * @see Activity#onCreatePanelView(int)
      */
+    @Override
     public View onCreatePanelView(int featureId) {
         return null;
     }
@@ -847,7 +856,8 @@
     /**
      * @see Activity#onCreatePanelMenu(int, Menu)
      */
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
+    @Override
+    public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
             return onCreateOptionsMenu(menu);
         }
@@ -858,10 +868,10 @@
     /**
      * @see Activity#onPreparePanel(int, View, Menu)
      */
+    @Override
     public boolean onPreparePanel(int featureId, View view, Menu menu) {
         if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
-            boolean goforit = onPrepareOptionsMenu(menu);
-            return goforit && menu.hasVisibleItems();
+            return onPrepareOptionsMenu(menu) && menu.hasVisibleItems();
         }
         return true;
     }
@@ -869,6 +879,7 @@
     /**
      * @see Activity#onMenuOpened(int, Menu)
      */
+    @Override
     public boolean onMenuOpened(int featureId, Menu menu) {
         if (featureId == Window.FEATURE_ACTION_BAR) {
             mActionBar.dispatchMenuVisibilityChanged(true);
@@ -879,6 +890,7 @@
     /**
      * @see Activity#onMenuItemSelected(int, MenuItem)
      */
+    @Override
     public boolean onMenuItemSelected(int featureId, MenuItem item) {
         return false;
     }
@@ -886,6 +898,7 @@
     /**
      * @see Activity#onPanelClosed(int, Menu)
      */
+    @Override
     public void onPanelClosed(int featureId, Menu menu) {
         if (featureId == Window.FEATURE_ACTION_BAR) {
             mActionBar.dispatchMenuVisibilityChanged(false);
@@ -900,7 +913,7 @@
      * @see Activity#onCreateOptionsMenu(Menu)
      * @see #getOwnerActivity()
      */
-    public boolean onCreateOptionsMenu(Menu menu) {
+    public boolean onCreateOptionsMenu(@NonNull Menu menu) {
         return true;
     }
 
@@ -912,21 +925,21 @@
      * @see Activity#onPrepareOptionsMenu(Menu)
      * @see #getOwnerActivity()
      */
-    public boolean onPrepareOptionsMenu(Menu menu) {
+    public boolean onPrepareOptionsMenu(@NonNull Menu menu) {
         return true;
     }
 
     /**
      * @see Activity#onOptionsItemSelected(MenuItem)
      */
-    public boolean onOptionsItemSelected(MenuItem item) {
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
         return false;
     }
 
     /**
      * @see Activity#onOptionsMenuClosed(Menu)
      */
-    public void onOptionsMenuClosed(Menu menu) {
+    public void onOptionsMenuClosed(@NonNull Menu menu) {
     }
 
     /**
@@ -959,47 +972,49 @@
     /**
      * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
      */
+    @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
     }
 
     /**
      * @see Activity#registerForContextMenu(View)
      */
-    public void registerForContextMenu(View view) {
+    public void registerForContextMenu(@NonNull View view) {
         view.setOnCreateContextMenuListener(this);
     }
     
     /**
      * @see Activity#unregisterForContextMenu(View)
      */
-    public void unregisterForContextMenu(View view) {
+    public void unregisterForContextMenu(@NonNull View view) {
         view.setOnCreateContextMenuListener(null);
     }
     
     /**
      * @see Activity#openContextMenu(View)
      */
-    public void openContextMenu(View view) {
+    public void openContextMenu(@NonNull View view) {
         view.showContextMenu();
     }
 
     /**
      * @see Activity#onContextItemSelected(MenuItem)
      */
-    public boolean onContextItemSelected(MenuItem item) {
+    public boolean onContextItemSelected(@NonNull MenuItem item) {
         return false;
     }
 
     /**
      * @see Activity#onContextMenuClosed(Menu)
      */
-    public void onContextMenuClosed(Menu menu) {
+    public void onContextMenuClosed(@NonNull Menu menu) {
     }
 
     /**
      * This hook is called when the user signals the desire to start a search.
      */
-    public boolean onSearchRequested(SearchEvent searchEvent) {
+    @Override
+    public boolean onSearchRequested(@NonNull SearchEvent searchEvent) {
         mSearchEvent = searchEvent;
         return onSearchRequested();
     }
@@ -1007,6 +1022,7 @@
     /**
      * This hook is called when the user signals the desire to start a search.
      */
+    @Override
     public boolean onSearchRequested() {
         final SearchManager searchManager = (SearchManager) mContext
                 .getSystemService(Context.SEARCH_SERVICE);
@@ -1029,13 +1045,10 @@
      * @return SearchEvent The SearchEvent that triggered the {@link
      *                    #onSearchRequested} callback.
      */
-    public final SearchEvent getSearchEvent() {
+    public final @Nullable SearchEvent getSearchEvent() {
         return mSearchEvent;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
         if (mActionBar != null && mActionModeTypeStarting == ActionMode.TYPE_PRIMARY) {
@@ -1044,9 +1057,6 @@
         return null;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
         try {
@@ -1063,6 +1073,7 @@
      * Note that if you override this method you should always call through
      * to the superclass implementation by calling super.onActionModeStarted(mode).
      */
+    @Override
     @CallSuper
     public void onActionModeStarted(ActionMode mode) {
         mActionMode = mode;
@@ -1074,6 +1085,7 @@
      * Note that if you override this method you should always call through
      * to the superclass implementation by calling super.onActionModeFinished(mode).
      */
+    @Override
     @CallSuper
     public void onActionModeFinished(ActionMode mode) {
         if (mode == mActionMode) {
@@ -1139,7 +1151,7 @@
      * Convenience for calling
      * {@link android.view.Window#setFeatureDrawableUri}.
      */
-    public final void setFeatureDrawableUri(int featureId, Uri uri) {
+    public final void setFeatureDrawableUri(int featureId, @Nullable Uri uri) {
         getWindow().setFeatureDrawableUri(featureId, uri);
     }
 
@@ -1147,7 +1159,7 @@
      * Convenience for calling
      * {@link android.view.Window#setFeatureDrawable(int, Drawable)}.
      */
-    public final void setFeatureDrawable(int featureId, Drawable drawable) {
+    public final void setFeatureDrawable(int featureId, @Nullable Drawable drawable) {
         getWindow().setFeatureDrawable(featureId, drawable);
     }
 
@@ -1159,7 +1171,7 @@
         getWindow().setFeatureDrawableAlpha(featureId, alpha);
     }
 
-    public LayoutInflater getLayoutInflater() {
+    public @NonNull LayoutInflater getLayoutInflater() {
         return getWindow().getLayoutInflater();
     }
 
@@ -1191,6 +1203,7 @@
      * Cancel the dialog.  This is essentially the same as calling {@link #dismiss()}, but it will
      * also call your {@link DialogInterface.OnCancelListener} (if registered).
      */
+    @Override
     public void cancel() {
         if (!mCanceled && mCancelMessage != null) {
             mCanceled = true;
@@ -1211,7 +1224,7 @@
      * 
      * @param listener The {@link DialogInterface.OnCancelListener} to use.
      */
-    public void setOnCancelListener(final OnCancelListener listener) {
+    public void setOnCancelListener(@Nullable OnCancelListener listener) {
         if (mCancelAndDismissTaken != null) {
             throw new IllegalStateException(
                     "OnCancelListener is already taken by "
@@ -1229,7 +1242,7 @@
      * @param msg The msg to send when the dialog is canceled.
      * @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener)
      */
-    public void setCancelMessage(final Message msg) {
+    public void setCancelMessage(@Nullable Message msg) {
         mCancelMessage = msg;
     }
 
@@ -1237,7 +1250,7 @@
      * Set a listener to be invoked when the dialog is dismissed.
      * @param listener The {@link DialogInterface.OnDismissListener} to use.
      */
-    public void setOnDismissListener(final OnDismissListener listener) {
+    public void setOnDismissListener(@Nullable OnDismissListener listener) {
         if (mCancelAndDismissTaken != null) {
             throw new IllegalStateException(
                     "OnDismissListener is already taken by "
@@ -1254,7 +1267,7 @@
      * Sets a listener to be invoked when the dialog is shown.
      * @param listener The {@link DialogInterface.OnShowListener} to use.
      */
-    public void setOnShowListener(OnShowListener listener) {
+    public void setOnShowListener(@Nullable OnShowListener listener) {
         if (listener != null) {
             mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
         } else {
@@ -1266,13 +1279,13 @@
      * Set a message to be sent when the dialog is dismissed.
      * @param msg The msg to send when the dialog is dismissed.
      */
-    public void setDismissMessage(final Message msg) {
+    public void setDismissMessage(@Nullable Message msg) {
         mDismissMessage = msg;
     }
 
     /** @hide */
-    public boolean takeCancelAndDismissListeners(String msg, final OnCancelListener cancel,
-            final OnDismissListener dismiss) {
+    public boolean takeCancelAndDismissListeners(@Nullable String msg,
+            @Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss) {
         if (mCancelAndDismissTaken != null) {
             mCancelAndDismissTaken = null;
         } else if (mCancelMessage != null || mDismissMessage != null) {
@@ -1306,15 +1319,15 @@
     /**
      * Sets the callback that will be called if a key is dispatched to the dialog.
      */
-    public void setOnKeyListener(final OnKeyListener onKeyListener) {
+    public void setOnKeyListener(@Nullable OnKeyListener onKeyListener) {
         mOnKeyListener = onKeyListener;
     }
 
     private static final class ListenersHandler extends Handler {
-        private WeakReference<DialogInterface> mDialog;
+        private final WeakReference<DialogInterface> mDialog;
 
         public ListenersHandler(Dialog dialog) {
-            mDialog = new WeakReference<DialogInterface>(dialog);
+            mDialog = new WeakReference<>(dialog);
         }
 
         @Override
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index bb3c719..c745644 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1033,11 +1033,11 @@
      *                        false if it is not.
      */
     public void setUserVisibleHint(boolean isVisibleToUser) {
-        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && mFragmentManager != null) {
+        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && isAdded()) {
             mFragmentManager.performPendingDeferredStart(this);
         }
         mUserVisibleHint = isVisibleToUser;
-        mDeferStart = !isVisibleToUser;
+        mDeferStart = mState < STARTED && !isVisibleToUser;
     }
 
     /**
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 3f22385..4c4f128 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -440,6 +440,11 @@
                 compatInfo);
         classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
 
+        if (DEBUG) {
+            Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+                    + " with key=" + key);
+        }
+
         synchronized (this) {
             final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
                     activityToken);
@@ -651,6 +656,16 @@
                 activityResources.overrideConfig.setToDefaults();
             }
 
+            if (DEBUG) {
+                Throwable here = new Throwable();
+                here.fillInStackTrace();
+                Slog.d(TAG, "updating resources override for activity=" + activityToken
+                        + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
+                        + " to newConfig="
+                        + Configuration.resourceQualifierString(activityResources.overrideConfig),
+                        here);
+            }
+
             final boolean activityHasOverrideConfig =
                     !activityResources.overrideConfig.equals(Configuration.EMPTY);
 
@@ -692,9 +707,15 @@
                         oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
                         rebasedOverrideConfig, oldKey.mCompatInfo);
 
+                if (DEBUG) {
+                    Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+                            + " to newKey=" + newKey);
+                }
+
                 ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
                 if (resourcesImpl == null) {
                     resourcesImpl = createResourcesImpl(newKey);
+                    mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
                 }
 
                 if (resourcesImpl != resources.getImpl()) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 656c7ff..45aa6b4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5654,10 +5654,9 @@
     }
 
     /**
-     * @hide
      * Return if this user is a managed profile of another user. An admin can become the profile
      * owner of a managed profile with {@link #ACTION_PROVISION_MANAGED_PROFILE} and of a managed
-     * user with {@link #ACTION_PROVISION_MANAGED_USER}.
+     * user with {@link #createAndManageUser}
      * @param admin Which profile owner this request is associated with.
      * @return if this user is a managed profile of another user.
      */
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 7d698b3..01f82e6 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -284,6 +284,8 @@
         out.writeInt(mInstance);
         out.writeInt(mProperties);
         out.writeInt(mPermissions);
+        out.writeInt(mKeySize);
+        out.writeInt(mWriteType);
         out.writeTypedList(mDescriptors);
     }
 
@@ -303,6 +305,8 @@
         mInstance = in.readInt();
         mProperties = in.readInt();
         mPermissions = in.readInt();
+        mKeySize = in.readInt();
+        mWriteType = in.readInt();
 
         mDescriptors = new ArrayList<BluetoothGattDescriptor>();
 
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e355a1c..35437a1 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -144,7 +144,7 @@
     }
 
     /**
-     * 
+     *
      * Get a list of devices that match any of the given connection
      * states.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e89cbd7..39bc783 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -130,6 +130,7 @@
             MATCH_DISABLED_COMPONENTS,
             MATCH_DISABLED_UNTIL_USED_COMPONENTS,
             MATCH_SYSTEM_ONLY,
+            MATCH_FACTORY_ONLY,
             MATCH_DEBUG_TRIAGED_MISSING,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -415,6 +416,13 @@
     public static final int MATCH_SYSTEM_ONLY = 0x00100000;
 
     /**
+     * Internal {@link PackageInfo} flag: include only components on the system image.
+     * This will not return information on any unbundled update to system components.
+     * @hide
+     */
+    public static final int MATCH_FACTORY_ONLY = 0x00200000;
+
+    /**
      * Internal flag used to indicate that a system component has done their
      * homework and verified that they correctly handle packages and components
      * that come and go over time. In particular:
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 984a960..65e0b92 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -113,6 +113,13 @@
     public static final int PROTECTION_FLAG_PREINSTALLED = 0x400;
 
     /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>setup</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     */
+    public static final int PROTECTION_FLAG_SETUP = 0x800;
+
+    /**
      * Mask for {@link #protectionLevel}: the basic protection type.
      */
     public static final int PROTECTION_MASK_BASE = 0xf;
@@ -226,6 +233,9 @@
         if ((level&PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
             protLevel += "|preinstalled";
         }
+        if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
+            protLevel += "|setup";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 63ee8d2..cc82eb6 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -186,7 +186,7 @@
      * @see #TYPE_LINEAR_ACCELERATION
      */
     public static final String STRING_TYPE_LINEAR_ACCELERATION =
-        "android.sensor.linear_acceleration";
+            "android.sensor.linear_acceleration";
 
     /**
      * A constant describing a rotation vector sensor type.
@@ -229,7 +229,7 @@
      * @see #TYPE_AMBIENT_TEMPERATURE
      */
     public static final String STRING_TYPE_AMBIENT_TEMPERATURE =
-        "android.sensor.ambient_temperature";
+            "android.sensor.ambient_temperature";
 
     /**
      * A constant describing an uncalibrated magnetic field sensor type.
@@ -254,7 +254,7 @@
      * @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED
      */
     public static final String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED =
-        "android.sensor.magnetic_field_uncalibrated";
+            "android.sensor.magnetic_field_uncalibrated";
 
     /**
      * A constant describing an uncalibrated rotation vector sensor type.
@@ -280,7 +280,7 @@
      * @see #TYPE_GAME_ROTATION_VECTOR
      */
     public static final String STRING_TYPE_GAME_ROTATION_VECTOR =
-        "android.sensor.game_rotation_vector";
+            "android.sensor.game_rotation_vector";
 
     /**
      * A constant describing an uncalibrated gyroscope sensor type.
@@ -302,7 +302,7 @@
      * @see #TYPE_GYROSCOPE_UNCALIBRATED
      */
     public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED =
-        "android.sensor.gyroscope_uncalibrated";
+            "android.sensor.gyroscope_uncalibrated";
 
     /**
      * A constant describing a significant motion trigger sensor.
@@ -324,7 +324,7 @@
      * @see #TYPE_SIGNIFICANT_MOTION
      */
     public static final String STRING_TYPE_SIGNIFICANT_MOTION =
-        "android.sensor.significant_motion";
+            "android.sensor.significant_motion";
 
     /**
      * A constant describing a step detector sensor.
@@ -391,7 +391,7 @@
      * @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR
      */
     public static final String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR =
-        "android.sensor.geomagnetic_rotation_vector";
+            "android.sensor.geomagnetic_rotation_vector";
 
     /**
      * A constant describing a heart rate monitor.
@@ -431,7 +431,7 @@
      * A constant string describing a wake up tilt detector sensor type.
      *
      * @hide
-     * @see #TYPE_WAKE_UP_TILT_DETECTOR
+     * @see #TYPE_TILT_DETECTOR
      */
     public static final String SENSOR_STRING_TYPE_TILT_DETECTOR =
             "android.sensor.tilt_detector";
@@ -495,7 +495,7 @@
      */
     public static final String STRING_TYPE_GLANCE_GESTURE = "android.sensor.glance_gesture";
 
-     /**
+    /**
      * A constant describing a pick up sensor.
      *
      * A sensor of this type triggers when the device is picked up regardless of wherever it was
@@ -514,7 +514,7 @@
      */
     public static final String STRING_TYPE_PICK_UP_GESTURE = "android.sensor.pick_up_gesture";
 
-     /**
+    /**
      * A constant describing a wrist tilt gesture sensor.
      *
      * A sensor of this type triggers when the device face is tilted towards the user.
@@ -553,7 +553,7 @@
      */
     public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation";
 
-     /**
+    /**
      * A constant describing a pose sensor with 6 degrees of freedom.
      *
      * Similar to {@link #TYPE_ROTATION_VECTOR}, with additional delta
@@ -578,7 +578,7 @@
      */
     public static final String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
 
-     /**
+    /**
      * A constant describing a stationary detect sensor.
      *
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
@@ -593,7 +593,7 @@
      */
     public static final String STRING_TYPE_STATIONARY_DETECT = "android.sensor.stationary_detect";
 
-     /**
+    /**
      * A constant describing a motion detect sensor.
      *
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
@@ -608,7 +608,7 @@
      */
     public static final String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
 
-     /**
+    /**
      * A constant describing a motion detect sensor.
      *
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
@@ -706,6 +706,14 @@
     private static final int DATA_INJECTION_MASK = 0x10;
     private static final int DATA_INJECTION_SHIFT = 4;
 
+    // MASK for dynamic sensor (sensor that added during runtime), bit 6.
+    private static final int DYNAMIC_SENSOR_MASK = 0x20;
+    private static final int DYNAMIC_SENSOR_SHIFT = 5;
+
+    // MASK for indication bit of sensor additional information support (bit 7).
+    private static final int ADDITIONAL_INFO_MASK = 0x40;
+    private static final int ADDITIONAL_INFO_SHIFT = 6;
+
     // TODO(): The following arrays are fragile and error-prone. This needs to be refactored.
 
     // Note: This needs to be updated, whenever a new sensor is added.
@@ -887,13 +895,37 @@
     }
 
     /**
-     * @return The type of this sensor as a string.
+     * @return The UUID of the sensor. If the sensor does not support UUID, the returned value will
+     * be an all zero UUID; if the sensor's combination of type and name is guaranteed to be unique
+     * in system, the return value will be an all "F" UUID.
+     *
+     * @hide
      */
+    @SystemApi
     public UUID getUuid() {
         return mUuid;
     }
 
     /**
+     * @return The unique id of sensor. Return value of 0 means this sensor does not support UUID;
+     * return value of -1 means this sensor can be uniquely identified in system by combination of
+     * its type and name.
+     */
+    public int getId() {
+        if (mUuid == ALL_0_UUID) {
+            return 0;
+        } else if (mUuid == ALL_F_UUID) {
+            return -1;
+        } else {
+            int id = Math.abs(mUuid.hashCode()) + 1;
+            if (id <= 0) { // catch corner case when hash is Integer.MIN_VALUE and Integer.MAX_VALUE
+                id = 1;
+            }
+            return id;
+        }
+    }
+
+    /**
      * @hide
      * @return The permission required to access this sensor. If empty, no permission is required.
      */
@@ -961,6 +993,26 @@
     }
 
     /**
+     * Returns true if the sensor is a dynamic sensor.
+     *
+     * @return <code>true</code> if the sensor is a dynamic sensor (sensor added at runtime).
+     * @see SensorManager.DynamicSensorCallback
+     */
+    public boolean isDynamicSensor() {
+        return (mFlags & DYNAMIC_SENSOR_MASK) != 0;
+    }
+
+    /**
+     * Returns true if the sensor supports sensor additional information API
+     *
+     * @return <code>true</code> if the sensor supports sensor additional information API
+     * @see SensorAdditionalInfo
+     */
+    public boolean isAdditionalInfoSupported() {
+        return (mFlags & ADDITIONAL_INFO_MASK) != 0;
+    }
+
+    /**
      * Returns true if the sensor supports data injection when the
      * HAL is set to data injection mode.
      *
@@ -986,6 +1038,10 @@
                 + ", power=" + mPower + ", minDelay=" + mMinDelay + "}";
     }
 
+    //special UUID hash constant
+    private final static UUID ALL_0_UUID = new UUID(0,0);
+    private final static UUID ALL_F_UUID = new UUID(~0L, ~0L);
+
     /**
      * Sets the Type associated with the sensor.
      * NOTE: to be used only by native bindings in SensorManager.
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 8e5b8a3..572a287 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -27,7 +27,7 @@
  * android.hardware.SensorEventCallback#onSensorAdditionalInfo onSensorAdditionalInfo}.
  *
  * @see SensorManager
- * @see SensorEventListener3
+ * @see SensorEventCallback
  * @see Sensor
  *
  */
@@ -106,7 +106,7 @@
      * such as accelerometer, gyro, etc.
      *
      * Payload:
-     *     floatValues[0..11]: First 3 rows of a homogenous matrix in row major order that captures
+     *     floatValues[0..11]: First 3 rows of a homogeneous matrix in row major order that captures
      *     any linear transformation, including rotation, scaling, shear, shift.
      */
     public static final int TYPE_VEC3_CALIBRATION = 0x10002;
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 35c96f7..0d96b8e 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -18,7 +18,7 @@
 
 /**
  * This class represents a {@link android.hardware.Sensor Sensor} event and
- * holds informations such as the sensor's type, the time-stamp, accuracy and of
+ * holds information such as the sensor's type, the time-stamp, accuracy and of
  * course the sensor's {@link SensorEvent#values data}.
  *
  * <p>
@@ -163,7 +163,7 @@
      * </ul>
      * <p>
      * Typically the output of the gyroscope is integrated over time to
-     * calculate a rotation describing the change of angles over the timestep,
+     * calculate a rotation describing the change of angles over the time step,
      * for example:
      * </p>
      *
@@ -173,7 +173,7 @@
      *     private float timestamp;
      *
      *     public void onSensorChanged(SensorEvent event) {
-     *          // This timestep's delta rotation to be multiplied by the current rotation
+     *          // This time step's delta rotation to be multiplied by the current rotation
      *          // after computing it from the gyro sample data.
      *          if (timestamp != 0) {
      *              final float dT = (event.timestamp - timestamp) * NS2S;
@@ -192,8 +192,8 @@
      *                  axisZ /= omegaMagnitude;
      *              }
      *
-     *              // Integrate around this axis with the angular speed by the timestep
-     *              // in order to get a delta rotation from this sample over the timestep
+     *              // Integrate around this axis with the angular speed by the time step
+     *              // in order to get a delta rotation from this sample over the time step
      *              // We will convert this axis-angle representation of the delta rotation
      *              // into a quaternion before turning it into the rotation matrix.
      *              float thetaOverTwo = omegaMagnitude * dT / 2.0f;
@@ -433,9 +433,9 @@
      * Each field is a component of the estimated hard iron calibration.
      * The values are in micro-Tesla (uT).
      * </p>
-     * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanenet
+     * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanent
      * magnets on the device.
-     * Soft iron - These distortions arise due to the interaction with the earth's magentic
+     * Soft iron - These distortions arise due to the interaction with the earth's magnetic
      * field.
      * </p>
      * <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR}:</h4>
@@ -508,14 +508,14 @@
      *
      *
      * <ul>
-     *  <li> values[0]: x*sin(&#952/2) </li>
-     *  <li> values[1]: y*sin(&#952/2) </li>
-     *  <li> values[2]: z*sin(&#952/2) </li>
-     *  <li> values[3]: cos(&#952/2)   </li>
+     * <li> values[0]: x*sin(&#952/2) </li>
+     * <li> values[1]: y*sin(&#952/2) </li>
+     * <li> values[2]: z*sin(&#952/2) </li>
+     * <li> values[3]: cos(&#952/2)   </li>
      *
      *
      * <li> values[4]: Translation along x axis from an arbitrary origin. </li>
-     * li> values[5]: Translation along y axis from an arbitrary origin. </li>
+     * <li> values[5]: Translation along y axis from an arbitrary origin. </li>
      * <li> values[6]: Translation along z axis from an arbitrary origin. </li>
      *
      * <li> values[7]:  Delta quaternion rotation x*sin(&#952/2) </li>
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 5684aa5..a20307a 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -884,7 +884,7 @@
      * Used for receiving notifications from the SensorManager when dynamic sensors are connected or
      * disconnected.
      */
-    public static abstract class DynamicSensorConnectionCallback {
+    public static abstract class DynamicSensorCallback {
         /**
          * Called when there is a dynamic sensor being connected to the system.
          *
@@ -902,62 +902,73 @@
 
 
     /**
-     * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
-     * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+     * Add a {@link android.hardware.SensorManager.DynamicSensorCallback
+     * DynamicSensorCallback} to receive dynamic sensor connection callbacks. Repeat
      * registration with the already registered callback object will have no additional effect.
      *
      * @param callback An object that implements the
-     *        {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
-     *        DynamicSensorConnectionCallback}
+     *        {@link android.hardware.SensorManager.DynamicSensorCallback
+     *        DynamicSensorCallback}
      *        interface for receiving callbacks.
-     * @see #addDynamicSensorCallback(DynamicSensorConnectionCallback, Handler)
+     * @see #addDynamicSensorCallback(DynamicSensorCallback, Handler)
      *
      * @throws IllegalArgumentException when callback is null.
      */
-    public void registerDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+    public void registerDynamicSensorCallback(DynamicSensorCallback callback) {
         registerDynamicSensorCallback(callback, null);
     }
 
     /**
-     * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
-     * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+     * Add a {@link android.hardware.SensorManager.DynamicSensorCallback
+     * DynamicSensorCallback} to receive dynamic sensor connection callbacks. Repeat
      * registration with the already registered callback object will have no additional effect.
      *
      * @param callback An object that implements the
-     *        {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
-     *        DynamicSensorConnectionCallback} interface for receiving callbacks.
+     *        {@link android.hardware.SensorManager.DynamicSensorCallback
+     *        DynamicSensorCallback} interface for receiving callbacks.
      * @param handler The {@link android.os.Handler Handler} the {@link
-     *        android.hardware.SensorManager.DynamicSensorConnectionCallback
+     *        android.hardware.SensorManager.DynamicSensorCallback
      *        sensor connection events} will be delivered to.
      *
      * @throws IllegalArgumentException when callback is null.
      */
     public void registerDynamicSensorCallback(
-            DynamicSensorConnectionCallback callback, Handler handler) {
+            DynamicSensorCallback callback, Handler handler) {
         registerDynamicSensorCallbackImpl(callback, handler);
     }
 
     /**
-     * Remove a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
-     * DynamicSensorConnectionCallback} to stop sending dynamic sensor connection events to that
+     * Remove a {@link android.hardware.SensorManager.DynamicSensorCallback
+     * DynamicSensorCallback} to stop sending dynamic sensor connection events to that
      * callback.
      *
      * @param callback An object that implements the
-     *        {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
-     *        DynamicSensorConnectionCallback}
+     *        {@link android.hardware.SensorManager.DynamicSensorCallback
+     *        DynamicSensorCallback}
      *        interface for receiving callbacks.
      */
-    public void unregisterDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+    public void unregisterDynamicSensorCallback(DynamicSensorCallback callback) {
         unregisterDynamicSensorCallbackImpl(callback);
     }
 
+    /**
+     * Tell if dynamic sensor discovery feature is supported by system.
+     *
+     * @return <code>true</code> if dynamic sensor discovery is supported, <code>false</code>
+     * otherwise.
+     */
+    public boolean isDynamicSensorDiscoverySupported() {
+        List<Sensor> sensors = getSensorList(Sensor.TYPE_DYNAMIC_SENSOR_META);
+        return sensors.size() > 0;
+    }
+
     /** @hide */
     protected abstract void registerDynamicSensorCallbackImpl(
-            DynamicSensorConnectionCallback callback, Handler handler);
+            DynamicSensorCallback callback, Handler handler);
 
     /** @hide */
     protected abstract void unregisterDynamicSensorCallbackImpl(
-            DynamicSensorConnectionCallback callback);
+            DynamicSensorCallback callback);
 
     /**
      * <p>
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 7d903bd..259ca03 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -1,4 +1,4 @@
-    /*
+/*
  * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -76,7 +76,7 @@
             new HashMap<TriggerEventListener, TriggerEventQueue>();
 
     // Dynamic Sensor callbacks
-    private HashMap<DynamicSensorConnectionCallback, Handler>
+    private HashMap<DynamicSensorCallback, Handler>
             mDynamicSensorCallbacks = new HashMap<>();
     private BroadcastReceiver mDynamicSensorBroadcastReceiver;
 
@@ -120,7 +120,7 @@
     @Override
     protected List<Sensor> getFullDynamicSensorList() {
         // only set up broadcast receiver if the application tries to find dynamic sensors or
-        // explicitly register a DynamicSensorConnectionCallback
+        // explicitly register a DynamicSensorCallback
         setupDynamicSensorBroadcastReceiver();
         updateDynamicSensorList();
         return mFullDynamicSensorsList;
@@ -354,9 +354,9 @@
 
                     Handler mainHandler = new Handler(mContext.getMainLooper());
 
-                    for (Map.Entry<DynamicSensorConnectionCallback, Handler> entry :
+                    for (Map.Entry<DynamicSensorCallback, Handler> entry :
                             mDynamicSensorCallbacks.entrySet()) {
-                        final DynamicSensorConnectionCallback callback = entry.getKey();
+                        final DynamicSensorCallback callback = entry.getKey();
                         Handler handler =
                                 entry.getValue() == null ? mainHandler : entry.getValue();
 
@@ -413,7 +413,7 @@
 
     /** @hide */
     protected void registerDynamicSensorCallbackImpl(
-            DynamicSensorConnectionCallback callback, Handler handler) {
+            DynamicSensorCallback callback, Handler handler) {
         if (DEBUG_DYNAMIC_SENSOR) {
             Log.i(TAG, "DYNS Register dynamic sensor callback");
         }
@@ -432,7 +432,7 @@
 
     /** @hide */
     protected void unregisterDynamicSensorCallbackImpl(
-            DynamicSensorConnectionCallback callback) {
+            DynamicSensorCallback callback) {
         if (DEBUG_DYNAMIC_SENSOR) {
             Log.i(TAG, "Removing dynamic sensor listerner");
         }
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 644e29f..ae44f1d 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -37,6 +37,7 @@
     private float mStoppedPowerDrawMw;
     private float mSleepPowerDrawMw;
     private float mPeakPowerDrawMw;
+    private int mMaxPacketLengthBytes;
 
     private int[] mSupportedSensors;
 
@@ -46,6 +47,27 @@
     }
 
     /**
+     * returns the maximum number of bytes that can be sent per message to the hub
+     *
+     * @return int - maximum bytes that can be transmitted in a
+     *         single packet
+     */
+    public int getMaxPacketLengthBytes() {
+        return mMaxPacketLengthBytes;
+    }
+
+    /**
+     * set the context hub unique identifer
+     *
+     * @param bytes - Maximum number of bytes per message
+     *
+     * @hide
+     */
+    public void setMaxPacketLenBytes(int bytes) {
+        mMaxPacketLengthBytes = bytes;
+    }
+
+    /**
      * get the context hub unique identifer
      *
      * @return int - unique system wide identifier
@@ -374,4 +396,4 @@
             return new ContextHubInfo[size];
         }
     };
-}
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 4ddf767..89edaa9 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -43,23 +43,6 @@
     private Handler mCallbackHandler;
 
     /**
-     * A special context hub identifier meaning any possible hub on the system.
-     */
-    public static final int ANY_HUB       = -1;
-    /**
-     * A constant denoting a message to load a a Nano App
-     */
-    public static final int MSG_LOAD_NANO_APP   = 1;
-    /**
-     * A constant denoting a message to unload a a Nano App
-     */
-    public static final int MSG_UNLOAD_NANO_APP = 2;
-    /**
-     * A constant denoting a message to send a message
-     */
-    public static final int MSG_DATA_SEND       = 3;
-
-    /**
      * An interface to receive asynchronous communication from the context hub.
      */
     public abstract static class Callback {
@@ -69,7 +52,7 @@
          * Callback function called on message receipt from context hub.
          *
          * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
-         * @param nanoAppHandle Handle (unique identifier) for the app that sent the message.
+         * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
          * @param message The context hub message.
          *
          * @see ContextHubMessage
@@ -89,7 +72,7 @@
         try {
             retVal = getBinder().getContextHubHandles();
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not fetch context hub handles : " + e);
+            Log.w(TAG, "Could not fetch context hub handles : " + e);
         }
         return retVal;
     }
@@ -107,7 +90,7 @@
         try {
             retVal = getBinder().getContextHubInfo(hubHandle);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not fetch context hub info :" + e);
+            Log.w(TAG, "Could not fetch context hub info :" + e);
         }
 
         return retVal;
@@ -126,6 +109,7 @@
      */
     public int loadNanoApp(int hubHandle, NanoApp app) {
         int retVal = -1;
+
         if (app == null) {
             return retVal;
         }
@@ -133,7 +117,7 @@
         try {
             retVal = getBinder().loadNanoApp(hubHandle, app);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not fetch load nanoApp :" + e);
+            Log.w(TAG, "Could not load nanoApp :" + e);
         }
 
         return retVal;
@@ -152,7 +136,7 @@
         try {
             retVal = getBinder().unloadNanoApp(nanoAppHandle);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not fetch unload nanoApp :" + e);
+            Log.w(TAG, "Could not fetch unload nanoApp :" + e);
         }
 
         return retVal;
@@ -172,7 +156,7 @@
         try {
             retVal = getBinder().getNanoAppInstanceInfo(nanoAppHandle);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not fetch nanoApp info :" + e);
+            Log.w(TAG, "Could not fetch nanoApp info :" + e);
         }
 
         return retVal;
@@ -193,7 +177,7 @@
         try {
             retVal = getBinder().findNanoAppOnHub(hubHandle, filter);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not query nanoApp instance :" + e);
+            Log.w(TAG, "Could not query nanoApp instance :" + e);
         }
         return retVal;
     }
@@ -212,10 +196,14 @@
     public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
         int retVal = -1;
 
+        if (message == null || message.getData() == null) {
+            Log.w(TAG, "null ptr");
+            return retVal;
+        }
         try {
             retVal = getBinder().sendMessage(hubHandle, nanoAppHandle, message);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not fetch send message :" + e.toString());
+            Log.w(TAG, "Could not send message :" + e.toString());
         }
 
         return retVal;
@@ -247,7 +235,7 @@
     public int registerCallback(Callback callback, Handler handler) {
         synchronized(this) {
             if (mCallback != null) {
-                Log.e(TAG, "Max number of callbacks reached!");
+                Log.w(TAG, "Max number of callbacks reached!");
                 return -1;
             }
             mCallback = callback;
@@ -268,7 +256,7 @@
     public int unregisterCallback(Callback callback) {
       synchronized(this) {
           if (callback != mCallback) {
-              Log.e(TAG, "Cannot recognize callback!");
+              Log.w(TAG, "Cannot recognize callback!");
               return -1;
           }
 
@@ -311,11 +299,11 @@
             try {
                 getBinder().registerCallback(mClientCallback);
             } catch (RemoteException e) {
-                Log.e(TAG, "Could not register callback:" + e);
+                Log.w(TAG, "Could not register callback:" + e);
             }
 
         } else {
-            Log.d(TAG, "failed to getService");
+            Log.w(TAG, "failed to getService");
         }
     }
 
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
index 954e97d..bca2ae6 100644
--- a/core/java/android/hardware/location/ContextHubMessage.java
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -16,10 +16,10 @@
 
 package android.hardware.location;
 
-
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import java.util.Arrays;
 
@@ -32,6 +32,9 @@
     private int mVersion;
     private byte[]mData;
 
+    private static final String TAG = "ContextHubMessage";
+
+
     /**
      * Get the message type
      *
@@ -106,9 +109,11 @@
     private ContextHubMessage(Parcel in) {
         mType = in.readInt();
         mVersion = in.readInt();
-        byte[] byteBuffer = new byte[in.readInt()];
-        in.readByteArray(byteBuffer);
+        int bufferLength = in.readInt();
+        mData = new byte[bufferLength];
+        in.readByteArray(mData);
     }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mType);
         out.writeInt(mVersion);
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 274babe..b65e24e 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -29,12 +29,30 @@
  */
 public class ContextHubService extends IContextHubService.Stub {
 
+    public static final String CONTEXTHUB_SERVICE = "contexthub_service";
+
     private static final String TAG = "ContextHubService";
     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
             + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
-    public static final String CONTEXTHUB_SERVICE = "contexthub_service";
+
+    public static final int ANY_HUB             = -1;
+    public static final int MSG_LOAD_NANO_APP   = 5;
+    public static final int MSG_UNLOAD_NANO_APP = 2;
+
+    private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
+    private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
+    private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
+    private static final int PRE_LOADED_APP_MEM_REQ = 0;
+
+    private static final int MSG_HEADER_SIZE = 4;
+    private static final int MSG_FIELD_TYPE = 0;
+    private static final int MSG_FIELD_VERSION = 1;
+    private static final int MSG_FIELD_HUB_HANDLE = 2;
+    private static final int MSG_FIELD_APP_INSTANCE = 3;
+
+    private static final int OS_APP_INSTANCE = -1;
 
     private final Context mContext;
 
@@ -42,44 +60,27 @@
     private ContextHubInfo[] mContextHubInfo;
     private IContextHubCallback mCallback;
 
+    private native int nativeSendMessage(int[] header, byte[] data);
+    private native ContextHubInfo[] nativeInitialize();
+
+
     public ContextHubService(Context context) {
         mContext = context;
         mContextHubInfo = nativeInitialize();
+        mNanoAppHash = new HashMap<Integer, NanoAppInstanceInfo>();
 
         for (int i = 0; i < mContextHubInfo.length; i++) {
-            Log.v(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
+            Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
                   + ", name:  " + mContextHubInfo[i].getName());
         }
     }
 
-    private native int nativeSendMessage(int[] header, byte[] data);
-    private native ContextHubInfo[] nativeInitialize();
-
     @Override
-    public int registerCallback(IContextHubCallback callback) throws RemoteException{
+    public int registerCallback(IContextHubCallback callback) throws RemoteException {
         checkPermissions();
-        mCallback = callback;
-        return 0;
-    }
-
-
-    private int onMessageReceipt(int[] header, byte[] data) {
-        if (mCallback != null) {
-            // TODO : Defend against unexpected header sizes
-            //        Add abstraction for magic numbers
-            //        onMessageRecipt should pass the right arguments
-            ContextHubMessage msg = new ContextHubMessage(header[0], header[1], data);
-
-            try {
-                mCallback.onMessageReceipt(0, 0, msg);
-            } catch (Exception e) {
-                Log.e(TAG, "Exception " + e + " when calling remote callback");
-                return -1;
-            }
-        } else {
-            Log.d(TAG, "Message Callback is NULL");
+        synchronized(this) {
+          mCallback = callback;
         }
-
         return 0;
     }
 
@@ -118,14 +119,17 @@
         }
 
         // Call Native interface here
-        int[] msgHeader = new int[8];
-        msgHeader[0] = contextHubHandle;
-        msgHeader[1] = app.getAppId();
-        msgHeader[2] = app.getAppVersion();
-        msgHeader[3] = ContextHubManager.MSG_LOAD_NANO_APP;
-        msgHeader[4] = 0; // Loading hints
+        int[] msgHeader = new int[MSG_HEADER_SIZE];
+        msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle;
+        msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+        msgHeader[MSG_FIELD_VERSION] = 0;
+        msgHeader[MSG_FIELD_TYPE] = MSG_LOAD_NANO_APP;
 
-        return nativeSendMessage(msgHeader, app.getAppBinary());
+        if (nativeSendMessage(msgHeader, app.getAppBinary()) != 0) {
+            return -1;
+        }
+        // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+        return 0;
     }
 
     @Override
@@ -137,12 +141,18 @@
         }
 
         // Call Native interface here
-        int[] msgHeader = new int[8];
-        msgHeader[0] = info.getContexthubId();
-        msgHeader[1] = ContextHubManager.MSG_UNLOAD_NANO_APP;
-        msgHeader[2] = info.getHandle();
+        int[] msgHeader = new int[MSG_HEADER_SIZE];
+        msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB;
+        msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+        msgHeader[MSG_FIELD_VERSION] = 0;
+        msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP;
 
-        return nativeSendMessage(msgHeader, null);
+        if(nativeSendMessage(msgHeader, null) != 0) {
+            return -1;
+        }
+
+        // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+        return 0;
     }
 
     @Override
@@ -166,7 +176,7 @@
         for(Integer nanoAppInstance : mNanoAppHash.keySet()) {
             NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
 
-            if(filter.testMatch(info)){
+            if (filter.testMatch(info)){
                 foundInstances.add(nanoAppInstance);
             }
         }
@@ -183,12 +193,12 @@
     public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
             throws RemoteException {
         checkPermissions();
-        int[] msgHeader = new int[8];
-        msgHeader[0] = ContextHubManager.MSG_DATA_SEND;
-        msgHeader[1] = hubHandle;
-        msgHeader[2] = nanoAppHandle;
-        msgHeader[3] = msg.getMsgType();
-        msgHeader[4] = msg.getVersion();
+
+        int[] msgHeader = new int[MSG_HEADER_SIZE];
+        msgHeader[MSG_FIELD_HUB_HANDLE] = hubHandle;
+        msgHeader[MSG_FIELD_APP_INSTANCE] = nanoAppHandle;
+        msgHeader[MSG_FIELD_VERSION] = msg.getVersion();
+        msgHeader[MSG_FIELD_TYPE] = msg.getMsgType();
 
         return nativeSendMessage(msgHeader, msg.getData());
     }
@@ -196,5 +206,52 @@
     private void checkPermissions() {
         mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
     }
-}
 
+    private int onMessageReceipt(int[] header, byte[] data) {
+        if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
+            return  -1;
+        }
+
+        synchronized(this) {
+            if (mCallback != null) {
+                ContextHubMessage msg = new ContextHubMessage(header[MSG_FIELD_TYPE],
+                        header[MSG_FIELD_VERSION],
+                        data);
+
+                try {
+                    mCallback.onMessageReceipt(header[MSG_FIELD_HUB_HANDLE],
+                            header[MSG_FIELD_APP_INSTANCE],
+                            msg);
+                } catch (Exception e) {
+                    Log.w(TAG, "Exception " + e + " when calling remote callback");
+                    return -1;
+                }
+            } else {
+                Log.d(TAG, "Message Callback is NULL");
+            }
+        }
+
+        return 0;
+    }
+
+    private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) {
+        // App Id encodes vendor & version
+        NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo();
+
+        appInfo.setAppId(appId);
+        appInfo.setAppVersion(appVersion);
+        appInfo.setName(PRE_LOADED_APP_NAME);
+        appInfo.setContexthubId(hubHandle);
+        appInfo.setHandle(appInstanceHandle);
+        appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER);
+        appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ);
+        appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ);
+        appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ);
+
+        mNanoAppHash.put(appInstanceHandle, appInfo);
+        Log.d(TAG, "Added app instance " + appInstanceHandle + " with id " + appId
+              + " version " + appVersion);
+
+        return 0;
+    }
+}
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 369f9e4..8db70e9 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 /**
  * @hide
@@ -27,6 +28,8 @@
 @SystemApi
 public class NanoAppFilter {
 
+    private static final String TAG = "NanoAppFilter";
+
     // The appId, can be set to APP_ID_ANY
     private long mAppId;
 
@@ -54,6 +57,10 @@
      * If this flag is set, only versions strictly less than the version specified shall match.
      */
     public static final int FLAGS_VERSION_LESS_THAN   = 4;
+    /**
+     * If this flag is set, only versions strictly equal to the
+     * version specified shall match.
+     */
     public static final int FLAGS_VERSION_STRICTLY_EQUAL = 8;
 
     /**
@@ -117,14 +124,9 @@
      * @return true if this is a match, false otherwise
      */
     public boolean testMatch(NanoAppInstanceInfo info) {
-        if ((mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
+        return (mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
                 (mAppId == APP_ANY || info.getAppId() == mAppId) &&
-               // (mAppIdVendorMask == VENDOR_ANY) TODO : Expose Vendor mask cleanly
-                (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()))) {
-            return true;
-        } else {
-            return false;
-        }
+                (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()));
     }
 
     public static final Parcelable.Creator<NanoAppFilter> CREATOR
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index ac62919..977f645 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -29,7 +29,7 @@
     private String mPublisher;
     private String mName;
 
-    private int mAppId;
+    private long mAppId;
     private int mAppVersion;
 
     private int mNeededReadMemBytes;
@@ -59,6 +59,8 @@
      * set the publisher name for the app
      *
      * @param publisher - name of the publisher
+     *
+     * @hide
      */
     public void setPublisher(String publisher) {
         mPublisher = publisher;
@@ -77,6 +79,8 @@
      * set the name of the app
      *
      * @param name - name of the app
+     *
+     * @hide
      */
     public void setName(String name) {
         mName = name;
@@ -87,7 +91,7 @@
      *
      * @return int - application identifier
      */
-    public int getAppId() {
+    public long getAppId() {
         return mAppId;
     }
 
@@ -95,8 +99,10 @@
      * Set the application identifier
      *
      * @param appId - application identifier
+     *
+     * @hide
      */
-    public void setAppId(int appId) {
+    public void setAppId(long appId) {
         mAppId = appId;
     }
 
@@ -113,6 +119,8 @@
      * Set the application version
      *
      * @param appVersion - version of the app
+     *
+     * @hide
      */
     public void setAppVersion(int appVersion) {
         mAppVersion = appVersion;
@@ -131,6 +139,8 @@
      * Set the read memory needed by the app
      *
      * @param neededReadMemBytes - readable Memory needed in bytes
+     *
+     * @hide
      */
     public void setNeededReadMemBytes(int neededReadMemBytes) {
         mNeededReadMemBytes = neededReadMemBytes;
@@ -150,6 +160,8 @@
      *
      * @param neededWriteMemBytes - writable memory needed by the
      *                            app
+     *
+     * @hide
      */
     public void setNeededWriteMemBytes(int neededWriteMemBytes) {
         mNeededWriteMemBytes = neededWriteMemBytes;
@@ -169,6 +181,8 @@
      *
      * @param neededExecMemBytes - executable memory needed by the
      *                           app
+     *
+     * @hide
      */
     public void setNeededExecMemBytes(int neededExecMemBytes) {
         mNeededExecMemBytes = neededExecMemBytes;
@@ -187,6 +201,8 @@
      * set the sensors needed by this app
      *
      * @param neededSensors - all the sensors needed by this app
+     *
+     * @hide
      */
     public void setNeededSensors(int[] neededSensors) {
         mNeededSensors = neededSensors;
@@ -206,6 +222,8 @@
      *
      * @param outputEvents - the events that may be generated by
      *                     this app
+     *
+     * @hide
      */
     public void setOutputEvents(int[] outputEvents) {
         mOutputEvents = outputEvents;
@@ -224,6 +242,8 @@
      * set the context hub identifier
      *
      * @param contexthubId - system wide unique identifier
+     *
+     * @hide
      */
     public void setContexthubId(int contexthubId) {
         mContexthubId = contexthubId;
@@ -242,6 +262,8 @@
      * set the handle for an app instance
      *
      * @param handle - handle to this instance
+     *
+     * @hide
      */
     public void setHandle(int handle) {
         mHandle = handle;
@@ -252,7 +274,7 @@
         mPublisher = in.readString();
         mName = in.readString();
 
-        mAppId = in.readInt();
+        mAppId = in.readLong();
         mAppVersion = in.readInt();
         mNeededReadMemBytes = in.readInt();
         mNeededWriteMemBytes = in.readInt();
@@ -274,7 +296,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(mPublisher);
         out.writeString(mName);
-        out.writeInt(mAppId);
+        out.writeLong(mAppId);
         out.writeInt(mAppVersion);
         out.writeInt(mContexthubId);
         out.writeInt(mNeededReadMemBytes);
@@ -286,7 +308,6 @@
 
         out.writeInt(mOutputEvents.length);
         out.writeIntArray(mOutputEvents);
-
     }
 
     public static final Parcelable.Creator<NanoAppInstanceInfo> CREATOR
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 97bd5d2..8c5f603 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -40,6 +40,9 @@
 
     public int leaseDuration;
 
+    /** Link MTU option. 0 means unset. */
+    public int mtu;
+
     public DhcpResults() {
         super();
     }
@@ -57,19 +60,7 @@
             serverAddress = source.serverAddress;
             vendorInfo = source.vendorInfo;
             leaseDuration = source.leaseDuration;
-        }
-    }
-
-    /**
-     * Updates the DHCP fields that need to be retained from
-     * original DHCP request if the current renewal shows them
-     * being empty.
-     */
-    public void updateFromDhcpRequest(DhcpResults orig) {
-        if (orig == null) return;
-        if (gateway == null) gateway = orig.gateway;
-        if (dnsServers.size() == 0) {
-            dnsServers.addAll(orig.dnsServers);
+            mtu = source.mtu;
         }
     }
 
@@ -89,6 +80,7 @@
         super.clear();
         vendorInfo = null;
         leaseDuration = 0;
+        mtu = 0;
     }
 
     @Override
@@ -98,6 +90,7 @@
         str.append(" DHCP server ").append(serverAddress);
         str.append(" Vendor info ").append(vendorInfo);
         str.append(" lease ").append(leaseDuration).append(" seconds");
+        if (mtu != 0) str.append(" MTU ").append(mtu);
 
         return str.toString();
     }
@@ -113,7 +106,8 @@
         return super.equals((StaticIpConfiguration) obj) &&
                 Objects.equals(serverAddress, target.serverAddress) &&
                 Objects.equals(vendorInfo, target.vendorInfo) &&
-                leaseDuration == target.leaseDuration;
+                leaseDuration == target.leaseDuration &&
+                mtu == target.mtu;
     }
 
     /** Implement the Parcelable interface */
@@ -134,6 +128,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         super.writeToParcel(dest, flags);
         dest.writeInt(leaseDuration);
+        dest.writeInt(mtu);
         NetworkUtils.parcelInetAddress(dest, serverAddress, flags);
         dest.writeString(vendorInfo);
     }
@@ -141,6 +136,7 @@
     private static void readFromParcel(DhcpResults dhcpResults, Parcel in) {
         StaticIpConfiguration.readFromParcel(dhcpResults, in);
         dhcpResults.leaseDuration = in.readInt();
+        dhcpResults.mtu = in.readInt();
         dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in);
         dhcpResults.vendorInfo = in.readString();
     }
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 05dd48b..1128074 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -208,6 +208,18 @@
     }
 
     /**
+     * Removes any entry with the given key from the mapping of this Bundle.
+     *
+     * @param key a String key
+     */
+    public void remove(String key) {
+        super.remove(key);
+        if ((mFlags & FLAG_HAS_FDS) != 0) {
+            mFlags &= ~FLAG_HAS_FDS_KNOWN;
+        }
+    }
+
+    /**
      * Inserts all mappings from the given Bundle into this Bundle.
      *
      * @param bundle a Bundle
@@ -288,6 +300,8 @@
 
             if (fdFound) {
                 mFlags |= FLAG_HAS_FDS;
+            } else {
+                mFlags &= ~FLAG_HAS_FDS;
             }
             mFlags |= FLAG_HAS_FDS_KNOWN;
         }
@@ -315,6 +329,8 @@
                 mMap.removeAt(i);
             }
         }
+        mFlags |= FLAG_HAS_FDS_KNOWN;
+        mFlags &= ~FLAG_HAS_FDS;
     }
 
     /**
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index e9c196d..721c94e 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources.NotFoundException;
@@ -31,6 +32,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -49,7 +51,7 @@
     @IntDef(flag = true, value = {
             COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR
     })
-    public @interface ColorMode {
+    @interface ColorMode {
     }
     /** Color mode: Monochrome color scheme, for example one color is used. */
     public static final int COLOR_MODE_MONOCHROME = 1 << 0;
@@ -64,7 +66,7 @@
     @IntDef(flag = true, value = {
             DUPLEX_MODE_NONE, DUPLEX_MODE_LONG_EDGE, DUPLEX_MODE_SHORT_EDGE
     })
-    public @interface DuplexMode {
+    @interface DuplexMode {
     }
     /** Duplex mode: No duplexing. */
     public static final int DUPLEX_MODE_NONE = 1 << 0;
@@ -76,23 +78,29 @@
     private static final int VALID_DUPLEX_MODES =
             DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
 
-    private MediaSize mMediaSize;
-    private Resolution mResolution;
-    private Margins mMinMargins;
+    private @Nullable MediaSize mMediaSize;
+    private @Nullable Resolution mResolution;
+    private @Nullable Margins mMinMargins;
 
-    private int mColorMode;
-    private int mDuplexMode;
+    private @IntRange(from = 0) int mColorMode;
+    private @IntRange(from = 0) int mDuplexMode;
 
     PrintAttributes() {
         /* hide constructor */
     }
 
     private PrintAttributes(@NonNull Parcel parcel) {
-        mMediaSize = (parcel.readInt() ==  1) ? MediaSize.createFromParcel(parcel) : null;
-        mResolution = (parcel.readInt() ==  1) ? Resolution.createFromParcel(parcel) : null;
-        mMinMargins = (parcel.readInt() ==  1) ? Margins.createFromParcel(parcel) : null;
+        mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null;
+        mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null;
+        mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
         mColorMode = parcel.readInt();
+        if (mColorMode != 0) {
+            enforceValidColorMode(mColorMode);
+        }
         mDuplexMode = parcel.readInt();
+        if (mDuplexMode != 0) {
+            enforceValidDuplexMode(mDuplexMode);
+        }
     }
 
     /**
@@ -179,7 +187,7 @@
      * @see #COLOR_MODE_COLOR
      * @see #COLOR_MODE_MONOCHROME
      */
-    public @ColorMode int getColorMode() {
+    public @IntRange(from = 0) int getColorMode() {
         return mColorMode;
     }
 
@@ -214,13 +222,13 @@
     /**
      * Gets the duplex mode.
      *
-     * @return The duplex mode.
+     * @return The duplex mode or zero if not set.
      *
      * @see #DUPLEX_MODE_NONE
      * @see #DUPLEX_MODE_LONG_EDGE
      * @see #DUPLEX_MODE_SHORT_EDGE
      */
-    public @DuplexMode int getDuplexMode() {
+    public @IntRange(from = 0) int getDuplexMode() {
         return mDuplexMode;
     }
 
@@ -448,7 +456,7 @@
         private static final String LOG_TAG = "MediaSize";
 
         private static final Map<String, MediaSize> sIdToMediaSizeMap =
-                new ArrayMap<String, MediaSize>();
+                new ArrayMap<>();
 
         /**
          * Unknown media size in portrait mode.
@@ -781,15 +789,15 @@
                 new MediaSize("JPN_YOU4", "android",
                         R.string.mediasize_japanese_you4, 4134, 9252);
 
-        private final String mId;
+        private final @NonNull String mId;
         /**@hide */
-        public final String mLabel;
+        public final @NonNull String mLabel;
         /**@hide */
-        public final String mPackageName;
+        public final @Nullable String mPackageName;
         /**@hide */
-        public final int mLabelResId;
-        private final int mWidthMils;
-        private final int mHeightMils;
+        public final @StringRes int mLabelResId;
+        private final @IntRange(from = 1) int mWidthMils;
+        private final @IntRange(from = 1) int mHeightMils;
 
         /**
          * Creates a new instance.
@@ -808,29 +816,7 @@
          */
         public MediaSize(String id, String packageName, int labelResId,
                 int widthMils, int heightMils) {
-            if (TextUtils.isEmpty(id)) {
-                throw new IllegalArgumentException("id cannot be empty.");
-            }
-            if (TextUtils.isEmpty(packageName)) {
-                throw new IllegalArgumentException("packageName cannot be empty.");
-            }
-            if (labelResId <= 0) {
-                throw new IllegalArgumentException("labelResId must be greater than zero.");
-            }
-            if (widthMils <= 0) {
-                throw new IllegalArgumentException("widthMils "
-                        + "cannot be less than or equal to zero.");
-            }
-            if (heightMils <= 0) {
-                throw new IllegalArgumentException("heightMils "
-                       + "cannot be less than or euqual to zero.");
-            }
-            mPackageName = packageName;
-            mId = id;
-            mLabelResId = labelResId;
-            mWidthMils = widthMils;
-            mHeightMils = heightMils;
-            mLabel = null;
+            this(id, null, packageName, widthMils, heightMils, labelResId);
 
             // Build this mapping only for predefined media sizes.
             sIdToMediaSizeMap.put(mId, this);
@@ -851,26 +837,7 @@
          */
         public MediaSize(@NonNull String id, @NonNull String label,
                 @IntRange(from = 1) int widthMils, @IntRange(from = 1) int heightMils) {
-            if (TextUtils.isEmpty(id)) {
-                throw new IllegalArgumentException("id cannot be empty.");
-            }
-            if (TextUtils.isEmpty(label)) {
-                throw new IllegalArgumentException("label cannot be empty.");
-            }
-            if (widthMils <= 0) {
-                throw new IllegalArgumentException("widthMils "
-                        + "cannot be less than or equal to zero.");
-            }
-            if (heightMils <= 0) {
-                throw new IllegalArgumentException("heightMils "
-                       + "cannot be less than or euqual to zero.");
-            }
-            mId = id;
-            mLabel = label;
-            mWidthMils = widthMils;
-            mHeightMils = heightMils;
-            mLabelResId = 0;
-            mPackageName = null;
+            this(id, label, null, widthMils, heightMils, 0);
         }
 
         /**
@@ -890,15 +857,37 @@
             return definedMediaSizes;
         }
 
-        /** @hide */
-        public MediaSize(String id, String label, String packageName,
-                int widthMils, int heightMils, int labelResId) {
+        /**
+         * Creates a new instance.
+         *
+         * @param id The unique media size id. It is unique amongst other media sizes
+         *        supported by the printer.
+         * @param label The <strong>localized</strong> human readable label.
+         * @param packageName The name of the creating package.
+         * @param widthMils The width in mils (thousands of an inch).
+         * @param heightMils The height in mils (thousands of an inch).
+         * @param labelResId The resource if of a human readable label.
+         *
+         * @throws IllegalArgumentException If the id is empty or the label is unset
+         * or the widthMils is less than or equal to zero or the heightMils is less
+         * than or equal to zero.
+         *
+         * @hide
+         */
+        public MediaSize(String id, String label, String packageName, int widthMils, int heightMils,
+                int labelResId) {
             mPackageName = packageName;
-            mId = id;
+            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty.");
             mLabelResId = labelResId;
-            mWidthMils = widthMils;
-            mHeightMils = heightMils;
+            mWidthMils = Preconditions.checkArgumentPositive(widthMils, "widthMils cannot be " +
+                    "less than or equal to zero.");
+            mHeightMils = Preconditions.checkArgumentPositive(heightMils, "heightMils cannot be " +
+                    "less than or equal to zero.");
             mLabel = label;
+
+            // The label has to be either a string ot a StringRes
+            Preconditions.checkArgument(!TextUtils.isEmpty(label) !=
+                    (!TextUtils.isEmpty(packageName) && labelResId != 0), "label cannot be empty.");
         }
 
         /**
@@ -926,10 +915,7 @@
                 try {
                     return packageManager.getResourcesForApplication(
                             mPackageName).getString(mLabelResId);
-                } catch (NotFoundException nfe) {
-                    Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                            + " from package " + mPackageName);
-                } catch (NameNotFoundException nnfee) {
+                } catch (NotFoundException | NameNotFoundException e) {
                     Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
                             + " from package " + mPackageName);
                 }
@@ -1084,10 +1070,10 @@
      * the one with 300 DPI resolution.
      */
     public static final class Resolution {
-        private final String mId;
-        private final String mLabel;
-        private final int mHorizontalDpi;
-        private final int mVerticalDpi;
+        private final @NonNull String mId;
+        private final @NonNull String mLabel;
+        private final @IntRange(from = 1) int mHorizontalDpi;
+        private final @IntRange(from = 1) int mVerticalDpi;
 
         /**
          * Creates a new instance.
@@ -1244,8 +1230,7 @@
          * @param rightMils The right margin in mils (thousands of an inch).
          * @param bottomMils The bottom margin in mils (thousands of an inch).
          */
-        public Margins(@IntRange(from = 0) int leftMils, @IntRange(from = 0) int topMils,
-                @IntRange(from = 0) int rightMils, @IntRange(from = 0) int bottomMils) {
+        public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
             mTopMils = topMils;
             mLeftMils = leftMils;
             mRightMils = rightMils;
@@ -1257,7 +1242,7 @@
          *
          * @return The left margin.
          */
-        public @IntRange(from = 0) int getLeftMils() {
+        public int getLeftMils() {
             return mLeftMils;
         }
 
@@ -1266,7 +1251,7 @@
          *
          * @return The top margin.
          */
-        public @IntRange(from = 0) int getTopMils() {
+        public int getTopMils() {
             return mTopMils;
         }
 
@@ -1275,7 +1260,7 @@
          *
          * @return The right margin.
          */
-        public @IntRange(from = 0) int getRightMils() {
+        public int getRightMils() {
             return mRightMils;
         }
 
@@ -1284,7 +1269,7 @@
          *
          * @return The bottom margin.
          */
-        public @IntRange(from = 0) int getBottomMils() {
+        public int getBottomMils() {
             return mBottomMils;
         }
 
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index db3b6f4..bec6f29 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -22,6 +22,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -114,8 +115,8 @@
      */
     public static final int CONTENT_TYPE_PHOTO = 1;
 
-    private String mName;
-    private int mPageCount;
+    private @NonNull String mName;
+    private @IntRange(from = -1) int mPageCount;
     private int mContentType;
     private long mDataSize;
 
@@ -144,10 +145,11 @@
      * @param parcel Data from which to initialize.
      */
     private PrintDocumentInfo(Parcel parcel) {
-        mName = parcel.readString();
+        mName = Preconditions.checkStringNotEmpty(parcel.readString());
         mPageCount = parcel.readInt();
+        Preconditions.checkArgument(mPageCount == PAGE_COUNT_UNKNOWN || mPageCount > 0);
         mContentType = parcel.readInt();
-        mDataSize = parcel.readLong();
+        mDataSize = Preconditions.checkArgumentNonnegative(parcel.readLong());
     }
 
     /**
@@ -180,7 +182,7 @@
      * @see #CONTENT_TYPE_DOCUMENT
      * @see #CONTENT_TYPE_PHOTO
      */
-    public @ContentType int getContentType() {
+    public int getContentType() {
         return mContentType;
     }
 
@@ -262,13 +264,13 @@
         builder.append("PrintDocumentInfo{");
         builder.append("name=").append(mName);
         builder.append(", pageCount=").append(mPageCount);
-        builder.append(", contentType=").append(contentTyepToString(mContentType));
+        builder.append(", contentType=").append(contentTypeToString(mContentType));
         builder.append(", dataSize=").append(mDataSize);
         builder.append("}");
         return builder.toString();
     }
 
-    private String contentTyepToString(int contentType) {
+    private String contentTypeToString(int contentType) {
         switch (contentType) {
             case CONTENT_TYPE_DOCUMENT: {
                 return "CONTENT_TYPE_DOCUMENT";
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index d13879b..01c23f6 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -24,11 +24,13 @@
 import android.print.PrintAttributes.Margins;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.IntConsumer;
 
 /**
  * This class represents the capabilities of a printer. Instances
@@ -55,9 +57,9 @@
 
     private static final Margins DEFAULT_MARGINS = new Margins(0,  0,  0,  0);
 
-    private Margins mMinMargins = DEFAULT_MARGINS;
-    private List<MediaSize> mMediaSizes;
-    private List<Resolution> mResolutions;
+    private @NonNull Margins mMinMargins = DEFAULT_MARGINS;
+    private @NonNull List<MediaSize> mMediaSizes;
+    private @NonNull List<Resolution> mResolutions;
 
     private int mColorModes;
     private int mDuplexModes;
@@ -205,15 +207,37 @@
         return builder.build();
     }
 
+    /**
+     * Call enforceSingle for each bit in the mask.
+     *
+     * @param mask The mask
+     * @param enforceSingle The function to call
+     */
+    private static void enforceValidMask(int mask, IntConsumer enforceSingle) {
+        int current = mask;
+        while (current > 0) {
+            final int currentMode = (1 << Integer.numberOfTrailingZeros(current));
+            current &= ~currentMode;
+            enforceSingle.accept(currentMode);
+        }
+    }
+
     private PrinterCapabilitiesInfo(Parcel parcel) {
-        mMinMargins = readMargins(parcel);
+        mMinMargins = Preconditions.checkNotNull(readMargins(parcel));
         readMediaSizes(parcel);
         readResolutions(parcel);
 
         mColorModes = parcel.readInt();
+        enforceValidMask(mColorModes,
+                (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode));
+
         mDuplexModes = parcel.readInt();
+        enforceValidMask(mDuplexModes,
+                (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode));
 
         readDefaults(parcel);
+        Preconditions.checkArgument(mMediaSizes.size() > mDefaults[PROPERTY_MEDIA_SIZE]);
+        Preconditions.checkArgument(mResolutions.size() > mDefaults[PROPERTY_RESOLUTION]);
     }
 
     @Override
@@ -537,12 +561,8 @@
          */
         public @NonNull Builder setColorModes(@ColorMode int colorModes,
                 @ColorMode int defaultColorMode) {
-            int currentModes = colorModes;
-            while (currentModes > 0) {
-                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
-                currentModes &= ~currentMode;
-                PrintAttributes.enforceValidColorMode(currentMode);
-            }
+            enforceValidMask(colorModes,
+                    (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode));
             PrintAttributes.enforceValidColorMode(defaultColorMode);
             mPrototype.mColorModes = colorModes;
             mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
@@ -568,12 +588,8 @@
          */
         public @NonNull Builder setDuplexModes(@DuplexMode int duplexModes,
                 @DuplexMode int defaultDuplexMode) {
-            int currentModes = duplexModes;
-            while (currentModes > 0) {
-                final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
-                currentModes &= ~currentMode;
-                PrintAttributes.enforceValidDuplexMode(currentMode);
-            }
+            enforceValidMask(duplexModes,
+                    (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode));
             PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
             mPrototype.mDuplexModes = duplexModes;
             mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode;
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 7af0b05..0557d13 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -35,7 +35,6 @@
 import android.view.ActionMode;
 import android.view.Display;
 import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5bcf102..4ba97d5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -180,7 +180,12 @@
     // caller must call setNewConfiguration() sometime later.
     Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
             IBinder freezeThisOneIfNeeded);
-    void setNewConfiguration(in Configuration config);
+    // Notify window manager of the new configuration. Returns an array of stack ids that's
+    // affected by the update, ActivityManager should resize these stacks.
+    int[] setNewConfiguration(in Configuration config);
+
+    // Retrieves the new bounds after the configuration update evaluated by window manager.
+    Rect getBoundsForNewConfiguration(int stackId);
 
     void startFreezingScreen(int exitAnim, int enterAnim);
     void stopFreezingScreen();
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
index c2bd347..eee925d 100644
--- a/core/java/android/view/KeyboardShortcutInfo.java
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -51,7 +51,7 @@
         mLabel = label;
         mIcon = icon;
         mBaseCharacter = MIN_VALUE;
-        checkArgument(keycode > KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode());
+        checkArgument(keycode >= KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode());
         mKeycode = keycode;
         mModifiers = modifiers;
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3ee6a8d..784164d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18079,13 +18079,7 @@
          * to clear the previous drawable. setVisible first while we still have the callback set.
          */
         if (mBackground != null) {
-            // It's possible for this method to be invoked from the View constructor before
-            // subclass constructors have run. Drawables can and should trigger invalidations
-            // and other activity with their callback on visibility changes, which shouldn't
-            // happen before subclass constructors finish. However, we won't have set the
-            // drawable as visible until the view becomes attached. This guard below keeps
-            // multiple calls to this method from constructors from causing issues.
-            if (mBackground.isVisible()) {
+            if (isAttachedToWindow()) {
                 mBackground.setVisible(false, false);
             }
             mBackground.setCallback(null);
@@ -18320,13 +18314,7 @@
         }
 
         if (mForegroundInfo.mDrawable != null) {
-            // It's possible for this method to be invoked from the View constructor before
-            // subclass constructors have run. Drawables can and should trigger invalidations
-            // and other activity with their callback on visibility changes, which shouldn't
-            // happen before subclass constructors finish. However, we won't have set the
-            // drawable as visible until the view becomes attached. This guard below keeps
-            // multiple calls to this method from constructors from causing issues.
-            if (mForegroundInfo.mDrawable.isVisible()) {
+            if (isAttachedToWindow()) {
                 mForegroundInfo.mDrawable.setVisible(false, false);
             }
             mForegroundInfo.mDrawable.setCallback(null);
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index d884f19..cc496dc 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -573,8 +573,12 @@
                 intent.getDataString().substring("package:".length()));
     }
 
-    private static IWebViewUpdateService getUpdateService() {
-        return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
+    private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
+
+    /** @hide */
+    public static IWebViewUpdateService getUpdateService() {
+        return IWebViewUpdateService.Stub.asInterface(
+                ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
     }
 
     private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index d106dba..5d091c9 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -16,12 +16,16 @@
 
 package android.webkit;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.util.Arrays;
 
-/** @hide */
+/**
+ * @hide
+ */
+@SystemApi
 public final class WebViewProviderInfo implements Parcelable {
 
     public WebViewProviderInfo(String packageName, String description,
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
new file mode 100644
index 0000000..4e83d88
--- /dev/null
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class WebViewUpdateService {
+
+    private WebViewUpdateService () {}
+
+    /**
+     * Fetch all packages that could potentially implement WebView.
+     */
+    public static WebViewProviderInfo[] getAllWebViewPackages() {
+        try {
+            return getUpdateService().getAllWebViewPackages();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Fetch all packages that could potentially implement WebView and are currently valid.
+     */
+    public static WebViewProviderInfo[] getValidWebViewPackages() {
+        try {
+            return getUpdateService().getValidWebViewPackages();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Used by DevelopmentSetting to get the name of the WebView provider currently in use.
+     */
+    public static String getCurrentWebViewPackageName() {
+        try {
+            return getUpdateService().getCurrentWebViewPackageName();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static IWebViewUpdateService getUpdateService() {
+        return WebViewFactory.getUpdateService();
+    }
+}
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 027f6d6..9d228cf 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
@@ -61,17 +62,13 @@
 
     private final LayoutInflater mInflater;
 
-    /**
-     * Contains the list of objects that represent the data of this ArrayAdapter.
-     * The content of this list is referred to as "the array" in the documentation.
-     */
-    private List<T> mObjects;
+    private final Context mContext;
 
     /**
      * The resource indicating what views to inflate to display the content of this
      * array adapter.
      */
-    private int mResource;
+    private final int mResource;
 
     /**
      * The resource indicating what views to inflate to display the content of this
@@ -80,7 +77,13 @@
     private int mDropDownResource;
 
     /**
-     * If the inflated resource is not a TextView, {@link #mFieldId} is used to find
+     * Contains the list of objects that represent the data of this ArrayAdapter.
+     * The content of this list is referred to as "the array" in the documentation.
+     */
+    private List<T> mObjects;
+
+    /**
+     * If the inflated resource is not a TextView, {@code mFieldId} is used to find
      * a TextView inside the inflated views hierarchy. This field must contain the
      * identifier that matches the one defined in the resource file.
      */
@@ -92,8 +95,6 @@
      */
     private boolean mNotifyOnChange = true;
 
-    private Context mContext;
-
     // A copy of the original mObjects array, initialized from and then used instead as soon as
     // the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
     private ArrayList<T> mOriginalValues;
@@ -109,8 +110,8 @@
      * @param resource The resource ID for a layout file containing a TextView to use when
      *                 instantiating views.
      */
-    public ArrayAdapter(Context context, @LayoutRes int resource) {
-        this(context, resource, 0, new ArrayList<T>());
+    public ArrayAdapter(@NonNull Context context, @LayoutRes int resource) {
+        this(context, resource, 0, new ArrayList<>());
     }
 
     /**
@@ -121,8 +122,9 @@
      *                 instantiating views.
      * @param textViewResourceId The id of the TextView within the layout resource to be populated
      */
-    public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId) {
-        this(context, resource, textViewResourceId, new ArrayList<T>());
+    public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+            @IdRes int textViewResourceId) {
+        this(context, resource, textViewResourceId, new ArrayList<>());
     }
 
     /**
@@ -133,7 +135,7 @@
      *                 instantiating views.
      * @param objects The objects to represent in the ListView.
      */
-    public ArrayAdapter(Context context, @LayoutRes int resource, @NonNull T[] objects) {
+    public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) {
         this(context, resource, 0, Arrays.asList(objects));
     }
 
@@ -146,8 +148,8 @@
      * @param textViewResourceId The id of the TextView within the layout resource to be populated
      * @param objects The objects to represent in the ListView.
      */
-    public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
-            @NonNull T[] objects) {
+    public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+            @IdRes int textViewResourceId, @NonNull T[] objects) {
         this(context, resource, textViewResourceId, Arrays.asList(objects));
     }
 
@@ -159,7 +161,8 @@
      *                 instantiating views.
      * @param objects The objects to represent in the ListView.
      */
-    public ArrayAdapter(Context context, @LayoutRes int resource, @NonNull List<T> objects) {
+    public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+            @NonNull List<T> objects) {
         this(context, resource, 0, objects);
     }
 
@@ -172,8 +175,8 @@
      * @param textViewResourceId The id of the TextView within the layout resource to be populated
      * @param objects The objects to represent in the ListView.
      */
-    public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
-            @NonNull List<T> objects) {
+    public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+            @IdRes int textViewResourceId, @NonNull List<T> objects) {
         mContext = context;
         mInflater = LayoutInflater.from(context);
         mResource = mDropDownResource = resource;
@@ -186,7 +189,7 @@
      *
      * @param object The object to add at the end of the array.
      */
-    public void add(T object) {
+    public void add(@Nullable T object) {
         synchronized (mLock) {
             if (mOriginalValues != null) {
                 mOriginalValues.add(object);
@@ -201,8 +204,17 @@
      * Adds the specified Collection at the end of the array.
      *
      * @param collection The Collection to add at the end of the array.
+     * @throws UnsupportedOperationException if the <tt>addAll</tt> operation
+     *         is not supported by this list
+     * @throws ClassCastException if the class of an element of the specified
+     *         collection prevents it from being added to this list
+     * @throws NullPointerException if the specified collection contains one
+     *         or more null elements and this list does not permit null
+     *         elements, or if the specified collection is null
+     * @throws IllegalArgumentException if some property of an element of the
+     *         specified collection prevents it from being added to this list
      */
-    public void addAll(Collection<? extends T> collection) {
+    public void addAll(@NonNull Collection<? extends T> collection) {
         synchronized (mLock) {
             if (mOriginalValues != null) {
                 mOriginalValues.addAll(collection);
@@ -235,7 +247,7 @@
      * @param object The object to insert into the array.
      * @param index The index at which the object must be inserted.
      */
-    public void insert(T object, int index) {
+    public void insert(@Nullable T object, int index) {
         synchronized (mLock) {
             if (mOriginalValues != null) {
                 mOriginalValues.add(index, object);
@@ -251,7 +263,7 @@
      *
      * @param object The object to remove.
      */
-    public void remove(T object) {
+    public void remove(@Nullable T object) {
         synchronized (mLock) {
             if (mOriginalValues != null) {
                 mOriginalValues.remove(object);
@@ -282,7 +294,7 @@
      * @param comparator The comparator used to sort the objects contained
      *        in this adapter.
      */
-    public void sort(Comparator<? super T> comparator) {
+    public void sort(@NonNull Comparator<? super T> comparator) {
         synchronized (mLock) {
             if (mOriginalValues != null) {
                 Collections.sort(mOriginalValues, comparator);
@@ -293,9 +305,6 @@
         if (mNotifyOnChange) notifyDataSetChanged();
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public void notifyDataSetChanged() {
         super.notifyDataSetChanged();
@@ -326,21 +335,17 @@
      *
      * @return The Context associated with this adapter.
      */
-    public Context getContext() {
+    public @NonNull Context getContext() {
         return mContext;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
     public int getCount() {
         return mObjects.size();
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public T getItem(int position) {
+    @Override
+    public @Nullable T getItem(int position) {
         return mObjects.get(position);
     }
 
@@ -351,28 +356,25 @@
      *
      * @return The position of the specified item.
      */
-    public int getPosition(T item) {
+    public int getPosition(@Nullable T item) {
         return mObjects.indexOf(item);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
     public long getItemId(int position) {
         return position;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public View getView(int position, View convertView, ViewGroup parent) {
+    @Override
+    public @NonNull View getView(int position, @Nullable View convertView,
+            @NonNull ViewGroup parent) {
         return createViewFromResource(mInflater, position, convertView, parent, mResource);
     }
 
-    private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
-            ViewGroup parent, int resource) {
-        View view;
-        TextView text;
+    private @NonNull View createViewFromResource(@NonNull LayoutInflater inflater, int position,
+            @Nullable View convertView, @NonNull ViewGroup parent, int resource) {
+        final View view;
+        final TextView text;
 
         if (convertView == null) {
             view = inflater.inflate(resource, parent, false);
@@ -387,6 +389,12 @@
             } else {
                 //  Otherwise, find the TextView field within the layout
                 text = (TextView) view.findViewById(mFieldId);
+
+                if (text == null) {
+                    throw new RuntimeException("Failed to find view with ID "
+                            + mContext.getResources().getResourceName(mFieldId)
+                            + " in item layout");
+                }
             }
         } catch (ClassCastException e) {
             Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
@@ -394,9 +402,9 @@
                     "ArrayAdapter requires the resource ID to be a TextView", e);
         }
 
-        T item = getItem(position);
+        final T item = getItem(position);
         if (item instanceof CharSequence) {
-            text.setText((CharSequence)item);
+            text.setText((CharSequence) item);
         } else {
             text.setText(item.toString());
         }
@@ -426,7 +434,7 @@
      * @see #getDropDownView(int, View, ViewGroup)
      */
     @Override
-    public void setDropDownViewTheme(Resources.Theme theme) {
+    public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
         if (theme == null) {
             mDropDownInflater = null;
         } else if (theme == mInflater.getContext().getTheme()) {
@@ -438,12 +446,13 @@
     }
 
     @Override
-    public Resources.Theme getDropDownViewTheme() {
+    public @Nullable Resources.Theme getDropDownViewTheme() {
         return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
     }
 
     @Override
-    public View getDropDownView(int position, View convertView, ViewGroup parent) {
+    public View getDropDownView(int position, @Nullable View convertView,
+            @NonNull ViewGroup parent) {
         final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
         return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
     }
@@ -458,16 +467,14 @@
      *
      * @return An ArrayAdapter<CharSequence>.
      */
-    public static ArrayAdapter<CharSequence> createFromResource(Context context,
+    public static @NonNull ArrayAdapter<CharSequence> createFromResource(@NonNull Context context,
             @ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
-        CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
-        return new ArrayAdapter<CharSequence>(context, textViewResId, strings);
+        final CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
+        return new ArrayAdapter<>(context, textViewResId, strings);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public Filter getFilter() {
+    @Override
+    public @NonNull Filter getFilter() {
         if (mFilter == null) {
             mFilter = new ArrayFilter();
         }
@@ -482,31 +489,31 @@
     private class ArrayFilter extends Filter {
         @Override
         protected FilterResults performFiltering(CharSequence prefix) {
-            FilterResults results = new FilterResults();
+            final FilterResults results = new FilterResults();
 
             if (mOriginalValues == null) {
                 synchronized (mLock) {
-                    mOriginalValues = new ArrayList<T>(mObjects);
+                    mOriginalValues = new ArrayList<>(mObjects);
                 }
             }
 
             if (prefix == null || prefix.length() == 0) {
-                ArrayList<T> list;
+                final ArrayList<T> list;
                 synchronized (mLock) {
-                    list = new ArrayList<T>(mOriginalValues);
+                    list = new ArrayList<>(mOriginalValues);
                 }
                 results.values = list;
                 results.count = list.size();
             } else {
-                String prefixString = prefix.toString().toLowerCase();
+                final String prefixString = prefix.toString().toLowerCase();
 
-                ArrayList<T> values;
+                final ArrayList<T> values;
                 synchronized (mLock) {
-                    values = new ArrayList<T>(mOriginalValues);
+                    values = new ArrayList<>(mOriginalValues);
                 }
 
                 final int count = values.size();
-                final ArrayList<T> newValues = new ArrayList<T>();
+                final ArrayList<T> newValues = new ArrayList<>();
 
                 for (int i = 0; i < count; i++) {
                     final T value = values.get(i);
@@ -517,11 +524,8 @@
                         newValues.add(value);
                     } else {
                         final String[] words = valueText.split(" ");
-                        final int wordCount = words.length;
-
-                        // Start at index 0, in case valueText starts with space(s)
-                        for (int k = 0; k < wordCount; k++) {
-                            if (words[k].startsWith(prefixString)) {
+                        for (String word : words) {
+                            if (word.startsWith(prefixString)) {
                                 newValues.add(value);
                                 break;
                             }
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 6e3dbd8..e3357a7 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -75,8 +75,6 @@
  */
 @Widget
 public class DatePicker extends FrameLayout {
-    private static final String LOG_TAG = DatePicker.class.getSimpleName();
-
     private static final int MODE_SPINNER = 1;
     private static final int MODE_CALENDAR = 2;
 
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 5adac01..332e89c 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -378,9 +378,9 @@
         mCurrentDate.set(Calendar.MONTH, monthOfYear);
         mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
 
-        mDateChangedListener = callBack;
-
         onDateChanged(false, false);
+
+        mDateChangedListener = callBack;
     }
 
     @Override
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 255de79..d8a3c56 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -244,6 +244,7 @@
         setDate(year, monthOfYear, dayOfMonth);
         updateSpinners();
         updateCalendarView();
+
         mOnDateChangedListener = onDateChangedListener;
     }
 
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index fe8916b..9ac4917 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -16,17 +16,15 @@
 
 package android.widget;
 
-import java.util.ArrayList;
+import com.android.internal.R;
 
+import android.annotation.AttrRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StyleRes;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -36,8 +34,7 @@
 import android.view.ViewHierarchyEncoder;
 import android.widget.RemoteViews.RemoteView;
 
-import com.android.internal.R;
-
+import java.util.ArrayList;
 
 /**
  * FrameLayout is designed to block out an area on the screen to display
@@ -75,31 +72,29 @@
     @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingBottom = 0;
 
-    private final Rect mSelfBounds = new Rect();
-    private final Rect mOverlayBounds = new Rect();
-    
-    private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
-    
-    public FrameLayout(Context context) {
+    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+    public FrameLayout(@NonNull Context context) {
         super(context);
     }
-    
-    public FrameLayout(Context context, @Nullable AttributeSet attrs) {
+
+    public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public FrameLayout(
-            Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
         final TypedArray a = context.obtainStyledAttributes(
-                attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes);
-        
-        if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
+                attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
+
+        if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
             setMeasureAllChildren(true);
         }
 
@@ -171,10 +166,6 @@
             mPaddingBottom + mForegroundPaddingBottom;
     }
 
-
-    /**
-     * {@inheritDoc}
-     */
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int count = getChildCount();
@@ -264,17 +255,13 @@
             }
         }
     }
- 
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         layoutChildren(left, top, right, bottom, false /* no force left gravity */);
     }
 
-    void layoutChildren(int left, int top, int right, int bottom,
-                                  boolean forceLeftGravity) {
+    void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
         final int count = getChildCount();
 
         final int parentLeft = getPaddingLeftWithForeground();
@@ -378,12 +365,9 @@
         return mMeasureAllChildren;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new FrameLayout.LayoutParams(getContext(), attrs);        
+        return new FrameLayout.LayoutParams(getContext(), attrs);
     }
 
     @Override
@@ -391,9 +375,6 @@
         return false;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
         return p instanceof LayoutParams;
@@ -431,34 +412,30 @@
      * Per-child layout information for layouts that support margins.
      * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
      * for a list of all child view attributes that this class supports.
-     * 
+     *
      * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
      */
     public static class LayoutParams extends MarginLayoutParams {
         /**
          * The gravity to apply with the View to which these layout parameters
          * are associated.
+         * <p>
+         * The default value is {@code Gravity.TOP | Gravity.START}
          *
          * @see android.view.Gravity
-         * 
          * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
          */
-        public int gravity = -1;
+        public int gravity = DEFAULT_CHILD_GRAVITY;
 
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(Context c, AttributeSet attrs) {
+        public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
             super(c, attrs);
 
-            TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout);
-            gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1);
+            final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
+            gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity,
+                    DEFAULT_CHILD_GRAVITY);
             a.recycle();
         }
 
-        /**
-         * {@inheritDoc}
-         */
         public LayoutParams(int width, int height) {
             super(width, height);
         }
@@ -468,9 +445,9 @@
          * and weight.
          *
          * @param width the width, either {@link #MATCH_PARENT},
-         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         *              {@link #WRAP_CONTENT} or a fixed size in pixels
          * @param height the height, either {@link #MATCH_PARENT},
-         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         *               {@link #WRAP_CONTENT} or a fixed size in pixels
          * @param gravity the gravity
          *
          * @see android.view.Gravity
@@ -480,17 +457,11 @@
             this.gravity = gravity;
         }
 
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.LayoutParams source) {
+        public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
             super(source);
         }
 
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.MarginLayoutParams source) {
+        public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
             super(source);
         }
 
@@ -500,7 +471,7 @@
          *
          * @param source The layout params to copy from.
          */
-        public LayoutParams(LayoutParams source) {
+        public LayoutParams(@NonNull LayoutParams source) {
             super(source);
 
             this.gravity = source.gravity;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0206577..222a040 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -911,17 +911,11 @@
         }
 
         if (mDrawable != null) {
-            // It's possible for this method to be invoked from the constructor before
-            // subclass constructors have run. Drawables can and should trigger invalidations
-            // and other activity with their callback on visibility changes, which shouldn't
-            // happen before subclass constructors finish. However, we won't have set the
-            // drawable as visible until the view becomes attached. This guard below keeps
-            // multiple calls to this method from constructors from causing issues.
-            if (mDrawable.isVisible()) {
-                mDrawable.setVisible(false, false);
-            }
             mDrawable.setCallback(null);
             unscheduleDrawable(mDrawable);
+            if (isAttachedToWindow()) {
+                mDrawable.setVisible(false, false);
+            }
         }
 
         mDrawable = d;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8bd63df..6d2cea6 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.annotation.ColorInt;
+import android.app.ActivityManager.StackId;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application;
@@ -228,6 +229,11 @@
 
         public boolean onClickHandler(View view, PendingIntent pendingIntent,
                 Intent fillInIntent) {
+            return onClickHandler(view, pendingIntent, fillInIntent, StackId.INVALID_STACK_ID);
+        }
+
+        public boolean onClickHandler(View view, PendingIntent pendingIntent,
+                Intent fillInIntent, int launchStackId) {
             try {
                 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
                 Context context = view.getContext();
@@ -239,6 +245,10 @@
                             0, 0,
                             view.getMeasuredWidth(), view.getMeasuredHeight());
                 }
+
+                if (launchStackId != StackId.INVALID_STACK_ID) {
+                    opts.setLaunchStackId(launchStackId);
+                }
                 context.startIntentSender(
                         pendingIntent.getIntentSender(), fillInIntent,
                         Intent.FLAG_ACTIVITY_NEW_TASK,
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 753c069..b7ac600 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -516,9 +516,15 @@
             }
 
             // Only show the divider if we have a title.
-            final View divider;
+            View divider = null;
             if (mMessage != null || mListView != null || hasCustomPanel) {
-                divider = topPanel.findViewById(R.id.titleDivider);
+                if (!hasCustomPanel) {
+                    divider = topPanel.findViewById(R.id.titleDividerNoCustom);
+                }
+                if (divider == null) {
+                    divider = topPanel.findViewById(R.id.titleDivider);
+                }
+
             } else {
                 divider = topPanel.findViewById(R.id.titleDividerTop);
             }
@@ -526,6 +532,17 @@
             if (divider != null) {
                 divider.setVisibility(View.VISIBLE);
             }
+        } else {
+            if (contentPanel != null) {
+                final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
+                if (spacer != null) {
+                    spacer.setVisibility(View.VISIBLE);
+                }
+            }
+        }
+
+        if (mListView instanceof RecycleListView) {
+            ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
         }
 
         // Update scroll indicators as needed.
@@ -861,23 +878,34 @@
     }
 
     public static class RecycleListView extends ListView {
+        private final int mPaddingTopNoTitle;
+        private final int mPaddingBottomNoButtons;
+
         boolean mRecycleOnMeasure = true;
 
         public RecycleListView(Context context) {
-            super(context);
+            this(context, null);
         }
 
         public RecycleListView(Context context, AttributeSet attrs) {
             super(context, attrs);
+
+            final TypedArray ta = context.obtainStyledAttributes(
+                    attrs, R.styleable.RecycleListView);
+            mPaddingBottomNoButtons = ta.getDimensionPixelOffset(
+                    R.styleable.RecycleListView_paddingBottomNoButtons, -1);
+            mPaddingTopNoTitle = ta.getDimensionPixelOffset(
+                    R.styleable.RecycleListView_paddingTopNoTitle, -1);
         }
 
-        public RecycleListView(Context context, AttributeSet attrs, int defStyleAttr) {
-            super(context, attrs, defStyleAttr);
-        }
-
-        public RecycleListView(
-                Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-            super(context, attrs, defStyleAttr, defStyleRes);
+        public void setHasDecor(boolean hasTitle, boolean hasButtons) {
+            if (!hasButtons || !hasTitle) {
+                final int paddingLeft = getPaddingLeft();
+                final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle;
+                final int paddingRight = getPaddingRight();
+                final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons;
+                setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+            }
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index 36db5d7..a9d5113 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -219,7 +219,7 @@
         public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) {
             // We don't care about the various suggestion types, just "suggested" (!= 0)
             // and "all others" (== 0)
-            if (mCountryMode || (lhs.isSuggested() == rhs.isSuggested())) {
+            if (lhs.isSuggested() == rhs.isSuggested()) {
                 // They are in the same "bucket" (suggested / others), so we compare the text
                 return mCollator.compare(
                         removePrefixForCompare(lhs.getLocale(), lhs.getLabel(mCountryMode)),
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index c4e6675..7803e52 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -31,8 +31,9 @@
     private static boolean sFullyInitialized = false;
 
     public static class LocaleInfo {
-        private static final int SUGGESTION_TYPE_NONE = 0x00;
-        private static final int SUGGESTION_TYPE_SIM = 0x01;
+        private static final int SUGGESTION_TYPE_NONE = 0;
+        private static final int SUGGESTION_TYPE_SIM = 1 << 0;
+        private static final int SUGGESTION_TYPE_CFG = 1 << 1;
 
         private final Locale mLocale;
         private final Locale mParent;
@@ -273,6 +274,22 @@
         final HashSet<String> localizedLocales = new HashSet<>();
         for (String localeId : LocalePicker.getSystemAssetLocales()) {
             LocaleInfo li = new LocaleInfo(localeId);
+            final String country = li.getLocale().getCountry();
+            // All this is to figure out if we should suggest a country
+            if (!country.isEmpty()) {
+                LocaleInfo cachedLocale = null;
+                if (sLocaleCache.containsKey(li.getId())) { // the simple case, e.g. fr-CH
+                    cachedLocale = sLocaleCache.get(li.getId());
+                } else { // e.g. zh-TW localized, zh-Hant-TW in cache
+                    final String langScriptCtry = li.getLangScriptKey() + "-" + country;
+                    if (sLocaleCache.containsKey(langScriptCtry)) {
+                        cachedLocale = sLocaleCache.get(langScriptCtry);
+                    }
+                }
+                if (cachedLocale != null) {
+                    cachedLocale.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CFG;
+                }
+            }
             localizedLocales.add(li.getLangScriptKey());
         }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0e02ed6..ff680e2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -243,10 +243,6 @@
             return;
         }
 
-        // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
-        // control of the system bars.
-        getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
-
         final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
         if (rdl != null) {
             rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 98102ea..e2d29e3 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -49,6 +49,7 @@
     private static final int TYPE_HEADER_SUGGESTED = 0;
     private static final int TYPE_HEADER_ALL_OTHERS = 1;
     private static final int TYPE_LOCALE = 2;
+    private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6;
 
     private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions;
     private ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions;
@@ -171,7 +172,15 @@
     }
 
     private boolean showHeaders() {
-        if (mCountryMode) { // never show suggestions in country mode
+        // We don't want to show suggestions for locales with very few regions
+        // (e.g. Romanian, with 2 regions)
+        // So we put a (somewhat) arbitrary limit.
+        //
+        // The initial idea was to make that limit dependent on the screen height.
+        // But that would mean rotating the screen could make the suggestions disappear,
+        // as the number of countries that fits on the screen would be different in portrait
+        // and landscape mode.
+        if (mCountryMode && mLocaleOptions.size() < MIN_REGIONS_FOR_SUGGESTIONS) {
             return false;
         }
         return mSuggestionCount != 0 && mSuggestionCount != mLocaleOptions.size();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index ee73b90..bed5a2e 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -238,6 +238,14 @@
         return total;
     }
 
+    public static int[] convertToIntArray(List<Integer> list) {
+        int[] array = new int[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
     /**
      * Adds value to given array if not already present, providing set-like
      * behavior.
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index bd0e6ce..d8be9fd 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -402,7 +402,7 @@
 
         // Initialize destination fields
         mDstMessenger = dstMessenger;
-
+        linkToDeathMonitor();
         if (DBG) log("connected srcHandler to the dstMessenger X");
     }
 
@@ -844,22 +844,30 @@
         msg.arg1 = status;
         msg.obj = this;
         msg.replyTo = mDstMessenger;
+        if (!linkToDeathMonitor()) {
+            // Override status to indicate failure
+            msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
+        }
 
-        /*
-         * Link to death only when bindService isn't used.
-         */
-        if (mConnection == null) {
+        mSrcHandler.sendMessage(msg);
+    }
+
+    /**
+     * Link to death monitor for destination messenger. Returns true if successfully binded to
+     * destination messenger; false otherwise.
+     */
+    private boolean linkToDeathMonitor() {
+        // Link to death only when bindService isn't used and not already linked.
+        if (mConnection == null && mDeathMonitor == null) {
             mDeathMonitor = new DeathMonitor();
             try {
                 mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
             } catch (RemoteException e) {
                 mDeathMonitor = null;
-                // Override status to indicate failure
-                msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
+                return false;
             }
         }
-
-        mSrcHandler.sendMessage(msg);
+        return true;
     }
 
     /**
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 7c85246..1ead5b3 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -191,6 +191,21 @@
      * Ensures that that the argument numeric value is non-negative.
      *
      * @param value a numeric long value
+     * @return the validated numeric value
+     * @throws IllegalArgumentException if {@code value} was negative
+     */
+    public static long checkArgumentNonnegative(final long value) {
+        if (value < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        return value;
+    }
+
+    /**
+     * Ensures that that the argument numeric value is non-negative.
+     *
+     * @param value a numeric long value
      * @param errorMessage the exception message to use if the check fails
      * @return the validated numeric value
      * @throws IllegalArgumentException if {@code value} was negative
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9d14478..3d892af 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1099,7 +1099,8 @@
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
+                || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
         return passwordEnabled && savedPasswordExists(userId);
     }
 
diff --git a/services/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
similarity index 94%
rename from services/core/java/com/android/server/SystemConfig.java
rename to core/java/com/android/server/SystemConfig.java
index 30e0ceb..07912cd 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -19,6 +19,7 @@
 import static com.android.internal.util.ArrayUtils.appendInt;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import android.os.Environment;
@@ -115,6 +116,9 @@
     // These are the packages that should not run under system user
     final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
 
+    // These are the components that are enabled by default as VR mode listener services.
+    final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
+
     public static SystemConfig getInstance() {
         synchronized (SystemConfig.class) {
             if (sInstance == null) {
@@ -168,6 +172,10 @@
         return mSystemUserBlacklistedApps;
     }
 
+    public ArraySet<ComponentName> getDefaultVrComponents() {
+        return mDefaultVrComponents;
+    }
+
     SystemConfig() {
         // Read configuration from system
         readPermissions(Environment.buildPath(
@@ -431,6 +439,19 @@
                         mSystemUserBlacklistedApps.add(pkgname);
                     }
                     XmlUtils.skipCurrentTag(parser);
+                } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
+                    String pkgname = parser.getAttributeValue(null, "package");
+                    String clsname = parser.getAttributeValue(null, "class");
+                    if (pkgname == null) {
+                        Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
+                                + " at " + parser.getPositionDescription());
+                    } else if (clsname == null) {
+                        Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
+                                + " at " + parser.getPositionDescription());
+                    } else {
+                        mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
+                    }
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     XmlUtils.skipCurrentTag(parser);
                     continue;
diff --git a/core/java/com/android/server/backup/ShortcutBackupHelper.java b/core/java/com/android/server/backup/ShortcutBackupHelper.java
new file mode 100644
index 0000000..0b3f2ae
--- /dev/null
+++ b/core/java/com/android/server/backup/ShortcutBackupHelper.java
@@ -0,0 +1,70 @@
+/*
+ * 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.backup;
+
+import android.app.backup.BlobBackupHelper;
+import android.content.Context;
+import android.content.pm.IShortcutService;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+public class ShortcutBackupHelper extends BlobBackupHelper {
+    private static final String TAG = "ShortcutBackupAgent";
+    private static final int BLOB_VERSION = 1;
+
+    private static final String KEY_USER_FILE = "shortcutuser.xml";
+
+    public ShortcutBackupHelper() {
+        super(BLOB_VERSION, KEY_USER_FILE);
+    }
+
+    private IShortcutService getShortcutService() {
+        return IShortcutService.Stub.asInterface(
+                ServiceManager.getService(Context.SHORTCUT_SERVICE));
+    }
+
+    @Override
+    protected byte[] getBackupPayload(String key) {
+        switch (key) {
+            case KEY_USER_FILE:
+                try {
+                    return getShortcutService().getBackupPayload(UserHandle.USER_SYSTEM);
+                } catch (Exception e) {
+                    Slog.wtf(TAG, "Backup failed", e);
+                }
+                break;
+            default:
+                Slog.w(TAG, "Unknown key: " + key);
+        }
+        return null;
+    }
+
+    @Override
+    protected void applyRestoredPayload(String key, byte[] payload) {
+        switch (key) {
+            case KEY_USER_FILE:
+                try {
+                    getShortcutService().applyRestore(payload, UserHandle.USER_SYSTEM);
+                } catch (Exception e) {
+                    Slog.wtf(TAG, "Restore failed", e);
+                }
+                break;
+            default:
+                Slog.w(TAG, "Unknown key: " + key);
+        }
+    }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 181ed51..2d12fcd 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -17,9 +17,9 @@
 package com.android.server.backup;
 
 import android.app.IWallpaperManager;
+import android.app.backup.BackupAgentHelper;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupAgentHelper;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
 import android.app.backup.WallpaperBackupHelper;
@@ -48,6 +48,7 @@
     private static final String NOTIFICATION_HELPER = "notifications";
     private static final String PERMISSION_HELPER = "permissions";
     private static final String USAGE_STATS_HELPER = "usage_stats";
+    private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
 
     // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
     // are also used in the full-backup file format, so must not change unless steps are
@@ -100,6 +101,7 @@
         addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
         addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
         addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+        addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
         super.onBackup(oldState, data, newState);
     }
 
@@ -138,6 +140,7 @@
         addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
         addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
         addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+        addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
 
         try {
             super.onRestore(data, appVersionCode, newState);
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 27b9830..22a81d4 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -251,13 +251,6 @@
 
 void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes,
         SkColorTable* ctable) {
-    {
-        android::AutoMutex _lock(mLock);
-        if (mPinnedRefCount) {
-            ALOGW("Called reconfigure on a bitmap that is in use! This may"
-                    " cause graphical corruption!");
-        }
-    }
     mPixelRef->reconfigure(info, rowBytes, ctable);
 }
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 27d8fed..1844a98 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -823,7 +823,7 @@
     static jfloat doRunAdvance(const Paint* paint, Typeface* typeface, const jchar buf[],
             jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
         int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-        if (offset == count) {
+        if (offset == start + count) {
             return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
                     bufSize, nullptr);
         }
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 8724729..42dc983 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -16,23 +16,39 @@
 
 #include "context_hub.h"
 
+#define LOG_NDEBUG 0
+#define LOG_TAG "ContextHubService"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <map>
+#include <queue>
 #include <string.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 
-#include <jni.h>
+#include <cutils/log.h>
+
 #include "JNIHelp.h"
 #include "core_jni_helpers.h"
-#include "stdint.h"
-#include "stdlib.h"
+
+//static constexpr int OS_APP_ID=-1;
+
+static constexpr int MIN_APP_ID=1;
+static constexpr int MAX_APP_ID=128;
+
+static constexpr size_t MSG_HEADER_SIZE=4;
+static constexpr int HEADER_FIELD_MSG_TYPE=0;
+//static constexpr int HEADER_FIELD_MSG_VERSION=1;
+static constexpr int HEADER_FIELD_HUB_HANDLE=2;
+static constexpr int HEADER_FIELD_APP_INSTANCE=3;
 
 
 namespace android {
 
 namespace {
 
-// TODO: We should share this array_length function widely around Android
-//     code.
 /*
  * Finds the length of a statically-sized array using template trickery that
  * also prevents it from being applied to the wrong type.
@@ -64,35 +80,207 @@
     jmethodID contextHubInfoSetPeakPowerDrawMw;
     jmethodID contextHubInfoSetSupportedSensors;
     jmethodID contextHubInfoSetMemoryRegions;
+    jmethodID contextHubInfoSetMaxPacketLenBytes;
 
     jmethodID contextHubServiceMsgReceiptCallback;
+    jmethodID contextHubServiceAddAppInstance;
 };
 
 struct context_hub_info_s {
-    int cookie;
+    uint32_t *cookies;
     int numHubs;
     const struct context_hub_t *hubs;
     struct context_hub_module_t *contextHubModule;
 };
 
+struct app_instance_info_s {
+    uint32_t hubHandle; // Id of the hub this app is on
+    int instanceId; // systemwide unique instance id - assigned
+    struct hub_app_info appInfo; // returned from the HAL
+    uint64_t truncName; // Possibly truncated name - logging
+};
+
 struct contextHubServiceDb_s {
     int initialized;
     context_hub_info_s hubInfo;
     jniInfo_s jniInfo;
+    std::queue<int> freeIds;
+    std::map<int, app_instance_info_s *> appInstances;
 };
 
 }  // unnamed namespace
 
 static contextHubServiceDb_s db;
 
-int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg,
+int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg,
                          void *cookie);
 
+const context_hub_t *get_hub_info(int hubHandle) {
+    if (hubHandle >= 0 && hubHandle < db.hubInfo.numHubs) {
+        return &db.hubInfo.hubs[hubHandle];
+    }
+    return nullptr;
+}
+
+static int send_msg_to_hub(const hub_message_t *msg, int hubHandle) {
+    const context_hub_t *info = get_hub_info(hubHandle);
+
+    if (info) {
+        return db.hubInfo.contextHubModule->send_message(info->hub_id, msg);
+    } else {
+        ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
+        return -1;
+    }
+}
+
+static int set_os_app_as_destination(hub_message_t *msg, int hubHandle) {
+    const context_hub_t *info = get_hub_info(hubHandle);
+
+    if (info) {
+        msg->app = info->os_app_name;
+        return 0;
+    } else {
+        ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
+        return -1;
+    }
+}
+
+static int get_hub_id_for_app_instance(int id) {
+    if (db.appInstances.find(id) == db.appInstances.end()) {
+        ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
+        return -1;
+    }
+
+    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()) {
+        ALOGD("%s: Cannod find app for app instance %d", __FUNCTION__, id);
+        return -1;
+    }
+
+    msg->app = db.appInstances[id]->appInfo.name;
+    return 0;
+}
+
+static void send_query_for_apps() {
+    hub_message_t msg;
+
+    msg.message_type = CONTEXT_HUB_QUERY_APPS;
+    msg.message_len  = 0;
+
+    for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
+        ALOGD("Sending query for apps to hub %d", i);
+        set_os_app_as_destination(&msg, i);
+        if (send_msg_to_hub(&msg, i) != 0) {
+          ALOGW("Could not query hub %i for apps", i);
+        }
+    }
+}
+
+static int return_id(int id) {
+    // Note : This method is not thread safe.
+    // id returned is guarenteed to be in use
+    db.freeIds.push(id);
+    return 0;
+}
+
+static int generate_id(void) {
+    // Note : This method is not thread safe.
+    int retVal = -1;
+
+    if (!db.freeIds.empty()) {
+        retVal = db.freeIds.front();
+        db.freeIds.pop();
+    }
+
+    return retVal;
+}
+
+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));
+
+    int appInstanceHandle = generate_id();
+
+    if (appInstanceHandle < 0 || !appName || !entry || !name) {
+        ALOGE("Cannot find resources to add app instance %d, %p, %p",
+            appInstanceHandle, appName, entry);
+
+        free(appName);
+        free(entry);
+        free(name);
+
+        if (appInstanceHandle >= 0) {
+            return_id(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;
+    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);
+
+    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);
+
+    return appInstanceHandle;
+}
+
+int delete_app_instance(int id) {
+    if (db.appInstances.find(id) == db.appInstances.end()) {
+        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);
+    }
+
+    return 0;
+}
+
+
 static void initContextHubService() {
     int err = 0;
-    db.hubInfo.hubs = NULL;
+    db.hubInfo.hubs = nullptr;
     db.hubInfo.numHubs = 0;
-    db.hubInfo.cookie = 0;
     int i;
 
     err = hw_get_module(CONTEXT_HUB_MODULE_ID,
@@ -103,26 +291,45 @@
             strerror(-err));
     }
 
-    if (db.hubInfo.contextHubModule) {
-      ALOGD("Fetching hub info");
-      db.hubInfo.numHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
-                                                                 &db.hubInfo.hubs);
+    // Prep for storing app info
+    for(i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
+        db.freeIds.push(i);
+    }
 
-      if (db.hubInfo.numHubs > 0) {
-        for (i = 0; i < db.hubInfo.numHubs; i++) {
-          // TODO : Event though one cookie is OK for now, lets change
-          // this to be one per hub
-          db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
-                                                          context_hub_callback,
-                                                          &db.hubInfo.cookie);
+    if (db.hubInfo.contextHubModule) {
+        int retNumHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
+                                                                 &db.hubInfo.hubs);
+        ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
+        db.hubInfo.numHubs = retNumHubs;
+
+        if (db.hubInfo.numHubs > 0) {
+            db.hubInfo.numHubs = retNumHubs;
+            db.hubInfo.cookies = (uint32_t *)malloc(sizeof(uint32_t) * db.hubInfo.numHubs);
+
+            if (!db.hubInfo.cookies) {
+                ALOGW("Ran out of memory allocating cookies, bailing");
+                return;
+            }
+
+            for (i = 0; i < db.hubInfo.numHubs; i++) {
+                db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id;
+                if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
+                                                                    context_hub_callback,
+                                                                    &db.hubInfo.cookies[i]) == 0) {
+                }
+            }
         }
-      }
+
+        send_query_for_apps();
+    } else {
+        ALOGW("No Context Hub Module present");
     }
 }
 
 static int onMessageReceipt(int *header, int headerLen, char *msg, int msgLen) {
     JNIEnv *env;
-    if ((db.jniInfo.vm)->AttachCurrentThread(&env, NULL) != JNI_OK) {
+
+    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
       return -1;
     }
 
@@ -132,28 +339,131 @@
     env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
     env->SetIntArrayRegion(jheader, 0, headerLen, (jint *)header);
 
-
-    return env->CallIntMethod(db.jniInfo.jContextHubService,
+    return (env->CallIntMethod(db.jniInfo.jContextHubService,
                           db.jniInfo.contextHubServiceMsgReceiptCallback,
-                          jheader, jmsg);
+                          jheader, jmsg) != 0);
 }
 
-int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg,
+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
+
+    if (!info) {
+        return -1;
+    }
+
+    memcpy(info, msg, msgLen);
+    for (i = 0; i < numApps; i++) {
+        add_app_instance(info, hubHandle, env);
+        info++;
+    }
+
+    free(info);
+
+    return 0;
+}
+
+
+int handle_os_message(uint32_t msgType, uint32_t hubHandle,
+                      char *msg, int msgLen) {
+    int retVal;
+
+    switch(msgType) {
+        case CONTEXT_HUB_APPS_ENABLE:
+            retVal = 0;
+            break;
+
+        case CONTEXT_HUB_APPS_DISABLE:
+            retVal = 0;
+            break;
+
+        case CONTEXT_HUB_LOAD_APP:
+            retVal = 0;
+            break;
+
+        case CONTEXT_HUB_UNLOAD_APP:
+            retVal = 0;
+            break;
+
+        case CONTEXT_HUB_QUERY_APPS:
+            retVal = handle_query_apps_response(msg, msgLen, hubHandle);
+            break;
+
+        case CONTEXT_HUB_QUERY_MEMORY:
+            retVal = 0;
+            break;
+
+        case CONTEXT_HUB_LOAD_OS:
+            retVal = 0;
+            break;
+
+        default:
+            retVal = -1;
+            break;
+
+    }
+
+    return retVal;
+}
+
+static bool sanity_check_cookie(void *cookie, uint32_t hub_id) {
+    int *ptr = (int *)cookie;
+
+    if (!ptr || *ptr >= db.hubInfo.numHubs) {
+        return false;
+    }
+
+    if (db.hubInfo.hubs[*ptr].hub_id != hub_id) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+int context_hub_callback(uint32_t hubId,
+                         const struct hub_message_t *msg,
                          void *cookie) {
-  int msgHeader[4];
+    int msgHeader[MSG_HEADER_SIZE];
 
-  msgHeader[0] = msg->message_type;
-  msgHeader[1] = 0; // TODO : HAL does not have a version field
-  msgHeader[2] = hub_id;
+    if (!msg) {
+        return -1;
+    }
 
-  onMessageReceipt(msgHeader, sizeof(msgHeader), (char *)msg->message, msg->message_len); // TODO : Populate this
-  return 0;
+    msgHeader[HEADER_FIELD_MSG_TYPE] = msg->message_type;
+
+    if (!sanity_check_cookie(cookie, hubId)) {
+        ALOGW("Incorrect cookie %" PRId32 " for cookie %p! Bailing",
+              hubId, cookie);
+
+        return -1;
+    }
+
+    msgHeader[HEADER_FIELD_HUB_HANDLE] = *(uint32_t*)cookie;
+
+    if (msgHeader[HEADER_FIELD_MSG_TYPE] < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE &&
+        msgHeader[HEADER_FIELD_MSG_TYPE] != 0 ) {
+        handle_os_message(msgHeader[HEADER_FIELD_MSG_TYPE],
+                          msgHeader[HEADER_FIELD_HUB_HANDLE],
+                          (char *)msg->message,
+                          msg->message_len);
+    } else {
+        onMessageReceipt(msgHeader, sizeof(msgHeader),
+                         (char *)msg->message, msg->message_len);
+    }
+
+    return 0;
 }
 
 static int init_jni(JNIEnv *env, jobject instance) {
 
     if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
-      return -1;
+        return -1;
     }
 
     db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
@@ -167,7 +477,6 @@
     db.jniInfo.memoryRegionsClass =
             env->FindClass("android/hardware/location/MemoryRegion");
 
-    //TODO :: Add error checking
     db.jniInfo.contextHubInfoCtor =
             env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
     db.jniInfo.contextHubInfoSetId =
@@ -209,6 +518,9 @@
     db.jniInfo.contextHubInfoSetMemoryRegions =
             env->GetMethodID(db.jniInfo.contextHubInfoClass,
                                 "setMemoryRegions", "([Landroid/hardware/location/MemoryRegion;)V");
+    db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
+             env->GetMethodID(db.jniInfo.contextHubInfoClass,
+                                "setMaxPacketLenBytes", "(I)V");
 
 
     db.jniInfo.contextHubServiceMsgReceiptCallback =
@@ -218,6 +530,11 @@
             env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName",
             "(Ljava/lang/String;)V");
 
+    db.jniInfo.contextHubServiceAddAppInstance =
+                 env->GetMethodID(db.jniInfo.contextHubServiceClass,
+                                    "addAppInstance", "(IIJI)I");
+
+
 
     return 0;
 }
@@ -245,20 +562,29 @@
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub->platform_version);
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub->toolchain_version);
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub->peak_mips);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, hub->stopped_power_draw_mw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, hub->sleep_power_draw_mw);
-    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, hub->peak_power_draw_mw);
+    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
+                        hub->stopped_power_draw_mw);
+    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
+                        hub->sleep_power_draw_mw);
+    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
+                        hub->peak_power_draw_mw);
+    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
+                        hub->max_supported_msg_len);
+
 
     // TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors);
-    // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, hub->connected_sensors);
+    // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors,
+    //                               hub->connected_sensors);
     jintBuf = env->NewIntArray(array_length(dummyConnectedSensors));
     env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors);
+    env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
 
     // We are not getting the memory regions from the CH Hal - change this when it is available
-    jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, NULL);
+    jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
     // Note the zero size above. We do not need to set any elements
     env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
 
+
     return jHub;
 }
 
@@ -267,18 +593,18 @@
     jobject hub;
     jobjectArray retArray;
 
-    initContextHubService();
-
     if (init_jni(env, instance) < 0) {
-        return NULL;
+        return nullptr;
     }
 
-    // Note : The service is clamping the number of hubs to 1
-    db.hubInfo.numHubs = 1;
-
     initContextHubService();
 
-    retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, NULL);
+    if (db.hubInfo.numHubs > 1) {
+      ALOGW("Clamping the number of hubs to 1");
+      db.hubInfo.numHubs = 1;
+    }
+
+    retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
 
     for(int i = 0; i < db.hubInfo.numHubs; i++) {
         hub = constructJContextHubInfo(env, &db.hubInfo.hubs[i]);
@@ -291,28 +617,27 @@
 static jint nativeSendMessage(JNIEnv *env, jobject instance, jintArray header_,
                               jbyteArray data_) {
     hub_message_t msg;
-    hub_app_name_t dest;
-    uint8_t os_name[8];
-
-    memset(os_name, 0, sizeof(os_name));
+    jint retVal = -1; // Default to failure
 
     jint *header = env->GetIntArrayElements(header_, 0);
-    //int numHeaderElements = env->GetArrayLength(header_);
+    unsigned int numHeaderElements = env->GetArrayLength(header_);
     jbyte *data = env->GetByteArrayElements(data_, 0);
     int dataBufferLength = env->GetArrayLength(data_);
 
-    /* Assume an int - thats all we understand */
-    dest.app_name_len = array_length(os_name); // TODO : Check this
-    //dest.app_name = &header[1];
-    dest.app_name = os_name;
-
-    msg.app = &dest;
-
-    msg.message_type = header[3];
-    msg.message_len = dataBufferLength;
-    msg.message = data;
-
-    jint retVal = db.hubInfo.contextHubModule->send_message(header[0], &msg);
+    if (numHeaderElements >= MSG_HEADER_SIZE) {
+        if (set_dest_app(&msg, header[HEADER_FIELD_APP_INSTANCE]) == 0) {
+          msg.message_type = header[HEADER_FIELD_MSG_TYPE];
+          msg.message_len = dataBufferLength;
+          msg.message = data;
+          retVal = db.hubInfo.contextHubModule->send_message(
+                  get_hub_id_for_app_instance(header[HEADER_FIELD_APP_INSTANCE]),
+                  &msg);
+        } else {
+          ALOGD("Could not find app instance %d", header[HEADER_FIELD_APP_INSTANCE]);
+        }
+    } else {
+        ALOGD("Malformed header len");
+    }
 
     env->ReleaseIntArrayElements(header_, header, 0);
     env->ReleaseByteArrayElements(data_, data, 0);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b0fcc28..30a5993 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -845,14 +845,14 @@
     -->
     <permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE"
         android:permissionGroup="android.permission-group.PHONE"
-        android:protectionLevel="dangerous"/>
+        android:protectionLevel="signatureOrSystem"/>
 
     <!-- @hide Allows an application to Access UCE-OPTIONS.
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE"
         android:permissionGroup="android.permission-group.PHONE"
-        android:protectionLevel="dangerous"/>
+        android:protectionLevel="signatureOrSystem"/>
 
 
 
@@ -2004,6 +2004,11 @@
     <permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows but does not guarantee access to user passwords at the conclusion of add
+         account -->
+    <permission android:name="android.permission.GET_PASSWORD_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows applications to RW to diagnostic resources.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.DIAGNOSTIC"
@@ -2984,6 +2989,11 @@
     <permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to whitelist tasks during lock task mode
+         @hide <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
+        android:protectionLevel="signature|setup" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -2993,7 +3003,7 @@
                  android:killAfterRestore="false"
                  android:icon="@drawable/ic_launcher_android"
                  android:supportsRtl="true"
-                 android:theme="@style/Theme.Material.DayNight.DarkActionBar"
+                 android:theme="@style/Theme.Material.Light.DarkActionBar"
                  android:defaultToDeviceProtectedStorage="true"
                  android:directBootAware="true">
         <activity android:name="com.android.internal.app.ChooserActivity"
@@ -3029,7 +3039,7 @@
                 android:label="@string/managed_profile_label">
         </activity-alias>
         <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
-                android:theme="@style/Theme.Material.DayNight.Dialog"
+                android:theme="@style/Theme.Material.Light.Dialog"
                 android:label="@string/heavy_weight_switcher_title"
                 android:finishOnCloseSystemDialogs="true"
                 android:excludeFromRecents="true"
@@ -3062,7 +3072,7 @@
         <activity android:name="android.accounts.ChooseAccountActivity"
                 android:excludeFromRecents="true"
                 android:exported="true"
-                android:theme="@style/Theme.Material.DayNight.Dialog"
+                android:theme="@style/Theme.Material.Light.Dialog"
                 android:label="@string/choose_account_label"
                 android:process=":ui">
         </activity>
@@ -3070,14 +3080,14 @@
         <activity android:name="android.accounts.ChooseTypeAndAccountActivity"
                 android:excludeFromRecents="true"
                 android:exported="true"
-                android:theme="@style/Theme.Material.DayNight.Dialog"
+                android:theme="@style/Theme.Material.Light.Dialog"
                 android:label="@string/choose_account_label"
                 android:process=":ui">
         </activity>
 
         <activity android:name="android.accounts.ChooseAccountTypeActivity"
                 android:excludeFromRecents="true"
-                android:theme="@style/Theme.Material.DayNight.Dialog"
+                android:theme="@style/Theme.Material.Light.Dialog"
                 android:label="@string/choose_account_label"
                 android:process=":ui">
         </activity>
@@ -3085,19 +3095,19 @@
         <activity android:name="android.accounts.CantAddAccountActivity"
                 android:excludeFromRecents="true"
                 android:exported="true"
-                android:theme="@style/Theme.Material.DayNight.Dialog.NoActionBar"
+                android:theme="@style/Theme.Material.Light.Dialog.NoActionBar"
                 android:process=":ui">
         </activity>
 
         <activity android:name="android.accounts.GrantCredentialsPermissionActivity"
                 android:excludeFromRecents="true"
                 android:exported="true"
-                android:theme="@style/Theme.Material.DayNight.DialogWhenLarge"
+                android:theme="@style/Theme.Material.Light.DialogWhenLarge"
                 android:process=":ui">
         </activity>
 
         <activity android:name="android.content.SyncActivityTooManyDeletes"
-               android:theme="@style/Theme.Material.DayNight.Dialog"
+               android:theme="@style/Theme.Material.Light.Dialog"
                android:label="@string/sync_too_many_deletes"
                android:process=":ui">
         </activity>
@@ -3117,7 +3127,7 @@
         </activity>
 
         <activity android:name="com.android.internal.app.NetInitiatedActivity"
-                android:theme="@style/Theme.Material.DayNight.Dialog.Alert"
+                android:theme="@style/Theme.Material.Light.Dialog.Alert"
                 android:excludeFromRecents="true"
                 android:process=":ui">
         </activity>
@@ -3138,7 +3148,7 @@
         <activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
                 android:excludeFromRecents="true"
                 android:process=":ui"
-                android:theme="@style/Theme.Material.DayNight.Dialog.Alert">
+                android:theme="@style/Theme.Material.Light.Dialog.Alert">
             <intent-filter android:priority="1000">
                 <action android:name="android.os.action.CREATE_USER" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -3146,7 +3156,7 @@
         </activity>
 
         <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
-                android:theme="@style/Theme.Material.DayNight.Dialog.Alert"
+                android:theme="@style/Theme.Material.Light.Dialog.Alert"
                 android:excludeFromRecents="true"
                 android:process=":ui">
         </activity>
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index 9f50937..6d33de6 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -34,7 +34,6 @@
             android:id="@+id/scrollView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingTop="@dimen/dialog_padding_top_material"
             android:clipToPadding="false">
 
             <LinearLayout
@@ -42,6 +41,12 @@
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
 
+                <Space
+                    android:id="@+id/textSpacerNoTitle"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/dialog_padding_top_material" />
+
                 <TextView
                     android:id="@+id/message"
                     android:layout_width="match_parent"
@@ -53,7 +58,7 @@
                 <Space
                     android:id="@+id/textSpacerNoButtons"
                     android:visibility="gone"
-                    android:layout_width="0dp"
+                    android:layout_width="match_parent"
                     android:layout_height="@dimen/dialog_padding_top_material" />
             </LinearLayout>
         </ScrollView>
diff --git a/core/res/res/layout/alert_dialog_title_material.xml b/core/res/res/layout/alert_dialog_title_material.xml
index 738a637..eef9585 100644
--- a/core/res/res/layout/alert_dialog_title_material.xml
+++ b/core/res/res/layout/alert_dialog_title_material.xml
@@ -21,6 +21,8 @@
               android:layout_height="wrap_content"
               android:orientation="vertical">
 
+    <!-- If the client uses a customTitle, it will be added here. -->
+
     <LinearLayout
         android:id="@+id/title_template"
         android:layout_width="match_parent"
@@ -49,5 +51,9 @@
             style="?attr/windowTitleStyle" />
     </LinearLayout>
 
-    <!-- If the client uses a customTitle, it will be added here. -->
+    <Space
+        android:id="@+id/titleDividerNoCustom"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/dialog_title_divider_material" />
 </LinearLayout>
diff --git a/core/res/res/layout/app_anr_dialog.xml b/core/res/res/layout/app_anr_dialog.xml
index 8bef116..5ad0f4c 100644
--- a/core/res/res/layout/app_anr_dialog.xml
+++ b/core/res/res/layout/app_anr_dialog.xml
@@ -19,7 +19,7 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingTop="@dimen/aerr_padding_list_top"
-        android:paddingBottom="@dimen/dialog_list_padding_vertical_material">
+        android:paddingBottom="@dimen/aerr_padding_list_bottom">
 
     <Button
             android:id="@+id/aerr_close"
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
index b9531f4..7147ea2 100644
--- a/core/res/res/layout/app_error_dialog.xml
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -22,7 +22,7 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingTop="@dimen/aerr_padding_list_top"
-        android:paddingBottom="@dimen/dialog_list_padding_vertical_material">
+        android:paddingBottom="@dimen/aerr_padding_list_bottom">
 
 
     <Button
diff --git a/core/res/res/layout/select_dialog_material.xml b/core/res/res/layout/select_dialog_material.xml
index 19ad407..9a068f9a 100644
--- a/core/res/res/layout/select_dialog_material.xml
+++ b/core/res/res/layout/select_dialog_material.xml
@@ -30,6 +30,6 @@
     android:scrollbars="vertical"
     android:overScrollMode="ifContentScrolls"
     android:textAlignment="viewStart"
-    android:paddingTop="@dimen/dialog_list_padding_vertical_material"
-    android:paddingBottom="@dimen/dialog_list_padding_vertical_material"
-    android:clipToPadding="false" />
+    android:clipToPadding="false"
+    android:paddingBottomNoButtons="@dimen/dialog_list_padding_bottom_no_buttons"
+    android:paddingTopNoTitle="@dimen/dialog_list_padding_top_no_title" />
diff --git a/core/res/res/values-night/themes_material_daynight.xml b/core/res/res/values-night/themes_material_daynight.xml
deleted file mode 100644
index b344582..0000000
--- a/core/res/res/values-night/themes_material_daynight.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!--
-===============================================================
-                        PLEASE READ
-===============================================================
-
-The Material themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see themes_device_defaults.xml.
-
-===============================================================
-                        PLEASE READ
-===============================================================
- -->
-<resources>
-
-    <!-- Material theme (day/night version) for activities. -->
-    <style name="Theme.Material.DayNight" parent="Theme.Material" />
-
-    <!-- Variant of Material.DayNight that has a solid (opaque) action bar
-         with an inverse color profile. The dark action bar sharply stands out against
-         the light content (when applicable).  -->
-    <style name="Theme.Material.DayNight.DarkActionBar" parent="Theme.Material" />
-
-    <!-- Variant of Material.DayNight with no action bar.  -->
-    <style name="Theme.Material.DayNight.NoActionBar" parent="Theme.Material.NoActionBar" />
-
-    <!-- Variant of Material.DayNight that has no title bar and fills
-         the entire screen. This theme
-         sets {@link android.R.attr#windowFullscreen} to true.  -->
-    <style name="Theme.Material.DayNight.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen" />
-
-    <!-- Variant of Material.DayNight that has no title bar and fills
-         the entire screen and extends into the display overscan region. This theme
-         sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
-         to true. -->
-    <style name="Theme.Material.DayNight.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan" />
-
-    <!-- Variant of Material.DayNight that has no title bar and translucent
-         system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
-         {@link android.R.attr#windowTranslucentNavigation} to true. -->
-    <style name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor" />
-
-    <!-- Default Material.DayNight theme for panel windows. This removes all extraneous
-         window decorations, so you basically have an empty rectangle in which
-         to place your content. It makes the window floating, with a transparent
-         background, and turns off dimming behind the window. -->
-    <style name="Theme.Material.DayNight.Panel" parent="Theme.Material.Panel" />
-
-    <!-- Material theme (day/night version) for dialog windows and activities,
-         which is used by the {@link android.app.Dialog} class. This changes
-         the window to be floating (not fill the entire screen), and puts a
-         frame around its contents. You can set this theme on an activity if
-         you would like to make an activity that looks like a Dialog. -->
-    <style name="Theme.Material.DayNight.Dialog" parent="Theme.Material.DayNight.BaseDialog" />
-    <style name="Theme.Material.DayNight.BaseDialog" parent="Theme.Material.BaseDialog" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog that has a nice minimum width for
-         a regular dialog. -->
-    <style name="Theme.Material.DayNight.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog that does not include a title bar. -->
-    <style name="Theme.Material.DayNight.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a nice minimum width for
-         a regular dialog. -->
-    <style name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog that has a fixed size. -->
-    <style name="Theme.Material.DayNight.Dialog.FixedSize" parent="Theme.Material.Dialog.FixedSize" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a fixed size. -->
-    <style name="Theme.Material.DayNight.Dialog.NoActionBar.FixedSize" parent="Theme.Material.Dialog.NoActionBar.FixedSize" />
-
-    <!-- Theme for a window that will be displayed either full-screen on
-         smaller screens (small, normal) or as a dialog on larger screens
-         (large, xlarge). -->
-    <style name="Theme.Material.DayNight.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge" />
-
-    <!-- Theme for a window with a dark action bar that will be displayed
-         either full-screen on smaller screens (small, normal) or as a dialog
-         on larger screens (large, xlarge). -->
-    <style name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" parent="Theme.Material.DialogWhenLarge" />
-
-    <!-- Theme for a window without an action bar that will be displayed either full-screen
-         on smaller screens (small, normal) or as a dialog on larger screens
-         (large, xlarge). -->
-    <style name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar" />
-
-    <!-- Theme for a presentation window on a secondary display. -->
-    <style name="Theme.Material.DayNight.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation" />
-
-    <!-- Material user theme for alert dialog windows, which is used by the
-         {@link android.app.AlertDialog} class. -->
-    <style name="Theme.Material.DayNight.Dialog.Alert" parent="Theme.Material.DayNight.Dialog.BaseAlert" />
-    <style name="Theme.Material.DayNight.Dialog.BaseAlert" parent="Theme.Material.Dialog.BaseAlert" />
-
-    <style name="Theme.Material.DayNight.SearchBar" parent="Theme.Material.SearchBar" />
-    <style name="Theme.Material.DayNight.CompactMenu" parent="Theme.Material.CompactMenu" />
-
-</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4429001..0ed1f13 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3528,6 +3528,13 @@
              This setting implies fastScrollEnabled. -->
         <attr name="fastScrollAlwaysVisible" format="boolean" />
     </declare-styleable>
+    <!-- @hide -->
+    <declare-styleable name="RecycleListView">
+        <!-- Bottom padding to use when no buttons are present. -->
+        <attr name="paddingBottomNoButtons" format="dimension" />
+        <!-- Top padding to use when no title is present. -->
+        <attr name="paddingTopNoTitle" format="dimension" />
+    </declare-styleable>
     <declare-styleable name="AbsSpinner">
         <!-- Reference to an array resource that will populate the Spinner.  For static content,
              this is simpler than populating the Spinner programmatically. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d1c0895..5b4364d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -225,6 +225,9 @@
             granted any application pre-installed on the system image (not just privileged
             apps). -->
         <flag name="preinstalled" value="0x400" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the setup wizard app -->
+        <flag name="setup" value="0x800" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 892b3d5..dbe3dca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -578,6 +578,9 @@
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
 
+    <!-- Fast brightness animation ramp rate -->
+    <integer translatable="false" name="config_brightness_ramp_rate_fast">200</integer>
+
     <!-- Don't name config resources like this.  It should look like config_annoyDianne -->
     <bool name="config_annoy_dianne">true</bool>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 71d9a1f..a21f276 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -450,6 +450,7 @@
     <item type="dimen" format="integer" name="time_picker_column_end_material">1</item>
 
     <item type="dimen" name="aerr_padding_list_top">15dp</item>
+    <item type="dimen" name="aerr_padding_list_bottom">8dp</item>
 
     <item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item>
 
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index ad2b335d..00e48a0 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -117,13 +117,13 @@
 
     <dimen name="dialog_padding_material">24dp</dimen>
     <dimen name="dialog_padding_top_material">18dp</dimen>
+    <dimen name="dialog_title_divider_material">8dp</dimen>
+    <dimen name="dialog_list_padding_top_no_title">8dp</dimen>
+    <dimen name="dialog_list_padding_bottom_no_buttons">8dp</dimen>
 
     <!-- Dialog padding minus control padding, used to fix alignment. -->
     <dimen name="select_dialog_padding_start_material">20dp</dimen>
 
-    <!-- Padding above and below selection dialog lists. -->
-    <dimen name="dialog_list_padding_vertical_material">8dp</dimen>
-
     <dimen name="seekbar_track_background_height_material">2dp</dimen>
     <dimen name="seekbar_track_progress_height_material">2dp</dimen>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b0726d2..7d19e99 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1766,6 +1766,7 @@
   <java-symbol type="integer" name="config_shutdownBatteryTemperature" />
   <java-symbol type="integer" name="config_undockedHdmiRotation" />
   <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
+  <java-symbol type="integer" name="config_brightness_ramp_rate_fast" />
   <java-symbol type="layout" name="am_compat_mode_dialog" />
   <java-symbol type="layout" name="launch_warning" />
   <java-symbol type="layout" name="safe_mode" />
@@ -2451,24 +2452,6 @@
   <java-symbol type="id" name="aerr_close" />
   <java-symbol type="id" name="aerr_mute" />
 
-  <!-- Framework-private Material.DayNight styles. -->
-  <java-symbol type="style" name="Theme.Material.DayNight" />
-  <java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Dialog" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Dialog.Alert" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Dialog.MinWidth" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Dialog.NoActionBar" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Dialog.Presentation" />
-  <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge" />
-  <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" />
-  <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar" />
-  <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.Fullscreen" />
-  <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.Overscan" />
-  <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" />
-  <java-symbol type="style" name="Theme.Material.DayNight.Panel" />
-  <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
-
   <java-symbol type="string" name="status_bar_rotate" />
   <java-symbol type="string" name="status_bar_headset" />
   <java-symbol type="string" name="status_bar_data_saver" />
@@ -2556,4 +2539,7 @@
 
   <!-- WallpaperManager config -->
   <java-symbol type="string" name="config_wallpaperCropperPackage" />
+
+  <java-symbol type="id" name="textSpacerNoTitle" />
+  <java-symbol type="id" name="titleDividerNoCustom" />
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index a361eda..6611eb1 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1294,7 +1294,7 @@
     </style>
 
     <!-- Default theme for Settings and activities launched from Settings. -->
-    <style name="Theme.Material.Settings" parent="Theme.Material.DayNight.DarkActionBar">
+    <style name="Theme.Material.Settings" parent="Theme.Material.Light.DarkActionBar">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
 
@@ -1304,7 +1304,7 @@
     </style>
 
     <!-- Default theme for Settings and activities launched from Settings. -->
-    <style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.DayNight.NoActionBar">
+    <style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.Light.NoActionBar">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
 
@@ -1313,41 +1313,41 @@
         <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
     </style>
 
-    <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.DayNight.BaseDialog">
+    <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.Light.BaseDialog">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
 
     <style name="Theme.Material.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog" />
 
-    <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.DayNight.Dialog.BaseAlert">
+    <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
 
     <style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert" />
 
-    <style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar">
+    <style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
 
-    <style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.DayNight.DialogWhenLarge.NoActionBar">
+    <style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
 
-    <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.DayNight.Dialog.Presentation">
+    <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
 
-    <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.DayNight.SearchBar">
+    <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.Light.SearchBar">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
 
-    <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.DayNight.CompactMenu">
+    <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.Light.CompactMenu">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
     </style>
diff --git a/core/res/res/values/themes_material_daynight.xml b/core/res/res/values/themes_material_daynight.xml
deleted file mode 100644
index 4ecca6b..0000000
--- a/core/res/res/values/themes_material_daynight.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!--
-===============================================================
-                        PLEASE READ
-===============================================================
-
-The Material themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see themes_device_defaults.xml.
-
-===============================================================
-                        PLEASE READ
-===============================================================
- -->
-<resources>
-
-    <!-- Material theme (day/night vesion) for activities. -->
-    <style name="Theme.Material.DayNight" parent="Theme.Material.Light" />
-
-    <!-- Variant of Material.DayNight that has a solid (opaque) action bar
-         with an inverse color profile. The dark action bar sharply stands out against
-         the light content (when applicable).  -->
-    <style name="Theme.Material.DayNight.DarkActionBar" parent="Theme.Material.Light.DarkActionBar" />
-
-    <!-- Variant of Material.DayNight with no action bar.  -->
-    <style name="Theme.Material.DayNight.NoActionBar" parent="Theme.Material.Light.NoActionBar" />
-
-    <!-- Variant of Material.DayNight that has no title bar and fills
-         the entire screen. This theme
-         sets {@link android.R.attr#windowFullscreen} to true.  -->
-    <style name="Theme.Material.DayNight.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen" />
-
-    <!-- Variant of Material.DayNight that has no title bar and fills
-         the entire screen and extends into the display overscan region. This theme
-         sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
-         to true. -->
-    <style name="Theme.Material.DayNight.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan" />
-
-    <!-- Variant of Material.DayNight that has no title bar and translucent
-         system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
-         {@link android.R.attr#windowTranslucentNavigation} to true. -->
-    <style name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor" />
-
-    <!-- Default Material.DayNight theme for panel windows. This removes all extraneous
-         window decorations, so you basically have an empty rectangle in which
-         to place your content. It makes the window floating, with a transparent
-         background, and turns off dimming behind the window. -->
-    <style name="Theme.Material.DayNight.Panel" parent="Theme.Material.Light.Panel" />
-
-    <!-- Material theme (day/night vesion) for dialog windows and activities,
-         which is used by the {@link android.app.Dialog} class. This changes
-         the window to be floating (not fill the entire screen), and puts a
-         frame around its contents. You can set this theme on an activity if
-         you would like to make an activity that looks like a Dialog. -->
-    <style name="Theme.Material.DayNight.Dialog" parent="Theme.Material.DayNight.BaseDialog" />
-    <style name="Theme.Material.DayNight.BaseDialog" parent="Theme.Material.Light.BaseDialog" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog that has a nice minimum width for
-         a regular dialog. -->
-    <style name="Theme.Material.DayNight.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog that does not include a title bar. -->
-    <style name="Theme.Material.DayNight.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a nice minimum width for
-         a regular dialog. -->
-    <style name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog that has a fixed size. -->
-    <style name="Theme.Material.DayNight.Dialog.FixedSize" parent="Theme.Material.Light.Dialog.FixedSize" />
-
-    <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a fixed size. -->
-    <style name="Theme.Material.DayNight.Dialog.NoActionBar.FixedSize" parent="Theme.Material.Light.Dialog.NoActionBar.FixedSize" />
-
-    <!-- Theme for a window that will be displayed either full-screen on
-         smaller screens (small, normal) or as a dialog on larger screens
-         (large, xlarge). -->
-    <style name="Theme.Material.DayNight.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge" />
-
-    <!-- Theme for a window with a dark action bar that will be displayed
-         either full-screen on smaller screens (small, normal) or as a dialog
-         on larger screens (large, xlarge). -->
-    <style name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
-
-    <!-- Theme for a window without an action bar that will be displayed either full-screen
-         on smaller screens (small, normal) or as a dialog on larger screens
-         (large, xlarge). -->
-    <style name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar" />
-
-    <!-- Theme for a presentation window on a secondary display. -->
-    <style name="Theme.Material.DayNight.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation" />
-
-    <!-- Material user theme for alert dialog windows, which is used by the
-         {@link android.app.AlertDialog} class. -->
-    <style name="Theme.Material.DayNight.Dialog.Alert" parent="Theme.Material.DayNight.Dialog.BaseAlert" />
-    <style name="Theme.Material.DayNight.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert" />
-
-    <style name="Theme.Material.DayNight.SearchBar" parent="Theme.Material.Light.SearchBar" />
-    <style name="Theme.Material.DayNight.CompactMenu" parent="Theme.Material.Light.CompactMenu" />
-
-</resources>
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 7519627..2a24881 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -22,7 +22,6 @@
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
 import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index fd28f64..e3639ec 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -101,8 +101,13 @@
 checkbuild: fontchain_lint
 
 FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+CHECK_EMOJI := false
+else
+CHECK_EMOJI := true
+endif
 
 .PHONY: fontchain_lint
 fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml
 	PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
-	python $(FONTCHAIN_LINTER) $(TARGET_OUT) external/unicode
+	python $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0a8c692..00eff91 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2157,7 +2157,7 @@
                                 final RecordConfigChangeCallbackData cbData =
                                         (RecordConfigChangeCallbackData) msg.obj;
                                 if (cbData.mCb != null) {
-                                    cbData.mCb.onRecordConfigChanged(cbData.mConfigs);
+                                    cbData.mCb.onRecordingConfigChanged(cbData.mConfigs);
                                 }
                                 break;
                             default:
@@ -2746,7 +2746,7 @@
          * @param configs array containing the results of
          *      {@link AudioManager#getActiveRecordingConfigurations()}.
          */
-        public void onRecordConfigChanged(AudioRecordingConfiguration[] configs) {}
+        public void onRecordingConfigChanged(AudioRecordingConfiguration[] configs) {}
     }
 
     private static class AudioRecordingCallbackInfo {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b6ff41e..621129d 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -596,14 +596,14 @@
      * AudioTrack player = new AudioTrack.Builder()
      *         .setAudioAttributes(new AudioAttributes.Builder()
      *                  .setUsage(AudioAttributes.USAGE_ALARM)
-     *                  .setContentType(CONTENT_TYPE_MUSIC)
+     *                  .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
      *                  .build())
      *         .setAudioFormat(new AudioFormat.Builder()
      *                 .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
-     *                 .setSampleRate(441000)
+     *                 .setSampleRate(44100)
      *                 .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
      *                 .build())
-     *         .setBufferSize(minBuffSize)
+     *         .setBufferSizeInBytes(minBuffSize)
      *         .build();
      * </pre>
      * <p>
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 9cad005..3d7e744 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -161,9 +161,14 @@
     public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
     /** Type is double. */
     public static final String TAG_EXPOSURE_TIME = "ExposureTime";
-    /** Type is String. */
+    /** Type is double. */
     public static final String TAG_F_NUMBER = "FNumber";
-    /** Type is String. */
+    /**
+     * Type is double.
+     *
+     * @deprecated use {@link #TAG_F_NUMBER} instead
+     */
+    @Deprecated
     public static final String TAG_APERTURE = "FNumber";
     /** Type is String. */
     public static final String TAG_FILE_SOURCE = "FileSource";
@@ -185,9 +190,14 @@
     public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
     /** Type is rational. */
     public static final String TAG_GAIN_CONTROL = "GainControl";
-    /** Type is String. */
+    /** Type is int. */
     public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
-    /** Type is String. */
+    /**
+     * Type is int.
+     *
+     * @deprecated use {@link #TAG_ISO_SPEED_RATINGS} instead
+     */
+    @Deprecated
     public static final String TAG_ISO = "ISOSpeedRatings";
     /** Type is String. */
     public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
@@ -225,8 +235,6 @@
     public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
     /** Type is int. */
     public static final String TAG_SUBSEC_TIME = "SubSecTime";
-    /** Type is int. @hide */
-    public static final String TAG_SUBSECTIME = "SubSecTime";
     /** Type is int. */
     public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
     /** Type is int. */
@@ -437,7 +445,7 @@
             new ExifTag(TAG_F_NUMBER, 33437),
             new ExifTag(TAG_EXPOSURE_PROGRAM, 34850),
             new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852),
-            new ExifTag(TAG_ISO, 34855),
+            new ExifTag(TAG_ISO_SPEED_RATINGS, 34855),
             new ExifTag(TAG_OECF, 34856),
             new ExifTag(TAG_EXIF_VERSION, 36864),
             new ExifTag(TAG_DATETIME_ORIGINAL, 36867),
@@ -1151,7 +1159,7 @@
             if (datetime == null) return -1;
             long msecs = datetime.getTime();
 
-            String subSecs = getAttribute(TAG_SUBSECTIME);
+            String subSecs = getAttribute(TAG_SUBSEC_TIME);
             if (subSecs != null) {
                 try {
                     long sub = Long.valueOf(subSecs);
@@ -1543,7 +1551,7 @@
         convertToDouble(TAG_EXPOSURE_TIME);
         convertToDouble(TAG_F_NUMBER);
         convertToDouble(TAG_SUBJECT_DISTANCE);
-        convertToInt(TAG_ISO);
+        convertToInt(TAG_ISO_SPEED_RATINGS);
         convertToDouble(TAG_EXPOSURE_BIAS_VALUE);
         convertToInt(TAG_WHITE_BALANCE);
         convertToInt(TAG_LIGHT_SOURCE);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index b1c1b79..2bd9781 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2666,12 +2666,13 @@
         public static final int HEVCHighTierLevel62 = 0x2000000;
 
         // from OMX_VIDEO_DOLBYVISIONPROFILETYPE
-        public static final int DolbyVisionProfileDvavDer = 0x1;
-        public static final int DolbyVisionProfileDvavDen = 0x2;
-        public static final int DolbyVisionProfileDvheDer = 0x3;
-        public static final int DolbyVisionProfileDvheDen = 0x4;
-        public static final int DolbyVisionProfileDvheDtr = 0x5;
-        public static final int DolbyVisionProfileDvheStn = 0x6;
+        public static final int DolbyVisionProfileDvavPer = 0x1;
+        public static final int DolbyVisionProfileDvavPen = 0x2;
+        public static final int DolbyVisionProfileDvheDer = 0x4;
+        public static final int DolbyVisionProfileDvheDen = 0x8;
+        public static final int DolbyVisionProfileDvheDtr = 0x10;
+        public static final int DolbyVisionProfileDvheStn = 0x20;
+        public static final int DolbyVisionProfileDvheDth = 0x40;
 
         // from OMX_VIDEO_DOLBYVISIONLEVELTYPE
         public static final int DolbyVisionLevelHd24    = 0x1;
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index ebe509c..7117fbd 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -34,7 +34,7 @@
 /**
  * MediaMuxer facilitates muxing elementary streams. Currently supports mp4 or
  * webm file as the output and at most one audio and/or one video elementary
- * stream.
+ * stream. MediaMuxer does not support muxing B-frames.
  * <p>
  * It is generally used like this:
  *
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index acf94f4c..03dc699 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -116,10 +116,10 @@
     private final int mType;
     private final boolean mIsHardwareInput;
 
-    // TODO: Remove mLabel and mIconUri when createTvInputInfo() is removed.
-    private String mLabel;
+    // TODO: Remove mIconUri when createTvInputInfo() is removed.
     private Uri mIconUri;
 
+    private final CharSequence mLabel;
     private final int mLabelResId;
     private final Icon mIcon;
     private final Icon mIconStandby;
@@ -161,8 +161,8 @@
         TvInputInfo info = new TvInputInfo.Builder(context, service)
                 .setHdmiDeviceInfo(hdmiDeviceInfo)
                 .setParentId(parentId)
+                .setLabel(label)
                 .build();
-        info.mLabel = label;
         info.mIconUri = iconUri;
         return info;
     }
@@ -215,8 +215,8 @@
                     throws XmlPullParserException, IOException {
         TvInputInfo info = new TvInputInfo.Builder(context, service)
                 .setTvInputHardwareInfo(hardwareInfo)
+                .setLabel(label)
                 .build();
-        info.mLabel = label;
         info.mIconUri = iconUri;
         return info;
     }
@@ -247,7 +247,7 @@
     }
 
     private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
-            int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
+            CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
             String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
             HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId,
             Bundle extras) {
@@ -255,6 +255,7 @@
         mId = id;
         mType = type;
         mIsHardwareInput = isHardwareInput;
+        mLabel = label;
         mLabelResId = labelResId;
         mIcon = icon;
         mIconStandby = iconStandby;
@@ -570,7 +571,7 @@
         dest.writeString(mId);
         dest.writeInt(mType);
         dest.writeByte(mIsHardwareInput ? (byte) 1 : 0);
-        dest.writeString(mLabel);
+        TextUtils.writeToParcel(mLabel, dest, flags);
         dest.writeParcelable(mIconUri, flags);
         dest.writeInt(mLabelResId);
         dest.writeParcelable(mIcon, flags);
@@ -612,7 +613,7 @@
         mId = in.readString();
         mType = in.readInt();
         mIsHardwareInput = in.readByte() == 1;
-        mLabel = in.readString();
+        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mIconUri = in.readParcelable(null);
         mLabelResId = in.readInt();
         mIcon = in.readParcelable(null);
@@ -660,6 +661,7 @@
 
         private final Context mContext;
         private final ResolveInfo mResolveInfo;
+        private CharSequence mLabel;
         private int mLabelResId;
         private Icon mIcon;
         private Icon mIconStandby;
@@ -746,12 +748,31 @@
         /**
          * Sets the label.
          *
+         * @param label The text to be used as label.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @hide
+         */
+        @SystemApi
+        public Builder setLabel(CharSequence label) {
+            if (mLabelResId != 0) {
+                throw new IllegalStateException("Resource ID for label is already set.");
+            }
+            this.mLabel = label;
+            return this;
+        }
+
+        /**
+         * Sets the label.
+         *
          * @param resId The resource ID of the text to use.
          * @return This Builder object to allow for chaining of calls to builder methods.
          * @hide
          */
         @SystemApi
         public Builder setLabel(int resId) {
+            if (mLabel != null) {
+                throw new IllegalStateException("Label text is already set.");
+            }
             this.mLabelResId = resId;
             return this;
         }
@@ -868,8 +889,8 @@
                 type = TYPE_TUNER;
             }
             parseServiceMetadata(type);
-            return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabelResId, mIcon,
-                    mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
+            return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
+                    mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
                     mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
                     mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras);
         }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
index dde9bda..312d9aa 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
@@ -61,7 +61,7 @@
     private static final String[] EXIF_TAGS = {
             ExifInterface.TAG_MAKE,
             ExifInterface.TAG_MODEL,
-            ExifInterface.TAG_APERTURE,
+            ExifInterface.TAG_F_NUMBER,
             ExifInterface.TAG_DATETIME,
             ExifInterface.TAG_EXPOSURE_TIME,
             ExifInterface.TAG_FLASH,
@@ -77,7 +77,7 @@
             ExifInterface.TAG_GPS_TIMESTAMP,
             ExifInterface.TAG_IMAGE_LENGTH,
             ExifInterface.TAG_IMAGE_WIDTH,
-            ExifInterface.TAG_ISO,
+            ExifInterface.TAG_ISO_SPEED_RATINGS,
             ExifInterface.TAG_ORIENTATION,
             ExifInterface.TAG_WHITE_BALANCE
     };
@@ -288,7 +288,7 @@
         // Checks values.
         assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
         assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
-        assertFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture);
+        assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture);
         assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
         assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
         assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
@@ -308,7 +308,7 @@
         assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp);
         assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
         assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
-        assertStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso);
+        assertStringTag(exifInterface, ExifInterface.TAG_ISO_SPEED_RATINGS, expectedValue.iso);
         assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
         assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance);
     }
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index b16554c..b0996aa 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -36,8 +36,6 @@
         <item name="android:windowActionBar">false</item>
         <item name="android:windowActionModeOverlay">true</item>
         <item name="android:windowNoTitle">true</item>
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:fitsSystemWindows">false</item>
 
         <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
     </style>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 2d051e4..87136ef3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -49,6 +49,7 @@
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.WindowManager;
 import android.widget.Spinner;
 
 import com.android.documentsui.SearchViewManager.SearchManagerListener;
@@ -103,6 +104,16 @@
     @CallSuper
     @Override
     public void onCreate(Bundle icicle) {
+        // This flag is being set here as a result of the bug. When the flag was set in the
+        // styles.xml keyboard was messing the layout of dialogs (create dir, rename).
+        // Attempts were made to keep the flag in the main theme and to override it in the dialog
+        // layout xml or to create separate style for dialog and assign it in styles.xml.
+        // None of this brought successful results.
+        // Setting the flag works here most probably because of the timing when it is set. Also the
+        // setting might not affect the dialogs that are created in new windows or it affects them
+        // in the different way that having this in the style.
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
         // Record the time when onCreate is invoked for metric.
         mStartTime = new Date().getTime();
 
@@ -261,6 +272,7 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
+        Metrics.logMenuAction(this, item.getItemId());
 
         switch (item.getItemId()) {
             case android.R.id.home:
@@ -624,10 +636,12 @@
                 return true;
             }
         } else if (keyCode == KeyEvent.KEYCODE_TAB) {
+            Metrics.logKeyboardAction(this, keyCode);
             // Tab toggles focus on the navigation drawer.
             toggleNavDrawerFocus();
             return true;
         } else if (keyCode == KeyEvent.KEYCODE_DEL) {
+            Metrics.logKeyboardAction(this, keyCode);
             popDir();
             return true;
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 68c0c2a..d4439d8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -212,19 +212,22 @@
             case R.id.menu_create_dir:
                 assert(canCreateDirectory());
                 showCreateDirectoryDialog();
-                return true;
+                break;
             case R.id.menu_new_window:
                 createNewWindow();
-                return true;
+                break;
             case R.id.menu_paste_from_clipboard:
                 DirectoryFragment dir = getDirectoryFragment();
                 if (dir != null) {
                     dir.pasteFromClipboard();
                 }
-                return true;
+                break;
+            default:
+                return super.onOptionsItemSelected(item);
         }
 
-        return super.onOptionsItemSelected(item);
+        Metrics.logMenuAction(this, item.getItemId());
+        return true;
     }
 
     private void createNewWindow() {
@@ -346,18 +349,21 @@
             case KeyEvent.KEYCODE_A:
                 dir = getDirectoryFragment();
                 if (dir != null) {
+                    Metrics.logKeyboardAction(this, keyCode);
                     dir.selectAllFiles();
                 }
                 return true;
             case KeyEvent.KEYCODE_C:
                 dir = getDirectoryFragment();
                 if (dir != null) {
+                    Metrics.logKeyboardAction(this, keyCode);
                     dir.copySelectedToClipboard();
                 }
                 return true;
             case KeyEvent.KEYCODE_V:
                 dir = getDirectoryFragment();
                 if (dir != null) {
+                    Metrics.logKeyboardAction(this, keyCode);
                     dir.pasteFromClipboard();
                 }
                 return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index f1f47c8..a4a67f9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.provider.DocumentsContract;
 import android.util.Log;
+import android.view.KeyEvent;
 
 import com.android.documentsui.State.ActionType;
 import com.android.documentsui.model.DocumentInfo;
@@ -67,6 +68,10 @@
     private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
     private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
     private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened";
+    private static final String COUNT_DRAG_N_DROP = "docsui_drag_n_drop";
+    private static final String COUNT_SEARCH = "docsui_search";
+    private static final String COUNT_MENU_ACTION = "docsui_menu_action";
+    private static final String COUNT_KEYBOARD_ACTION = "docsui_keyboard_action";
 
     // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
     // root that is not explicitly recognized by the Metrics code (see {@link
@@ -196,8 +201,71 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface MetricsOpType {}
 
-    // Codes representing different launch actions. These are used for bucketing stats in the
-    // COUNT_LAUNCH_ACTION histogram.
+    // Codes representing different provider types.  Used for sorting file operations when logging.
+    private static final int PROVIDER_INTRA = 0;
+    private static final int PROVIDER_SYSTEM = 1;
+    private static final int PROVIDER_EXTERNAL = 2;
+
+    @IntDef(flag = false, value = {
+            PROVIDER_INTRA,
+            PROVIDER_SYSTEM,
+            PROVIDER_EXTERNAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Provider {}
+
+
+    // Codes representing different menu actions. These are used for bucketing stats in the
+    // COUNT_MENU_ACTION histogram.
+    // Both regular toolbar menu and action mode menu operations are included.
+    // Do not change or rearrange these values, that will break historical data. Only add to the
+    // list.
+    // Do not use negative numbers or zero; clearcut only handles positive integers.
+    private static final int ACTION_MENU_OTHER = 1;
+    private static final int ACTION_MENU_GRID = 2;
+    private static final int ACTION_MENU_LIST = 3;
+    private static final int ACTION_MENU_SORT = 4;
+    private static final int ACTION_MENU_SORT_NAME = 5;
+    private static final int ACTION_MENU_SORT_DATE = 6;
+    private static final int ACTION_MENU_SORT_SIZE = 7;
+    private static final int ACTION_MENU_SEARCH = 8;
+    private static final int ACTION_MENU_SHOW_SIZE = 9;
+    private static final int ACTION_MENU_SETTINGS = 10;
+    private static final int ACTION_MENU_COPY_TO = 11;
+    private static final int ACTION_MENU_MOVE_TO = 12;
+    private static final int ACTION_MENU_DELETE = 13;
+    private static final int ACTION_MENU_RENAME = 14;
+    private static final int ACTION_MENU_CREATE_DIR = 15;
+    private static final int ACTION_MENU_SELECT_ALL = 16;
+    private static final int ACTION_MENU_SHARE = 17;
+    private static final int ACTION_MENU_OPEN = 18;
+    private static final int ACTION_MENU_ADVANCED = 19;
+
+    @IntDef(flag = false, value = {
+            ACTION_MENU_OTHER,
+            ACTION_MENU_GRID,
+            ACTION_MENU_LIST,
+            ACTION_MENU_SORT,
+            ACTION_MENU_SORT_NAME,
+            ACTION_MENU_SORT_DATE,
+            ACTION_MENU_SORT_SIZE,
+            ACTION_MENU_SHOW_SIZE,
+            ACTION_MENU_SETTINGS,
+            ACTION_MENU_COPY_TO,
+            ACTION_MENU_MOVE_TO,
+            ACTION_MENU_DELETE,
+            ACTION_MENU_RENAME,
+            ACTION_MENU_CREATE_DIR,
+            ACTION_MENU_SELECT_ALL,
+            ACTION_MENU_SHARE,
+            ACTION_MENU_OPEN,
+            ACTION_MENU_ADVANCED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MenuAction {}
+
+    // Codes representing different menu actions. These are used for bucketing stats in the
+    // COUNT_MENU_ACTION histogram.
     // Do not change or rearrange these values, that will break historical data. Only add to the
     // list.
     // Do not use negative numbers or zero; clearcut only handles positive integers.
@@ -223,18 +291,30 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface MetricsAction {}
 
-    // Codes representing different provider types.  Used for sorting file operations when logging.
-    private static final int PROVIDER_INTRA = 0;
-    private static final int PROVIDER_SYSTEM = 1;
-    private static final int PROVIDER_EXTERNAL = 2;
+    // Codes representing different keyboard shortcut triggered actions. These are used for
+    // bucketing stats in the COUNT_KEYBOARD_ACTION histogram.
+    // Do not change or rearrange these values, that will break historical data. Only add to the
+    // list.
+    // Do not use negative numbers or zero; clearcut only handles positive integers.
+    private static final int ACTION_KEYBOARD_OTHER = 1;
+    private static final int ACTION_KEYBOARD_PASTE = 2;
+    private static final int ACTION_KEYBOARD_COPY = 3;
+    private static final int ACTION_KEYBOARD_DELETE = 4;
+    private static final int ACTION_KEYBOARD_SELECT_ALL = 5;
+    private static final int ACTION_KEYBOARD_BACK = 6;
+    private static final int ACTION_KEYBOARD_SWITCH_FOCUS = 7;
 
-    @IntDef(flag = true, value = {
-            PROVIDER_INTRA,
-            PROVIDER_SYSTEM,
-            PROVIDER_EXTERNAL
+    @IntDef(flag = false, value = {
+            ACTION_KEYBOARD_OTHER,
+            ACTION_KEYBOARD_PASTE,
+            ACTION_KEYBOARD_COPY,
+            ACTION_KEYBOARD_DELETE,
+            ACTION_KEYBOARD_SELECT_ALL,
+            ACTION_KEYBOARD_BACK,
+            ACTION_KEYBOARD_SWITCH_FOCUS
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Provider {}
+    public @interface KeyboardAction {}
 
     // Codes representing different actions to open the drawer. They are used for bucketing stats in
     // the COUNT_DRAWER_OPENED histogram.
@@ -440,6 +520,39 @@
     }
 
     /**
+     * Logs keyboard shortcut actions. Since keyboard shortcuts have their corresponding menu items,
+     * they are identified by menu item resource id for convenience.
+     * @param context
+     * @param keyCode
+     */
+    public static void logKeyboardAction(Context context, int keyCode) {
+        @KeyboardAction int keyboardAction = ACTION_KEYBOARD_OTHER;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_V:
+                keyboardAction = ACTION_KEYBOARD_PASTE;
+                break;
+            case KeyEvent.KEYCODE_C:
+                keyboardAction = ACTION_KEYBOARD_COPY;
+                break;
+            case KeyEvent.KEYCODE_FORWARD_DEL:
+                keyboardAction = ACTION_KEYBOARD_DELETE;
+                break;
+            case KeyEvent.KEYCODE_A:
+                keyboardAction = ACTION_KEYBOARD_SELECT_ALL;
+                break;
+            case KeyEvent.KEYCODE_DEL:
+                keyboardAction = ACTION_KEYBOARD_BACK;
+                break;
+            case KeyEvent.KEYCODE_TAB:
+                keyboardAction = ACTION_KEYBOARD_SWITCH_FOCUS;
+                break;
+            default:
+                break;
+        }
+        logHistogram(context, COUNT_KEYBOARD_ACTION, keyboardAction);
+    }
+
+    /**
      * Logs startup time in milliseconds.
      * @param context
      * @param startupMs Startup time in milliseconds.
@@ -448,6 +561,25 @@
         logHistogram(context, COUNT_STARTUP_MS, startupMs);
     }
 
+    /**
+     * Logs a drag and drop action. Call this when the user drops the content triggering copy.
+     * operation.
+     *
+     * @param context
+     */
+    public static void logDragNDrop(Context context) {
+        logCount(context, COUNT_DRAG_N_DROP);
+    }
+
+    /**
+     * Logs a search. Call this when the search operation is finished.
+     *
+     * @param context
+     */
+    public static void logSearch(Context context) {
+        logCount(context, COUNT_SEARCH);
+    }
+
     private static void logInterProviderFileOps(
             Context context,
             String histogram,
@@ -568,6 +700,74 @@
     }
 
     /**
+     * Logs menu action that was selected by user.
+     * @param context
+     * @param id Resource id of the menu item.
+     */
+    public static void logMenuAction(Context context, int id) {
+        @MenuAction int menuAction = ACTION_MENU_OTHER;
+        switch (id) {
+            case R.id.menu_grid:
+                menuAction = ACTION_MENU_GRID;
+                break;
+            case R.id.menu_list:
+                menuAction = ACTION_MENU_LIST;
+                break;
+            case R.id.menu_sort:
+                menuAction = ACTION_MENU_SORT;
+                break;
+            case R.id.menu_sort_name:
+                menuAction = ACTION_MENU_SORT_NAME;
+                break;
+            case R.id.menu_sort_date:
+                menuAction = ACTION_MENU_SORT_DATE;
+                break;
+            case R.id.menu_sort_size:
+                menuAction = ACTION_MENU_SORT_SIZE;
+                break;
+            case R.id.menu_search:
+                menuAction = ACTION_MENU_SEARCH;
+                break;
+            case R.id.menu_file_size:
+                menuAction = ACTION_MENU_SHOW_SIZE;
+                break;
+            case R.id.menu_settings:
+                menuAction = ACTION_MENU_SETTINGS;
+                break;
+            case R.id.menu_copy_to:
+                menuAction = ACTION_MENU_COPY_TO;
+                break;
+            case R.id.menu_move_to:
+                menuAction = ACTION_MENU_MOVE_TO;
+                break;
+            case R.id.menu_delete:
+                menuAction = ACTION_MENU_DELETE;
+                break;
+            case R.id.menu_rename:
+                menuAction = ACTION_MENU_RENAME;
+                break;
+            case R.id.menu_create_dir:
+                menuAction = ACTION_MENU_CREATE_DIR;
+                break;
+            case R.id.menu_select_all:
+                menuAction = ACTION_MENU_SELECT_ALL;
+                break;
+            case R.id.menu_share:
+                menuAction = ACTION_MENU_SHARE;
+                break;
+            case R.id.menu_open:
+                menuAction = ACTION_MENU_OPEN;
+                break;
+            case R.id.menu_advanced:
+                menuAction = ACTION_MENU_ADVANCED;
+                break;
+            default:
+                break;
+        }
+        logHistogram(context, COUNT_MENU_ACTION, menuAction);
+    }
+
+    /**
      * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
      *
      * @param context
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 11b8891..945ed34 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -185,6 +185,9 @@
         if(mFullBar) {
             Menu menu = mActionBar.getMenu();
             menu.setGroupVisible(R.id.group_hide_when_searching, false);
+        } else {
+            // If search in full-bar mode it will be logged in FilesActivity#onOptionsItemSelected
+            Metrics.logMenuAction(mActionBar.getContext(), R.id.menu_search);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 73ce0e1..20316ff 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -85,6 +85,7 @@
 import com.android.documentsui.Events.MotionInputEvent;
 import com.android.documentsui.Menus;
 import com.android.documentsui.MessageBar;
+import com.android.documentsui.Metrics;
 import com.android.documentsui.R;
 import com.android.documentsui.RecentsLoader;
 import com.android.documentsui.RootsCache;
@@ -594,6 +595,7 @@
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            Metrics.logMenuAction(getContext(), item.getItemId());
 
             Selection selection = mSelectionManager.getSelection(new Selection());
 
@@ -1124,6 +1126,7 @@
                     if (Objects.equals(src, dst)) {
                         return false;
                     }
+                    Metrics.logDragNDrop(getContext());
                     copyFromClipData(event.getClipData(), dst);
                     return true;
             }
@@ -1336,6 +1339,7 @@
                     // This has to be handled here instead of in a keyboard shortcut, because
                     // keyboard shortcuts all have to be modified with the 'Ctrl' key.
                     if (mSelectionManager.hasSelection()) {
+                        Metrics.logKeyboardAction(getContext(), keyCode);
                         deleteDocuments(mSelectionManager.getSelection());
                     }
                     // Always handle the key, even if there was nothing to delete. This is a
@@ -1622,6 +1626,9 @@
     @Override
     public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
         if (!isAdded()) return;
+        if (mSearchMode) {
+            Metrics.logSearch(getContext());
+        }
 
         State state = getDisplayState();
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
index 5b53caf..4c8dc00 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
@@ -162,7 +162,9 @@
     }
 
     UiObject findSearchViewIcon() {
-        return findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
+        return mContext.getResources().getBoolean(R.bool.full_bar_search_view)
+                ? findObject("com.android.documentsui:id/menu_search")
+                : findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
     }
 
     UiObject findActionModeBar() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
index 454221a..2b549f1 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -77,6 +77,7 @@
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+            case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
                 return SecurityMode.Password;
 
             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
index af4c347..0feda92 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
@@ -244,9 +244,20 @@
 
                 ranges = PageRangeUtils.normalize(ranges);
 
+                int lastPageIdx = mEditor.getPageCount() - 1;
+
                 final int rangeCount = ranges.length;
                 for (int i = rangeCount - 1; i >= 0; i--) {
                     PageRange range = ranges[i];
+
+                    // Ignore removal of pages that are outside the document
+                    if (range.getEnd() > lastPageIdx) {
+                        if (range.getStart() > lastPageIdx) {
+                            continue;
+                        }
+                        range = new PageRange(range.getStart(), lastPageIdx);
+                    }
+
                     for (int j = range.getEnd(); j >= range.getStart(); j--) {
                         mEditor.removePage(j);
                     }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index dba6a8b..cde0fa3 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -476,7 +476,7 @@
     }
 
     private void onPrintDocumentError(String message) {
-        mProgressMessageController.cancel();
+        setState(mProgressMessageController.cancel());
         ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY);
 
         setState(STATE_UPDATE_FAILED);
@@ -502,7 +502,7 @@
             Log.i(LOG_TAG, "onUpdateCanceled()");
         }
 
-        mProgressMessageController.cancel();
+        setState(mProgressMessageController.cancel());
         ensurePreviewUiShown();
 
         switch (mState) {
@@ -524,7 +524,7 @@
             Log.i(LOG_TAG, "onUpdateCompleted()");
         }
 
-        mProgressMessageController.cancel();
+        setState(mProgressMessageController.cancel());
         ensurePreviewUiShown();
 
         // Update the print job with the info for the written document. The page
@@ -572,7 +572,7 @@
             Log.i(LOG_TAG, "onUpdateFailed()");
         }
 
-        mProgressMessageController.cancel();
+        setState(mProgressMessageController.cancel());
         ensureErrorUiShown(error, PrintErrorFragment.ACTION_RETRY);
 
         if (mState == STATE_CREATE_FILE_FAILED
@@ -1169,6 +1169,18 @@
         doFinish();
     }
 
+    /**
+     * Update the selected pages from the text field.
+     */
+    private void updateSelectedPagesFromTextField() {
+        PageRange[] selectedPages = computeSelectedPages();
+        if (!Arrays.equals(mSelectedPages, selectedPages)) {
+            mSelectedPages = selectedPages;
+            // Update preview.
+            updatePrintPreviewController(false);
+        }
+    }
+
     private void confirmPrint() {
         setState(STATE_PRINT_CONFIRMED);
 
@@ -1178,14 +1190,10 @@
         addCurrentPrinterToHistory();
         setUserPrinted();
 
-        PageRange[] selectedPages = computeSelectedPages();
-        if (!Arrays.equals(mSelectedPages, selectedPages)) {
-            mSelectedPages = selectedPages;
-            // Update preview.
-            updatePrintPreviewController(false);
-        }
-
+        // updateSelectedPagesFromTextField migth update the preview, hence apply the preview first
         updateSelectedPagesFromPreview();
+        updateSelectedPagesFromTextField();
+
         mPrintPreviewController.closeOptions();
 
         if (canUpdateDocument()) {
@@ -1485,6 +1493,10 @@
                     cancelPrint();
                 }
             } else if (view == mMoreOptionsButton) {
+                // The selected pages is only applied once the user leaves the text field. A click
+                // on this button, does not count as leaving.
+                updateSelectedPagesFromTextField();
+
                 if (mCurrentPrinter != null) {
                     startAdvancedPrintOptionsActivity(mCurrentPrinter);
                 }
@@ -2066,8 +2078,9 @@
             mSpoolerProvider.destroy();
         }
 
+        setState(mProgressMessageController.cancel());
+
         if (mState != STATE_INITIALIZING) {
-            mProgressMessageController.cancel();
             mPrintedDocument.finish();
             mPrintedDocument.destroy();
             mPrintPreviewController.destroy(new Runnable() {
@@ -2773,13 +2786,7 @@
             }
 
             if (view == mPageRangeEditText && !hasFocus) {
-                PageRange[] selectedPages = computeSelectedPages();
-                if (selectedPages != null && !Arrays.equals(mSelectedPages, selectedPages)) {
-                    mSelectedPages = selectedPages;
-
-                    // Update preview.
-                    updatePrintPreviewController(false);
-                }
+                updateSelectedPagesFromTextField();
             }
         }
     }
@@ -2910,29 +2917,50 @@
 
         private boolean mPosted;
 
+        /** State before run was executed */
+        private int mPreviousState = -1;
+
         public ProgressMessageController(Context context) {
             mHandler = new Handler(context.getMainLooper(), null, false);
         }
 
         public void post() {
-            if (mPosted) {
+            if (mState == STATE_UPDATE_SLOW) {
+                setState(STATE_UPDATE_SLOW);
+                ensureProgressUiShown();
+                updateOptionsUi();
+
+                return;
+            } else if (mPosted) {
                 return;
             }
+            mPreviousState = -1;
             mPosted = true;
             mHandler.postDelayed(this, PROGRESS_TIMEOUT_MILLIS);
         }
 
-        public void cancel() {
+        private int getStateAfterCancel() {
+            if (mPreviousState == -1) {
+                return mState;
+            } else {
+                return mPreviousState;
+            }
+        }
+
+        public int cancel() {
             if (!mPosted) {
-                return;
+                return getStateAfterCancel();
             }
             mPosted = false;
             mHandler.removeCallbacks(this);
+
+            return getStateAfterCancel();
         }
 
         @Override
         public void run() {
             mPosted = false;
+            mPreviousState = mState;
             setState(STATE_UPDATE_SLOW);
             ensureProgressUiShown();
             updateOptionsUi();
@@ -3081,12 +3109,10 @@
             List<PageRange> rangesToShred = new ArrayList<>();
             PageRange previousRange = null;
 
-            final int pageCount = printJob.getDocumentInfo().getPageCount();
-
             PageRange[] printedPages = printJob.getPages();
             final int rangeCount = printedPages.length;
             for (int i = 0; i < rangeCount; i++) {
-                PageRange range = PageRangeUtils.asAbsoluteRange(printedPages[i], pageCount);
+                PageRange range = printedPages[i];
 
                 if (previousRange == null) {
                     final int startPageIdx = 0;
@@ -3105,11 +3131,8 @@
                 }
 
                 if (i == rangeCount - 1) {
-                    final int startPageIdx = range.getEnd() + 1;
-                    final int endPageIdx = printJob.getDocumentInfo().getPageCount() - 1;
-                    if (startPageIdx <= endPageIdx) {
-                        PageRange removedRange = new PageRange(startPageIdx, endPageIdx);
-                        rangesToShred.add(removedRange);
+                    if (range.getEnd() != Integer.MAX_VALUE) {
+                        rangesToShred.add(new PageRange(range.getEnd() + 1, Integer.MAX_VALUE));
                     }
                 }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 987b5ea..61c9f2b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -21,6 +21,7 @@
 import android.app.AppGlobals;
 import android.app.backup.BackupManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -63,6 +64,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.providers.settings.SettingsState.Setting;
+import com.android.server.SystemConfig;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -1940,7 +1942,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 125;
+            private static final int SETTINGS_VERSION = 126;
 
             private final int mUserId;
 
@@ -2136,6 +2138,35 @@
                     currentVersion = 125;
                 }
 
+                if (currentVersion == 125) {
+                    // Version 125: Allow OEMs to set the default VR service.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+
+                    Setting currentSetting = secureSettings.getSettingLocked(
+                            Settings.Secure.ENABLED_VR_LISTENERS);
+                    if (currentSetting == null) {
+                        ArraySet<ComponentName> l =
+                                SystemConfig.getInstance().getDefaultVrComponents();
+
+                        if (l != null && !l.isEmpty()) {
+                            StringBuilder b = new StringBuilder();
+                            boolean start = true;
+                            for (ComponentName c : l) {
+                                if (!start) {
+                                    b.append(':');
+                                }
+                                b.append(c.flattenToString());
+                                start = false;
+                            }
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.ENABLED_VR_LISTENERS, b.toString(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+
+                    }
+                    currentVersion = 126;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0c35573..b557dc4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -139,7 +139,7 @@
 
         <activity
             android:name=".BugreportWarningActivity"
-            android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert"
+            android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
             android:exported="false" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
similarity index 78%
rename from packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
index 89e4aac..ebc6a4a7 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
+++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
@@ -17,13 +17,13 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android">
 
     <objectAnimator
-        android:propertyName="translationY"
-        android:valueTo="10dp"
-        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:propertyName="scaleX"
+        android:valueTo="1.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
     <objectAnimator
-        android:propertyName="alpha"
+        android:propertyName="scaleY"
         android:valueTo="1.0"
-        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
 </set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
similarity index 76%
copy from packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
copy to packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
index 89e4aac..95499bd 100644
--- a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
+++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
@@ -17,13 +17,13 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android">
 
     <objectAnimator
-        android:propertyName="translationY"
-        android:valueTo="10dp"
-        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:propertyName="scaleX"
+        android:valueTo="0.7"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
     <objectAnimator
-        android:propertyName="alpha"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:propertyName="scaleY"
+        android:valueTo="0.7"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
         android:duration="@integer/recents_tv_pip_focus_anim_duration" />
 </set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml b/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
deleted file mode 100644
index c73fed6..0000000
--- a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <objectAnimator
-        android:propertyName="translationY"
-        android:valueTo="0dp"
-        android:interpolator="@android:interpolator/fast_out_linear_in"
-        android:duration="@integer/recents_tv_pip_focus_anim_duration" />
-    <objectAnimator
-        android:propertyName="alpha"
-        android:valueTo="0.0"
-        android:interpolator="@android:interpolator/fast_out_linear_in"
-        android:duration="@integer/recents_tv_pip_focus_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..7555bdd
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_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="translationY"
+    android:valueTo="0dp"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml
new file mode 100644
index 0000000..b40ccd4
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_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="translationY"
+    android:valueTo="-57dp"
+    android:interpolator="@android:interpolator/fast_out_slow_in"
+    android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..681ff91
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_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="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
new file mode 100644
index 0000000..e6deb0f
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_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="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png
deleted file mode 100644
index 7b0fcc7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png
deleted file mode 100644
index 73e9c96..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png
deleted file mode 100644
index a02e21c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png
deleted file mode 100644
index 4af2617..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png
deleted file mode 100644
index 24bdbb6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png
deleted file mode 100644
index 6ecd2d3..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png
deleted file mode 100644
index 5e733ef..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png
deleted file mode 100644
index ecc2c83..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/stat_notify_image.xml b/packages/SystemUI/res/drawable/stat_notify_image.xml
new file mode 100644
index 0000000..c8745d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_notify_image.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M21,19V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14C20.1,21,21,20.1,21,19z
+M8.5,13.5l2.5,3l3.5-4.5l4.5,6H5 L8.5,13.5z" />
+    <path
+        android:pathData="M0,0h24v24H0V0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_notify_image_error.xml b/packages/SystemUI/res/drawable/stat_notify_image_error.xml
new file mode 100644
index 0000000..b929005
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_notify_image_error.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0,0h24v24H0V0z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17,18H5l3.5-4.5l2.5,3l3.3-4.5l2.7,3.8V8h4V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h12V18z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M19.2,21H21v-1.8h-1.8V21z M19.2,9.9v7.4H21V9.9H19.2z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
index 5cabb77a..405ea0c 100644
--- a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
@@ -17,8 +17,8 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
     <size
-        android:width="36dp"
-        android:height="36dp" />
+        android:width="34dp"
+        android:height="34dp" />
     <solid
         android:color="#4DFFFFFF" />
 </shape>
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 0f8c77c..28ea66d 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -31,18 +31,12 @@
         android:focusable="true"
         android:layoutDirection="rtl" />
 
+    <!-- Placeholder view to give focus to the PIP menus. -->
     <View
-        android:id="@+id/pip_shade"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone"
-        android:background="#76000000" />
-
-    <include
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top|center_horizontal"
-        android:layout_marginTop="132dp"
-        layout="@layout/tv_pip_controls" />
+        android:id="@+id/pip"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:focusable="true"
+        android:visibility="gone" />
 
 </com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml
index 2b2ce4e..625e9c1 100644
--- a/packages/SystemUI/res/layout/recents_stack_action_button.xml
+++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml
@@ -18,7 +18,6 @@
     android:id="@+id/button"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:gravity="start|center_vertical"
     android:paddingStart="14dp"
     android:paddingEnd="14dp"
     android:paddingTop="12dp"
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index 2e0c9e7..563441f 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -17,13 +17,8 @@
 */
 -->
 
-<com.android.systemui.tv.pip.PipControlsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/pip_controls"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:orientation="horizontal">
+<!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <LinearLayout
         android:layout_width="100dp"
@@ -98,4 +93,4 @@
             android:textSize="12sp"
             android:textColor="#EEEEEE" />
     </LinearLayout>
-</com.android.systemui.tv.pip.PipControlsView>
+</merge>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index c2c83ff..2647a99 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -26,7 +26,8 @@
     android:gravity="top|center_horizontal"
     android:clipChildren="false">
 
-    <include
-        layout="@layout/tv_pip_controls"
-        android:clipChildren="false" />
+    <com.android.systemui.tv.pip.PipControlsView
+        android:id="@+id/pip_controls"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index c5c7e84..64bf3b5 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -38,25 +38,4 @@
         android:gravity="center"
         android:maxLines="2"
         android:text="@string/pip_hold_home" />
-    <LinearLayout
-        android:id="@+id/guide_buttons"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_centerHorizontal="true"
-        android:orientation="horizontal">
-        <ImageView
-            android:layout_width="19dp"
-            android:layout_height="19dp"
-            android:src="@drawable/ic_fullscreen_white_24dp" />
-        <ImageView
-            android:layout_width="19dp"
-            android:layout_height="19dp"
-            android:src="@drawable/ic_close_white" />
-        <ImageView
-            android:id="@+id/guide_button_play_pause"
-            android:layout_width="19dp"
-            android:layout_height="19dp"
-            android:src="@drawable/ic_pause_white_24dp" />
-    </LinearLayout>
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
new file mode 100644
index 0000000..1e464d8
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:gravity="top|center_horizontal">
+
+    <com.android.systemui.tv.pip.PipRecentsControlsView
+        android:id="@+id/pip_controls"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent" />
+
+    <View
+        android:id="@+id/recents"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        android:focusable="true" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 953dd65..337513d 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -38,9 +38,6 @@
     <dimen name="recents_tv_unselected_item_z">6dp</dimen>
     <dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
 
-    <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
-    <dimen name="tv_pip_bounds_space">3dp</dimen>
-
     <!-- Values for text on recents cards on tv -->
     <dimen name="recents_tv_title_text_size">12sp</dimen>
 
@@ -52,4 +49,10 @@
     <dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen>
     <dimen name="recents_tv_dismiss_text_size">12sp</dimen>
 
+    <!-- Values for PIP in recents -->
+    <dimen name="recents_tv_pip_controls_margin_top">10dp</dimen>
+
+    <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
+    <dimen name="tv_pip_bounds_space">3dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a523a41..a01066d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1388,6 +1388,31 @@
     <string name="keyboard_shortcut_group_system_recents">Recents</string>
     <!-- User visible title for the the keyboard shortcut that triggers the back action. -->
     <string name="keyboard_shortcut_group_system_back">Back</string>
+    <!-- User visible title for the the keyboard shortcut that triggers the notification shade. -->
+    <string name="keyboard_shortcut_group_system_notifications">Notifications</string>
+    <!-- User visible title for the the keyboard shortcut that triggers the keyboard shortcuts helper. -->
+    <string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string>
+    <!-- User visible title for the the keyboard shortcut that switches input methods. -->
+    <string name="keyboard_shortcut_group_system_switch_input">Switch input method</string>
+
+    <!-- User visible title for the system-wide applications keyboard shortcuts list. -->
+    <string name="keyboard_shortcut_group_applications">Applications</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the assist app. -->
+    <string name="keyboard_shortcut_group_applications_assist">Assist</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the browser app. -->
+    <string name="keyboard_shortcut_group_applications_browser">Browser</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the contacts app. -->
+    <string name="keyboard_shortcut_group_applications_contacts">Contacts</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the email app. -->
+    <string name="keyboard_shortcut_group_applications_email">Email</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the instant messaging app. -->
+    <string name="keyboard_shortcut_group_applications_im">IM</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the music app. -->
+    <string name="keyboard_shortcut_group_applications_music">Music</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the YouTube app. -->
+    <string name="keyboard_shortcut_group_applications_youtube">YouTube</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
+    <string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
 
     <!-- SysUI Tuner: Option to show full do not disturb panel in volume [CHAR LIMIT=60] -->
     <string name="tuner_full_zen_title">Show with volume controls</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index fff7d78..8060e07 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -344,7 +344,7 @@
 
         // Update the nav bar scrim, but defer the animation until the enter-window event
         boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
-        updateNavBarScrim(animateNavBarScrim, null);
+        mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
 
         // If this is a new instance relaunched by AM, without going through the normal mechanisms,
         // then we have to manually trigger the enter animation state
@@ -417,39 +417,43 @@
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        // Update the nav bar for the current orientation
-        updateNavBarScrim(false /* animateNavBarScrim */, AnimationProps.IMMEDIATE);
 
         // Notify of the config change
         int newOrientation = getResources().getConfiguration().orientation;
+        int numStackTasks = mRecentsView.getStack().getStackTaskCount();
         EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
-                (mLastOrientation != newOrientation)));
+                (mLastOrientation != newOrientation), numStackTasks > 0));
         mLastOrientation = newOrientation;
     }
 
     @Override
     public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
         super.onMultiWindowModeChanged(isInMultiWindowMode);
+
+        // Reload the task stack completely
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+        RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+        loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
+
+        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+        loader.loadTasks(this, loadPlan, loadOpts);
+
+        TaskStack stack = loadPlan.getTaskStack();
+        int numStackTasks = stack.getStackTaskCount();
+
         EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
-                false /* fromOrientationChange */));
+                false /* fromOrientationChange */, numStackTasks > 0));
 
         if (mRecentsView != null) {
-            // Reload the task stack completely
-            RecentsConfiguration config = Recents.getConfiguration();
-            RecentsActivityLaunchState launchState = config.getLaunchState();
-            RecentsTaskLoader loader = Recents.getTaskLoader();
-            RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
-            loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
-
-            RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
-            loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
-            loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
-            loader.loadTasks(this, loadPlan, loadOpts);
-
-            mRecentsView.updateStack(loadPlan.getTaskStack());
+            mRecentsView.updateStack(stack);
         }
 
-        EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode));
+        EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
+                numStackTasks > 0));
     }
 
     @Override
@@ -729,18 +733,4 @@
         });
         return true;
     }
-
-    /**
-     * Updates the nav bar scrim.
-     */
-    private void updateNavBarScrim(boolean animateNavBarScrim, AnimationProps animation) {
-        // Animate the SystemUI scrims into view
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        int taskCount = mRecentsView.getStack().getTaskCount();
-        boolean hasNavBarScrim = (taskCount > 0) && !ssp.hasTransposedNavBar();
-        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
-        if (animateNavBarScrim && animation != null) {
-            mScrimViews.animateNavBarScrimVisibility(true, animation);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 43d627d..4dae746 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -584,18 +584,24 @@
         Rect systemInsets = new Rect();
         ssp.getStableInsets(systemInsets);
         Rect windowRect = ssp.getWindowRect();
+        // When docked, the nav bar insets are consumed and the activity is measured without insets.
+        // However, the window bounds include the insets, so we need to subtract them here to make
+        // them identical.
+        if (ssp.hasDockedTask()) {
+            windowRect.bottom -= systemInsets.bottom;
+            systemInsets.bottom = 0;
+        }
         calculateWindowStableInsets(systemInsets, windowRect);
         windowRect.offsetTo(0, 0);
 
         TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
-        stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
-                mTaskStackBounds);
 
         // Rebind the header bar and draw it for the transition
-        Rect taskStackBounds = new Rect(mTaskStackBounds);
         stackLayout.setSystemInsets(systemInsets);
         if (stack != null) {
-            stackLayout.initialize(windowRect, taskStackBounds,
+            stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+                    mTaskStackBounds);
+            stackLayout.initialize(windowRect, mTaskStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
             mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
         }
@@ -794,6 +800,9 @@
             synchronized (mHeaderBarLock) {
                 int toHeaderWidth = (int) toTransform.rect.width();
                 int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+                if (toHeaderWidth <= 0 || toHeaderHeight <= 0) {
+                    return null;
+                }
                 boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
                 mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
                         (int) toTransform.rect.height());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
index 8be9ca7..e3bc2a7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
@@ -25,9 +25,12 @@
 
     public final boolean fromMultiWindow;
     public final boolean fromOrientationChange;
+    public final boolean hasStackTasks;
 
-    public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromOrientationChange) {
+    public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromOrientationChange,
+            boolean hasStackTasks) {
         this.fromMultiWindow = fromMultiWindow;
         this.fromOrientationChange = fromOrientationChange;
+        this.hasStackTasks = hasStackTasks;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
index 19245d9..cf2a68e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
@@ -24,8 +24,10 @@
 public class MultiWindowStateChangedEvent extends EventBus.Event {
 
     public final boolean inMultiWindow;
+    public final boolean hasStackTasks;
 
-    public MultiWindowStateChangedEvent(boolean inMultiWindow) {
+    public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean hasStackTasks) {
         this.inMultiWindow = inMultiWindow;
+        this.hasStackTasks = hasStackTasks;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
index b85ddac..216be61 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
@@ -23,7 +23,7 @@
 /**
  * This event is sent when a user drags in/out of a drop target.
  */
-public class DragDropTargetChangedEvent extends EventBus.Event {
+public class DragDropTargetChangedEvent extends EventBus.AnimatedEvent {
 
     // The task that is currently being dragged
     public final Task task;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 134b90c..483f9e5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.recents.tv;
 
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Intent;
@@ -55,11 +53,12 @@
 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.tv.animations.FocusAnimationHolder;
 import com.android.systemui.recents.tv.views.RecentsTvView;
 import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.tv.pip.PipManager;
-import com.android.systemui.tv.pip.PipControlsView;
+import com.android.systemui.tv.pip.PipRecentsOverlayManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -80,15 +79,13 @@
     private boolean mIgnoreAltTabRelease;
 
     private RecentsTvView mRecentsView;
-    private PipControlsView mPipControlsView;
-    private View mPipShadeView;
-    private AnimatorSet mPipControlsViewFadeInAnimator;
-    private AnimatorSet mPipControlsViewFadeOutAnimator;
+    private FocusAnimationHolder mRecentsFocusAnimationHolder;
+    private View mPipView;
     private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
     private FinishRecentsRunnable mFinishLaunchHomeRunnable;
 
-    private PipManager mPipManager;
-    private PipManager.Listener mPipListener = new PipManager.Listener() {
+    private final PipManager mPipManager = PipManager.getInstance();
+    private final PipManager.Listener mPipListener = new PipManager.Listener() {
         @Override
         public void onPipEntered() {
             updatePipUI();
@@ -113,10 +110,38 @@
 
         @Override
         public void onPipResizeAboutToStart() { }
-
-        @Override
-        public void onMediaControllerChanged() { }
     };
+    private PipRecentsOverlayManager mPipRecentsOverlayManager;
+    private final PipRecentsOverlayManager.Callback mPipRecentsOverlayManagerCallback =
+            new PipRecentsOverlayManager.Callback() {
+                @Override
+                public void onClosed() {
+                    dismissRecentsToLaunchTargetTaskOrHome();
+                }
+
+                @Override
+                public void onBackPressed() {
+                    RecentsTvActivity.this.onBackPressed();
+                }
+
+                @Override
+                public void onRecentsFocused() {
+                    mRecentsView.requestFocus();
+                }
+            };
+    private final View.OnFocusChangeListener mPipViewFocusChangeListener =
+            new View.OnFocusChangeListener() {
+                @Override
+                public void onFocusChange(View v, boolean hasFocus) {
+                    if (hasFocus) {
+                        mRecentsFocusAnimationHolder.startFocusLoseAnimation();
+                        mPipRecentsOverlayManager.requestFocus(
+                                mTaskStackViewAdapter.getItemCount() > 0);
+                    } else {
+                        mRecentsFocusAnimationHolder.startFocusGainAnimation();
+                    }
+                }
+            };
 
     /**
      * A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -248,7 +273,7 @@
             finish();
             return;
         }
-        mPipManager = PipManager.getInstance();
+        mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager();
 
         // Register this activity with the event bus
         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
@@ -263,21 +288,19 @@
         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
-        mPipControlsView.setListener(new PipControlsView.Listener() {
-            @Override
-            public void onClosed() {
-                dismissRecentsToLaunchTargetTaskOrHome();
-            }
-        });
-        mPipShadeView = findViewById(R.id.pip_shade);
+        mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView);
 
-        mPipControlsViewFadeInAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
-                R.anim.tv_pip_controls_fade_in);
-        mPipControlsViewFadeInAnimator.setTarget(mPipControlsView);
-        mPipControlsViewFadeOutAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
-                R.anim.tv_pip_controls_fade_out);
-        mPipControlsViewFadeOutAnimator.setTarget(mPipControlsView);
+        mPipView = findViewById(R.id.pip);
+        // Place mPipView at the PIP bounds for fine tuned focus handling.
+        Rect pipBounds = mPipManager.getPipBounds();
+        LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
+        lp.width = pipBounds.width();
+        lp.height = pipBounds.height();
+        lp.leftMargin = pipBounds.left;
+        lp.topMargin = pipBounds.top;
+        mPipView.setLayoutParams(lp);
+
+        mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback);
 
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -289,7 +312,6 @@
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
 
-        updatePipUI();
         mPipManager.addListener(mPipListener);
     }
 
@@ -321,9 +343,7 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
 
-        mPipManager.onRecentsStarted();
-        // Give focus to the recents row whenever its visible to an user.
-        mRecentsView.requestFocus();
+        updatePipUI();
     }
 
     @Override
@@ -333,10 +353,21 @@
     }
 
     @Override
+    public void onResume() {
+        super.onResume();
+        mPipRecentsOverlayManager.onRecentsResumed();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mPipRecentsOverlayManager.onRecentsPaused();
+    }
+
+    @Override
     protected void onStop() {
         super.onStop();
 
-        mPipManager.onRecentsStopped();
         mIgnoreAltTabRelease = false;
         // Notify that recents is now hidden
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
@@ -480,25 +511,13 @@
 
     private void updatePipUI() {
         if (mPipManager.isPipShown()) {
-            mPipControlsView.setAlpha(0);
-            mPipControlsView.setVisibility(View.VISIBLE);
-            mPipShadeView.setVisibility(View.INVISIBLE);
-            mPipControlsView.setOnChildFocusChangeListener(new View.OnFocusChangeListener() {
-                @Override
-                public void onFocusChange(View v, boolean hasFocus) {
-                    mPipManager.onPipViewFocusChangedInRecents(hasFocus);
-                    if (hasFocus) {
-                        mPipControlsViewFadeInAnimator.start();
-                    } else {
-                        mPipControlsViewFadeOutAnimator.start();
-                    }
-                    mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-                }
-            });
-            mPipShadeView.setVisibility(View.GONE);
+            mPipView.setVisibility(View.VISIBLE);
+            mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
+            mPipView.requestFocus();
         } else {
-            mPipControlsView.setVisibility(View.GONE);
-            mPipShadeView.setVisibility(View.GONE);
+            mPipView.setVisibility(View.GONE);
+            mPipRecentsOverlayManager.removePipRecentsOverlayView();
+            mRecentsFocusAnimationHolder.reset();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java
new file mode 100644
index 0000000..864540c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java
@@ -0,0 +1,63 @@
+/*
+ * 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.tv.animations;
+
+import android.content.res.Resources;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+/**
+ * Collections of Recents row's animation depending on the PIP's focus.
+ */
+public class FocusAnimationHolder {
+    private final float DIM_ALPHA = 0.5f;
+
+    private View mRecentsRowView;
+    private int mCardYDelta;
+    private long mDuration;
+
+    public FocusAnimationHolder(View recentsRowView) {
+        mRecentsRowView = recentsRowView;
+
+        Resources res = recentsRowView.getResources();
+        mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
+        mDuration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
+    }
+
+    public void startFocusGainAnimation() {
+        mRecentsRowView.animate()
+                .setDuration(mDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(1f)
+                .translationY(0);
+    }
+
+    public void startFocusLoseAnimation() {
+        mRecentsRowView.animate()
+                .setDuration(mDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(DIM_ALPHA)
+                .translationY(mCardYDelta);
+    }
+
+    public void reset() {
+        mRecentsRowView.setTransitionAlpha(1f);
+        mRecentsRowView.setTranslationY(0);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index da2f721..e0d0486 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -59,7 +59,6 @@
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
@@ -344,10 +343,10 @@
 
         if (RecentsDebugFlags.Static.EnableStackActionButton) {
             // Measure the stack action button within the constraints of the space above the stack
-            Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
+            Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
             measureChild(mStackActionButton,
-                    MeasureSpec.makeMeasureSpec(actionButtonRect.width(), MeasureSpec.AT_MOST),
-                    MeasureSpec.makeMeasureSpec(actionButtonRect.height(), MeasureSpec.AT_MOST));
+                    MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
+                    MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
         }
 
         setMeasuredDimension(width, height);
@@ -376,16 +375,9 @@
         if (RecentsDebugFlags.Static.EnableStackActionButton) {
             // Layout the stack action button such that its drawable is start-aligned with the
             // stack, vertically centered in the available space above the stack
-            Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
-            int buttonLeft = isLayoutRtl()
-                    ? actionButtonRect.right + mStackActionButton.getPaddingStart()
-                    - mStackActionButton.getMeasuredWidth()
-                    : actionButtonRect.left - mStackActionButton.getPaddingStart();
-            int buttonTop = actionButtonRect.top +
-                    (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
-            mStackActionButton.layout(buttonLeft, buttonTop,
-                    buttonLeft + mStackActionButton.getMeasuredWidth(),
-                    buttonTop + mStackActionButton.getMeasuredHeight());
+            Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+            mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
+                    buttonBounds.bottom);
         }
 
         if (mAwaitingFirstLayout) {
@@ -479,6 +471,17 @@
                     false /* isDefaultDockState */, -1, true /* animateAlpha */,
                     true /* animateBounds */);
         }
+        if (mStackActionButton != null) {
+            event.addPostAnimationCallback(new Runnable() {
+                @Override
+                public void run() {
+                    // Move the clear all button to its new position
+                    Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+                    mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top,
+                            buttonBounds.right, buttonBounds.bottom);
+                }
+            });
+        }
     }
 
     public final void onBusEvent(final DragEndEvent event) {
@@ -726,13 +729,31 @@
      */
     private void animateBackgroundScrim(float alpha, int duration) {
         Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
-        int alphaInt = (int) (alpha * 255);
+        // Calculate the absolute alpha to animate from
+        int fromAlpha = (int) ((mBackgroundScrim.getAlpha() / (DEFAULT_SCRIM_ALPHA * 255)) * 255);
+        int toAlpha = (int) (alpha * 255);
         mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA,
-                mBackgroundScrim.getAlpha(), alphaInt);
+                fromAlpha, toAlpha);
         mBackgroundScrimAnimator.setDuration(duration);
-        mBackgroundScrimAnimator.setInterpolator(alphaInt > mBackgroundScrim.getAlpha()
-                ? Interpolators.ALPHA_OUT
-                : Interpolators.ALPHA_IN);
+        mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha
+                ? Interpolators.ALPHA_IN
+                : Interpolators.ALPHA_OUT);
         mBackgroundScrimAnimator.start();
     }
+
+    /**
+     * @return the bounds of the stack action button.
+     */
+    private Rect getStackActionButtonBoundsFromStackLayout() {
+        Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect);
+        int left = isLayoutRtl()
+                ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
+                : actionButtonRect.right + mStackActionButton.getPaddingRight()
+                        - mStackActionButton.getMeasuredWidth();
+        int top = actionButtonRect.top +
+                (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
+        actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
+                top + mStackActionButton.getMeasuredHeight());
+        return actionButtonRect;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 13ad9a5..07a1d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -16,40 +16,58 @@
 
 package com.android.systemui.recents.views;
 
-import android.app.Activity;
 import android.content.Context;
 import android.view.View;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 
 /** Manages the scrims for the various system bars. */
 public class SystemBarScrimViews {
 
-    Context mContext;
+    private static final int DEFAULT_ANIMATION_DURATION = 150;
 
-    View mNavBarScrimView;
+    private Context mContext;
 
-    boolean mHasNavBarScrim;
-    boolean mShouldAnimateNavBarScrim;
+    private View mNavBarScrimView;
 
-    int mNavBarScrimEnterDuration;
+    private boolean mHasNavBarScrim;
+    private boolean mShouldAnimateNavBarScrim;
 
-    public SystemBarScrimViews(Activity activity) {
+    private int mNavBarScrimEnterDuration;
+
+    public SystemBarScrimViews(RecentsActivity activity) {
         mContext = activity;
         mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
+        mNavBarScrimView.forceHasOverlappingRendering(false);
         mNavBarScrimEnterDuration = activity.getResources().getInteger(
                 R.integer.recents_nav_bar_scrim_enter_duration);
     }
 
     /**
+     * Updates the nav bar scrim.
+     */
+    public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
+            AnimationProps animation) {
+        prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
+        if (animateNavBarScrim && animation != null) {
+            animateNavBarScrimVisibility(true, animation);
+        }
+    }
+
+    /**
      * Prepares the scrim views for animating when entering Recents. This will be called before
      * the first draw, unless we are updating the scrim on configuration change.
      */
-    public void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
+    private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
         mHasNavBarScrim = hasNavBarScrim;
         mShouldAnimateNavBarScrim = animateNavBarScrim;
 
@@ -60,7 +78,7 @@
     /**
      * Animates the nav bar scrim visibility.
      */
-    public void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
+    private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
         int toY = 0;
         if (visible) {
             mNavBarScrimView.setVisibility(View.VISIBLE);
@@ -79,6 +97,14 @@
         }
     }
 
+    /**
+     * @return Whether to show the nav bar scrim.
+     */
+    private boolean isNavBarScrimRequired(boolean hasStackTasks) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        return hasStackTasks && !ssp.hasTransposedNavBar() && !ssp.hasDockedTask();
+    }
+
     /**** EventBus events ****/
 
     /**
@@ -101,21 +127,48 @@
      */
     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
         if (mHasNavBarScrim) {
-            AnimationProps animation = new AnimationProps()
-                    .setDuration(AnimationProps.BOUNDS,
-                            TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
-                    .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+            AnimationProps animation = createBoundsAnimation(
+                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
             animateNavBarScrimVisibility(false, animation);
         }
     }
 
     public final void onBusEvent(DismissAllTaskViewsEvent event) {
         if (mHasNavBarScrim) {
-            AnimationProps animation = new AnimationProps()
-                    .setDuration(AnimationProps.BOUNDS,
-                            TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
-                    .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+            AnimationProps animation = createBoundsAnimation(
+                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
             animateNavBarScrimVisibility(false, animation);
         }
     }
+
+    public final void onBusEvent(ConfigurationChangedEvent event) {
+        animateScrimToCurrentNavBarState(event.hasStackTasks);
+    }
+
+    public final void onBusEvent(MultiWindowStateChangedEvent event) {
+        animateScrimToCurrentNavBarState(event.hasStackTasks);
+    }
+
+    /**
+     * Animates the scrim to match the state of the current nav bar.
+     */
+    private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
+        boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
+        if (mHasNavBarScrim != hasNavBarScrim) {
+            AnimationProps animation = hasNavBarScrim
+                    ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
+                    : AnimationProps.IMMEDIATE;
+            animateNavBarScrimVisibility(hasNavBarScrim, animation);
+        }
+        mHasNavBarScrim = hasNavBarScrim;
+    }
+
+    /**
+     * @return a default animation to aniamte the bounds of the scrim.
+     */
+    private AnimationProps createBoundsAnimation(int duration) {
+        return new AnimationProps()
+                .setDuration(AnimationProps.BOUNDS, duration)
+                .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 2508304..9eab0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -537,11 +537,13 @@
             } else {
                 mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
             }
+            mInitialNormX = null;
         } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
             // If there is one stack task, ignore the min/max/initial scroll positions
             mMinScrollP = 0;
             mMaxScrollP = 0;
             mInitialScrollP = 0;
+            mInitialNormX = null;
         } else {
             // Set the max scroll to be the point where the front most task is visible with the
             // stack bottom offset
@@ -803,8 +805,9 @@
     public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
             TaskViewTransform transformOut, TaskViewTransform frontTransform) {
         Rect windowRect = Recents.getSystemServices().getWindowRect();
-        TaskViewTransform transform = getStackTransform(task, stackScroll, transformOut,
-                frontTransform);
+        TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
+                transformOut, frontTransform, true /* forceUpdate */,
+                false /* ignoreTaskOverrides */);
         transform.rect.offset(windowRect.left, windowRect.top);
         return transform;
     }
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 ddea4d9..a2f61c2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -267,9 +267,9 @@
         lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
         lp.setMarginStart(mHeaderBarHeight);
-        lp.rightMargin = mMoveTaskButton != null
+        lp.setMarginEnd(mMoveTaskButton != null
                 ? 2 * mHeaderBarHeight
-                : mHeaderBarHeight;
+                : mHeaderBarHeight);
         title.setLayoutParams(lp);
         if (secondaryButton != null) {
             lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 3eeabc7..e5ac0d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -141,9 +141,9 @@
             return;
         }
 
+        int viewWidth = mTaskViewRect.width();
+        int viewHeight = mTaskViewRect.height();
         if (mBitmapShader != null) {
-            int viewWidth = mTaskViewRect.width();
-            int viewHeight = mTaskViewRect.height();
 
             // We are drawing the thumbnail in the same orientation, so just fit the width
             int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
@@ -180,6 +180,9 @@
 
                 canvas.restoreToCount(count);
             }
+        } else {
+            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+                    mBgFillPaint);
         }
     }
 
@@ -319,12 +322,12 @@
         mDisabledInSafeMode = disabledInSafeMode;
         if (t.thumbnail != null) {
             setThumbnail(t.thumbnail, thumbnailInfo);
-            if (t.colorBackground != 0) {
-                mBgFillPaint.setColor(t.colorBackground);
-            }
         } else {
             setThumbnail(null, null);
         }
+        if (t.colorBackground != 0) {
+            mBgFillPaint.setColor(t.colorBackground);
+        }
     }
 
     /** Unbinds the thumbnail view from the task */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 0e21517..4ed6426 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -19,7 +19,9 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
 import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -347,7 +349,7 @@
                 }, afterKeyguardGone);
                 return true;
             } else {
-                return super.onClickHandler(view, pendingIntent, fillInIntent);
+                return superOnClickHandler(view, pendingIntent, fillInIntent);
             }
         }
 
@@ -384,7 +386,8 @@
 
         private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
                 Intent fillInIntent) {
-            return super.onClickHandler(view, pendingIntent, fillInIntent);
+            return super.onClickHandler(view, pendingIntent, fillInIntent,
+                    StackId.FULLSCREEN_WORKSPACE_STACK_ID);
         }
 
         private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
@@ -990,7 +993,7 @@
                             }
                             TaskStackBuilder.create(mContext)
                                     .addNextIntentWithParentStack(intent)
-                                    .startActivities(null,
+                                    .startActivities(getActivityOptions(),
                                             new UserHandle(UserHandle.getUserId(appUid)));
                             overrideActivityPendingAppTransition(keyguardShowing);
                         } catch (RemoteException e) {
@@ -1740,7 +1743,7 @@
                         } catch (RemoteException e) {
                         }
                         try {
-                            intent.send();
+                            intent.send(null, 0, null, null, null, null, getActivityOptions());
                         } catch (PendingIntent.CanceledException e) {
                             // the stack trace isn't very helpful here.
                             // Just log the exception message.
@@ -1848,7 +1851,8 @@
                                     }
                                 }
                                 try {
-                                    intent.send();
+                                    intent.send(null, 0, null, null, null, null,
+                                            getActivityOptions());
                                 } catch (PendingIntent.CanceledException e) {
                                     // the stack trace isn't very helpful here.
                                     // Just log the exception message.
@@ -1924,6 +1928,14 @@
         }
     }
 
+    protected Bundle getActivityOptions() {
+        // Anything launched from the notification shade should always go into the
+        // fullscreen stack.
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+        return options.toBundle();
+    }
+
     protected void visibilityChanged(boolean visible) {
         if (mVisible != visible) {
             mVisible = visible;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 8fe60a0..2b365dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -245,7 +245,57 @@
                         systemGroup.addItem(new KeyboardShortcutInfo(
                                 mContext.getString(R.string.keyboard_shortcut_group_system_recents),
                                 KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
+                        systemGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_system_notifications),
+                                KeyEvent.KEYCODE_N, KeyEvent.META_META_ON));
+                        systemGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_system_shortcuts_helper),
+                                KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON));
+                        systemGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_system_switch_input),
+                                KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON));
                         result.add(systemGroup);
+
+                        KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
+                                mContext.getString(R.string.keyboard_shortcut_group_applications),
+                                true);
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_assist),
+                                KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_browser),
+                                KeyEvent.KEYCODE_B, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_contacts),
+                                KeyEvent.KEYCODE_C, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_email),
+                                KeyEvent.KEYCODE_E, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_im),
+                                KeyEvent.KEYCODE_T, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_music),
+                                KeyEvent.KEYCODE_P, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_youtube),
+                                KeyEvent.KEYCODE_Y, KeyEvent.META_META_ON));
+                        applicationGroup.addItem(new KeyboardShortcutInfo(
+                                mContext.getString(
+                                        R.string.keyboard_shortcut_group_applications_calendar),
+                                KeyEvent.KEYCODE_L, KeyEvent.META_META_ON));
+                        result.add(applicationGroup);
+
                         showKeyboardShortcutsDialog(result);
                     }
                 }, deviceId);
@@ -354,11 +404,15 @@
             return null;
         }
         String displayLabelString;
-        if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
+        if (info.getBaseCharacter() > Character.MIN_VALUE) {
             displayLabelString = String.valueOf(info.getBaseCharacter());
         } else if (SPECIAL_CHARACTER_NAMES.get(info.getKeycode()) != null) {
             displayLabelString = SPECIAL_CHARACTER_NAMES.get(info.getKeycode());
         } else {
+            // Special case for shortcuts with no base key or keycode.
+            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());
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 4c5c843..c563eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -21,7 +21,9 @@
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
 import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -3091,8 +3093,8 @@
                             null, mContext.getBasePackageName(),
                             intent,
                             intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                            null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
-                            UserHandle.CURRENT.getIdentifier());
+                            null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+                            getActivityOptions(), UserHandle.CURRENT.getIdentifier());
                 } catch (RemoteException e) {
                     Log.w(TAG, "Unable to start activity", e);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
index 15ad1f1..3f87611 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.tv.pip;
 
-import android.app.Activity;
 import android.content.Context;
 import android.media.session.MediaController;
 import android.media.session.PlaybackState;
 import android.view.View;
+import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View.OnFocusChangeListener;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -40,28 +41,29 @@
 /**
  * A view containing PIP controls including fullscreen, close, and media controls.
  */
-public class PipControlsView extends LinearLayout implements PipManager.Listener {
+public class PipControlsView extends LinearLayout {
     /**
      * An interface to listen user action.
      */
-    public interface Listener {
+    public abstract static interface Listener {
         /**
          * Called when an user clicks close PIP button.
          */
-        void onClosed();
-    }
+        public abstract void onClosed();
+    };
 
-    private final PipManager mPipManager = PipManager.getInstance();
     private MediaController mMediaController;
-    private Listener mListener;
 
-    private View mFullButtonView;
-    private View mFullDescriptionView;
-    private View mPlayPauseView;
-    private ImageView mPlayPauseButtonImageView;
-    private TextView mPlayPauseDescriptionTextView;
-    private View mCloseButtonView;
-    private View mCloseDescriptionView;
+    final PipManager mPipManager = PipManager.getInstance();
+    Listener mListener;
+
+    View mFullButtonView;
+    View mFullDescriptionView;
+    View mPlayPauseView;
+    ImageView mPlayPauseButtonImageView;
+    TextView mPlayPauseDescriptionTextView;
+    View mCloseButtonView;
+    View mCloseDescriptionView;
 
     private boolean mHasFocus;
     private OnFocusChangeListener mOnChildFocusChangeListener;
@@ -73,6 +75,13 @@
         }
     };
 
+    private PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
+        @Override
+        public void onMediaControllerChanged() {
+            updateMediaController();
+        }
+    };
+
     public PipControlsView(Context context) {
         this(context, null, 0, 0);
     }
@@ -87,6 +96,12 @@
 
     public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        LayoutInflater inflater = (LayoutInflater) getContext()
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.tv_pip_controls, this);
+
+        setOrientation(LinearLayout.HORIZONTAL);
+        setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
     }
 
     @Override
@@ -161,13 +176,13 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         updateMediaController();
-        mPipManager.addListener(this);
+        mPipManager.addMediaListener(mPipMediaListener);
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mPipManager.removeListener(this);
+        mPipManager.removeMediaListener(mPipMediaListener);
         if (mMediaController != null) {
             mMediaController.unregisterCallback(mMediaControllerCallback);
         }
@@ -230,24 +245,4 @@
     public void setListener(Listener listener) {
         mListener = listener;
     }
-
-    @Override
-    public void onPipEntered() { }
-
-    @Override
-    public void onPipActivityClosed() { }
-
-    @Override
-    public void onShowPipMenu() { }
-
-    @Override
-    public void onMoveToFullscreen() { }
-
-    @Override
-    public void onMediaControllerChanged() {
-        updateMediaController();
-    }
-
-    @Override
-    public void onPipResizeAboutToStart() { }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 68e0883..b5c1f57 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -62,9 +62,27 @@
 
     private static final int MAX_RUNNING_TASKS_COUNT = 10;
 
+    /**
+     * State when there's no PIP.
+     */
     public static final int STATE_NO_PIP = 0;
+    /**
+     * State when PIP is shown with an overlay message on top of it.
+     * This is used as default PIP state.
+     */
     public static final int STATE_PIP_OVERLAY = 1;
+    /**
+     * State when PIP menu dialog is shown.
+     */
     public static final int STATE_PIP_MENU = 2;
+    /**
+     * State when PIP is shown in Recents.
+     */
+    public static final int STATE_PIP_RECENTS = 3;
+    /**
+     * State when PIP is shown in Recents and it's focused to allow an user to control.
+     */
+    public static final int STATE_PIP_RECENTS_FOCUSED = 4;
 
     private static final int TASK_ID_NO_PIP = -1;
     private static final int INVALID_RESOURCE_TYPE = -1;
@@ -90,11 +108,13 @@
     private int mSuspendPipResizingReason;
 
     private Context mContext;
+    private PipRecentsOverlayManager mPipRecentsOverlayManager;
     private IActivityManager mActivityManager;
     private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
     private final Handler mHandler = new Handler();
     private List<Listener> mListeners = new ArrayList<>();
+    private List<MediaListener> mMediaListeners = new ArrayList<>();
     private Rect mCurrentPipBounds;
     private Rect mPipBounds;
     private Rect mMenuModePipBounds;
@@ -107,9 +127,6 @@
     private MediaController mPipMediaController;
     private boolean mOnboardingShown;
 
-    private boolean mIsRecentsShown;
-    private boolean mIsPipFocusedInRecent;
-
     private final Runnable mResizePinnedStackRunnable = new Runnable() {
         @Override
         public void run() {
@@ -178,6 +195,7 @@
         mOnboardingShown = Prefs.getBoolean(
                 mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
 
+        mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
         mMediaSessionManager =
                 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
     }
@@ -231,7 +249,7 @@
     /**
      * Moves the PIPed activity to the fullscreen and closes PIP system UI.
      */
-    public void movePipToFullscreen() {
+    void movePipToFullscreen() {
         mState = STATE_NO_PIP;
         mPipTaskId = TASK_ID_NO_PIP;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -247,8 +265,11 @@
      */
     private void showPipOverlay() {
         if (DEBUG) Log.d(TAG, "showPipOverlay()");
-        mState = STATE_PIP_OVERLAY;
-        PipOverlayActivity.showPipOverlay(mContext);
+        Intent intent = new Intent(mContext, PipOverlayActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchStackId(PINNED_STACK_ID);
+        mContext.startActivity(intent, options.toBundle());
     }
 
     /**
@@ -279,8 +300,10 @@
      * Resize the Pip to the appropriate size for the input state.
      * @param state In Pip state also used to determine the new size for the Pip.
      */
-    public void resizePinnedStack(int state) {
+    void resizePinnedStack(int state) {
         if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
+        boolean wasRecentsShown =
+                (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED);
         mState = state;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onPipResizeAboutToStart();
@@ -291,7 +314,6 @@
                             mSuspendPipResizingReason);
             return;
         }
-        int animationDurationMs = -1;
         switch (mState) {
             case STATE_NO_PIP:
                 mCurrentPipBounds = null;
@@ -300,25 +322,24 @@
                 mCurrentPipBounds = mMenuModePipBounds;
                 break;
             case STATE_PIP_OVERLAY:
-                if (mIsRecentsShown) {
-                    if (mCurrentPipBounds == mRecentsFocusedPipBounds
-                            || mCurrentPipBounds == mRecentsFocusedPipBounds) {
-                        animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
-                    }
-                    if (mIsPipFocusedInRecent) {
-                        mCurrentPipBounds = mRecentsFocusedPipBounds;
-                    } else {
-                        mCurrentPipBounds = mRecentsPipBounds;
-                    }
-                } else {
-                    mCurrentPipBounds = mPipBounds;
-                }
+                mCurrentPipBounds = mPipBounds;
+                break;
+            case STATE_PIP_RECENTS:
+                mCurrentPipBounds = mRecentsPipBounds;
+                break;
+            case STATE_PIP_RECENTS_FOCUSED:
+                mCurrentPipBounds = mRecentsFocusedPipBounds;
                 break;
             default:
                 mCurrentPipBounds = mPipBounds;
                 break;
         }
         try {
+            int animationDurationMs = -1;
+            if (wasRecentsShown
+                    && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
+                animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+            }
             mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
                     true, true, true, animationDurationMs);
         } catch (RemoteException e) {
@@ -327,67 +348,18 @@
     }
 
     /**
-     * Returns the current PIP bound for activities to sync their UI with PIP.
+     * Returns the default PIP bound.
      */
     public Rect getPipBounds() {
-        return mCurrentPipBounds;
+        return mPipBounds;
     }
 
     /**
-     * Called when Recents is started.
-     * PIPed activity will be resized accordingly and overlay will show available buttons.
+     * Returns the focused PIP bound while Recents is shown.
+     * This is used to place PIP controls in Recents.
      */
-    public void onRecentsStarted() {
-        mIsRecentsShown = true;
-        mIsPipFocusedInRecent = false;
-        if (mState == STATE_NO_PIP) {
-            return;
-        }
-        resizePinnedStack(STATE_PIP_OVERLAY);
-    }
-
-    /**
-     * Called when Recents is stopped.
-     * PIPed activity will be resized accordingly and overlay will hide available buttons.
-     */
-    public void onRecentsStopped() {
-        mIsRecentsShown = false;
-        mIsPipFocusedInRecent = false;
-        if (mState == STATE_NO_PIP) {
-            return;
-        }
-        resizePinnedStack(STATE_PIP_OVERLAY);
-    }
-
-    /**
-     * Returns {@code true} if recents is shown.
-     */
-    boolean isRecentsShown() {
-        return mIsRecentsShown;
-    }
-
-    /**
-     * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
-     * is focused.
-     * This only resizes pinned stack so it looks like it's in Recents.
-     * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
-     */
-    public void onPipViewFocusChangedInRecents(boolean hasFocus) {
-        mIsPipFocusedInRecent = hasFocus;
-        if (mState != STATE_PIP_OVERLAY) {
-            Log.w(TAG, "There is no pinned stack to handle focus change.");
-            return;
-        }
-        resizePinnedStack(STATE_PIP_OVERLAY);
-    }
-
-    /**
-     * Returns {@code true} if the PIP view in
-     * {@link com.android.systemui.recents.tv.RecentsTvActivity} is focused in Recents.
-     * This API is valid only when {@link isRecentsShown()} returns {@code true}.
-     */
-    boolean isPipViewFocusdInRecents() {
-        return mIsPipFocusedInRecent;
+    public Rect getRecentsFocusedPipBounds() {
+        return mRecentsFocusedPipBounds;
     }
 
     /**
@@ -396,6 +368,10 @@
      */
     private void showPipMenu() {
         if (DEBUG) Log.d(TAG, "showPipMenu()");
+        if (mPipRecentsOverlayManager.isRecentsShown()) {
+            if (DEBUG) Log.d(TAG, "Ignore showing PIP menu");
+            return;
+        }
         mState = STATE_PIP_MENU;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onShowPipMenu();
@@ -405,14 +381,34 @@
         mContext.startActivity(intent);
     }
 
+    /**
+     * Adds a {@link Listener} to PipManager.
+     */
     public void addListener(Listener listener) {
         mListeners.add(listener);
     }
 
+    /**
+     * Removes a {@link Listener} from PipManager.
+     */
     public void removeListener(Listener listener) {
         mListeners.remove(listener);
     }
 
+    /**
+     * Adds a {@link MediaListener} to PipManager.
+     */
+    public void addMediaListener(MediaListener listener) {
+        mMediaListeners.add(listener);
+    }
+
+    /**
+     * Removes a {@link MediaListener} from PipManager.
+     */
+    public void removeMediaListener(MediaListener listener) {
+        mMediaListeners.remove(listener);
+    }
+
     private void launchPipOnboardingActivityIfNeeded() {
         if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
             mOnboardingShown = true;
@@ -485,8 +481,8 @@
         }
         if (mPipMediaController != mediaController) {
             mPipMediaController = mediaController;
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onMediaControllerChanged();
+            for (int i = mMediaListeners.size() - 1; i >= 0; i--) {
+                mMediaListeners.get(i).onMediaControllerChanged();
             }
             if (mPipMediaController == null) {
                 mHandler.postDelayed(mClosePipRunnable,
@@ -530,7 +526,7 @@
         return PLAYBACK_STATE_UNAVAILABLE;
     }
 
-    TaskStackListener mTaskStackListener = new TaskStackListener() {
+    private TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
             if (mState != STATE_NO_PIP) {
@@ -582,10 +578,10 @@
             mMediaSessionManager.addOnActiveSessionsChangedListener(
                     mActiveMediaSessionListener, null);
             updateMediaController(mMediaSessionManager.getActiveSessions(null));
-            if (mIsRecentsShown) {
+            if (mPipRecentsOverlayManager.isRecentsShown()) {
                 // If an activity becomes PIPed again after the fullscreen, the Recents is shown
                 // behind so we need to resize the pinned stack and show the correct overlay.
-                resizePinnedStack(STATE_PIP_OVERLAY);
+                resizePinnedStack(STATE_PIP_RECENTS);
             }
             for (int i = mListeners.size() - 1; i >= 0; i--) {
                 mListeners.get(i).onPipEntered();
@@ -604,7 +600,18 @@
             if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
             switch (mState) {
                 case STATE_PIP_OVERLAY:
-                    showPipOverlay();
+                    if (!mPipRecentsOverlayManager.isRecentsShown()) {
+                        showPipOverlay();
+                        break;
+                    } else {
+                        // This happens only if an activity is PIPed after the Recents is shown.
+                        // See {@link PipRecentsOverlayManager.requestFocus} for more details.
+                        resizePinnedStack(mState);
+                        break;
+                    }
+                case STATE_PIP_RECENTS:
+                case STATE_PIP_RECENTS_FOCUSED:
+                    mPipRecentsOverlayManager.addPipRecentsOverlayView();
                     break;
                 case STATE_PIP_MENU:
                     showPipMenu();
@@ -621,7 +628,7 @@
          * Invoked when an activity is pinned and PIP manager is set corresponding information.
          * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
          * because there's no guarantee for the PIP manager be return relavent information
-         * correctly. (e.g. {@link isPipShown}, {@link getPipBounds})
+         * correctly. (e.g. {@link isPipShown}).
          */
         void onPipEntered();
         /** Invoked when a PIPed activity is closed. */
@@ -632,6 +639,12 @@
         void onMoveToFullscreen();
         /** Invoked when we are above to start resizing the Pip. */
         void onPipResizeAboutToStart();
+    }
+
+    /**
+     * A listener interface to receive change in PIP's media controller
+     */
+    public interface MediaListener {
         /** Invoked when the MediaController on PIPed activity is changed. */
         void onMediaControllerChanged();
     }
@@ -645,4 +658,11 @@
         }
         return sPipManager;
     }
+
+    /**
+     * Gets an instance of {@link PipRecentsOverlayManager}.
+     */
+    public PipRecentsOverlayManager getPipRecentsOverlayManager() {
+        return mPipRecentsOverlayManager;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index ea9275f..c54e73a 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -20,12 +20,6 @@
 import android.os.Bundle;
 
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.Recents;
-
-import static android.content.pm.PackageManager.FEATURE_LEANBACK;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
 /**
  * Activity to show the PIP menu to control PIP.
@@ -36,7 +30,6 @@
     private final PipManager mPipManager = PipManager.getInstance();
 
     private PipControlsView mPipControlsView;
-    private boolean mPipMovedToFullscreen;
 
     @Override
     protected void onCreate(Bundle bundle) {
@@ -47,17 +40,10 @@
         mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
     }
 
-    private void restorePipAndFinish() {
-        if (!mPipMovedToFullscreen) {
-            mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
-        }
-        finish();
-    }
-
     @Override
     public void onPause() {
         super.onPause();
-        restorePipAndFinish();
+        finish();
     }
 
     @Override
@@ -69,11 +55,6 @@
     }
 
     @Override
-    public void onBackPressed() {
-        restorePipAndFinish();
-    }
-
-    @Override
     public void onPipEntered() { }
 
     @Override
@@ -86,31 +67,13 @@
 
     @Override
     public void onMoveToFullscreen() {
-        mPipMovedToFullscreen = true;
         finish();
     }
 
     @Override
-    public void onMediaControllerChanged() { }
-
-    @Override
     public void onPipResizeAboutToStart() {
         finish();
         mPipManager.suspendPipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
     }
-
-    @Override
-    public void finish() {
-        super.finish();
-        if (mPipManager.isRecentsShown() && !mPipMovedToFullscreen) {
-            SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
-            for (int i = services.length - 1; i >= 0; i--) {
-                if (services[i] instanceof Recents) {
-                    ((Recents) services[i]).showRecents(false, null);
-                    break;
-                }
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index 79daf3d..86ceff4 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -86,7 +86,4 @@
 
     @Override
     public void onPipResizeAboutToStart() { }
-
-    @Override
-    public void onMediaControllerChanged() { }
 }
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 12cb4cd..5472ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -35,14 +35,6 @@
 public class PipOverlayActivity extends Activity implements PipManager.Listener {
     private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
 
-    /**
-     * The single instance of PipOverlayActivity to prevent it from restarting.
-     * Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is
-     * restarted. It's because the activity may be started by the Launcher or an intent again,
-     * but we don't want do so for the PipOverlayActivity.
-     */
-    private static PipOverlayActivity sPipOverlayActivity;
-
     private final PipManager mPipManager = PipManager.getInstance();
     private final Handler mHandler = new Handler();
     private View mGuideOverlayView;
@@ -54,47 +46,17 @@
         }
     };
 
-    /**
-     * Launches the PIP overlay. This should be only called on the main thread.
-     */
-    public static void showPipOverlay(Context context) {
-        if (sPipOverlayActivity == null) {
-            Intent intent = new Intent(context, PipOverlayActivity.class);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            final ActivityOptions options = ActivityOptions.makeBasic();
-            options.setLaunchStackId(PINNED_STACK_ID);
-            context.startActivity(intent, options.toBundle());
-        }
-    }
-
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_overlay);
         mGuideOverlayView = findViewById(R.id.guide_overlay);
-        mGuideButtonsView = findViewById(R.id.guide_buttons);
-        mGuideButtonPlayPauseImageView = (ImageView) findViewById(R.id.guide_button_play_pause);
         mPipManager.addListener(this);
-
-        sPipOverlayActivity = this;
     }
 
     @Override
     protected void onResume() {
         super.onResume();
-        // TODO: Implement animation for this
-        if (mPipManager.isRecentsShown()) {
-            mGuideOverlayView.setVisibility(View.GONE);
-            if (mPipManager.isPipViewFocusdInRecents()) {
-                mGuideButtonsView.setVisibility(View.GONE);
-            } else {
-                mGuideButtonsView.setVisibility(View.VISIBLE);
-                updateGuideButtonsView();
-            }
-        } else {
-            mGuideOverlayView.setVisibility(View.VISIBLE);
-            mGuideButtonsView.setVisibility(View.GONE);
-        }
         mHandler.removeCallbacks(mHideGuideOverlayRunnable);
         mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
     }
@@ -109,7 +71,6 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        sPipOverlayActivity = null;
         mHandler.removeCallbacksAndMessages(null);
         mPipManager.removeListener(this);
         mPipManager.resumePipResizing(
@@ -140,32 +101,4 @@
         mPipManager.suspendPipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
     }
-
-    @Override
-    public void onMediaControllerChanged() {
-        updateGuideButtonsView();
-    }
-
-    @Override
-    public void finish() {
-        sPipOverlayActivity = null;
-        super.finish();
-    }
-
-    private void updateGuideButtonsView() {
-        switch (mPipManager.getPlaybackState()) {
-            case PipManager.PLAYBACK_STATE_PLAYING:
-                mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
-                mGuideButtonPlayPauseImageView.setImageResource(R.drawable.ic_pause_white_24dp);
-                break;
-            case PipManager.PLAYBACK_STATE_PAUSED:
-                mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
-                mGuideButtonPlayPauseImageView.setImageResource(
-                        R.drawable.ic_play_arrow_white_24dp);
-                break;
-            case PipManager.PLAYBACK_STATE_UNAVAILABLE:
-                mGuideButtonPlayPauseImageView.setVisibility(View.GONE);
-                break;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
new file mode 100644
index 0000000..8b8c105
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv.pip;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.systemui.R;
+
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
+
+/**
+ * An extended version of {@link PipControlsView} that supports animation in Recents.
+ */
+public class PipRecentsControlsView extends PipControlsView {
+    /**
+     * An interface to listen user action.
+     */
+    public interface Listener extends PipControlsView.Listener {
+        /**
+         * Called when an user presses BACK key and up.
+         */
+        abstract void onBackPressed();
+    }
+
+    private AnimatorSet mFocusGainAnimatorSet;
+    private AnimatorSet mFocusLoseAnimatorSet;
+
+    public PipRecentsControlsView(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public PipRecentsControlsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public PipRecentsControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public PipRecentsControlsView(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+
+        int buttonsFocusGainAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_gain_animation;
+        int textFocusGainAnim = R.anim.tv_pip_controls_text_in_recents_focus_gain_animation;
+        mFocusGainAnimatorSet = new AnimatorSet();
+        mFocusGainAnimatorSet.playTogether(
+                loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_gain_animation),
+                loadAnimator(mFullButtonView,buttonsFocusGainAnim),
+                loadAnimator(mPlayPauseButtonImageView, buttonsFocusGainAnim),
+                loadAnimator(mCloseButtonView, buttonsFocusGainAnim),
+                loadAnimator(mFullDescriptionView, textFocusGainAnim),
+                loadAnimator(mPlayPauseDescriptionTextView, textFocusGainAnim),
+                loadAnimator(mCloseDescriptionView, textFocusGainAnim));
+
+        int buttonsFocusLoseAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_lose_animation;
+        int textFocusLoseAnim = R.anim.tv_pip_controls_text_in_recents_focus_lose_animation;
+        mFocusLoseAnimatorSet = new AnimatorSet();
+        mFocusLoseAnimatorSet.playTogether(
+                loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_lose_animation),
+                loadAnimator(mFullButtonView, buttonsFocusLoseAnim),
+                loadAnimator(mPlayPauseButtonImageView, buttonsFocusLoseAnim),
+                loadAnimator(mCloseButtonView, buttonsFocusLoseAnim),
+                loadAnimator(mFullDescriptionView, textFocusLoseAnim),
+                loadAnimator(mPlayPauseDescriptionTextView, textFocusLoseAnim),
+                loadAnimator(mCloseDescriptionView, textFocusLoseAnim));
+
+        Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
+        int pipControlsMarginTop = getContext().getResources().getDimensionPixelSize(
+                R.dimen.recents_tv_pip_controls_margin_top);
+        setPadding(0, pipBounds.bottom + pipControlsMarginTop, 0, 0);
+    }
+
+    private Animator loadAnimator(View view, int animatorResId) {
+        Animator animator = AnimatorInflater.loadAnimator(getContext(), animatorResId);
+        animator.setTarget(view);
+        return animator;
+    }
+
+    /**
+     * Starts focus gaining animation.
+     */
+    public void startFocusGainAnimation() {
+        if (mFocusLoseAnimatorSet.isStarted()) {
+            mFocusLoseAnimatorSet.cancel();
+        }
+        mFocusGainAnimatorSet.start();
+    }
+
+    /**
+     * Starts focus losing animation.
+     */
+    public void startFocusLoseAnimation() {
+        if (mFocusGainAnimatorSet.isStarted()) {
+            mFocusGainAnimatorSet.cancel();
+        }
+        mFocusLoseAnimatorSet.start();
+    }
+
+    /**
+     * Resets the view to the initial state. (i.e. end of the focus gain)
+     */
+    public void reset() {
+        if (mFocusGainAnimatorSet.isStarted()) {
+            mFocusGainAnimatorSet.cancel();
+        }
+        if (mFocusLoseAnimatorSet.isStarted()) {
+            mFocusLoseAnimatorSet.cancel();
+        }
+
+        // Reset to initial state (i.e. end of focused)
+        requestFocus();
+        setTranslationY(0);
+        setScaleXY(mFullButtonView, 1);
+        setScaleXY(mPlayPauseButtonImageView, 1);
+        setScaleXY(mCloseButtonView, 1);
+        mFullDescriptionView.setAlpha(1);
+        mPlayPauseDescriptionTextView.setAlpha(1);
+        mCloseDescriptionView.setAlpha(1);
+    }
+
+    private void setScaleXY(View view, float scale) {
+        view.setScaleX(scale);
+        view.setScaleY(scale);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (!event.isCanceled()
+                && event.getKeyCode() == KeyEvent.KEYCODE_BACK
+                && event.getAction() == KeyEvent.ACTION_UP) {
+            if (mListener != null) {
+                ((PipRecentsControlsView.Listener) mListener).onBackPressed();
+            }
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
new file mode 100644
index 0000000..b90b727
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv.pip;
+
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY;
+import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS;
+import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED;
+
+public class PipRecentsOverlayManager {
+    private static final String TAG = "PipRecentsOverlayManager";
+
+    public interface Callback {
+        void onClosed();
+        void onBackPressed();
+        void onRecentsFocused();
+    }
+
+    private final PipManager mPipManager = PipManager.getInstance();
+    private final WindowManager mWindowManager;
+    private final View mOverlayView;
+    private final PipRecentsControlsView mPipControlsView;
+    private final View mRecentsView;
+
+    private final LayoutParams mPipRecentsControlsViewLayoutParams;
+    private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
+
+    private boolean mIsPipRecentsOverlayShown;
+    private boolean mIsRecentsShown;
+    private boolean mIsPipFocusedInRecent;
+    private Callback mCallback;
+    private PipRecentsControlsView.Listener mPipControlsViewListener =
+            new PipRecentsControlsView.Listener() {
+                @Override
+                public void onClosed() {
+                    if (mCallback != null) {
+                        mCallback.onClosed();
+                    }
+                }
+
+                @Override
+                public void onBackPressed() {
+                    if (mCallback != null) {
+                        mCallback.onBackPressed();
+                    }
+                }
+            };
+
+    PipRecentsOverlayManager(Context context) {
+        mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
+        mPipControlsView = (PipRecentsControlsView) mOverlayView.findViewById(R.id.pip_controls);
+        mRecentsView = mOverlayView.findViewById(R.id.recents);
+        mRecentsView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (hasFocus) {
+                    clearFocus();
+                }
+            }
+        });
+
+        mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                LayoutParams.TYPE_SYSTEM_DIALOG,
+                LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
+                PixelFormat.TRANSLUCENT);
+        mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                LayoutParams.TYPE_SYSTEM_DIALOG,
+                0,
+                PixelFormat.TRANSLUCENT);
+    }
+
+    /**
+     * Add Recents overlay view.
+     * This is expected to be called after the PIP animation is over.
+     */
+    void addPipRecentsOverlayView() {
+        if (mIsPipRecentsOverlayShown) {
+            return;
+        }
+        mIsPipRecentsOverlayShown = true;
+        mIsPipFocusedInRecent = true;
+        mPipControlsView.reset();
+        mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+    }
+
+    /**
+     * Remove Recents overlay view.
+     * This should be called when Recents or PIP is closed.
+     */
+    public void removePipRecentsOverlayView() {
+        if (!mIsPipRecentsOverlayShown) {
+            return;
+        }
+        mWindowManager.removeView(mOverlayView);
+        mIsPipRecentsOverlayShown = false;
+    }
+
+    /**
+     * Request focus to the PIP Recents overlay.
+     * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
+     * is focused.
+     * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
+     * @param hasRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
+     */
+    public void requestFocus(boolean hasRecentsFocusable) {
+        if (!mIsRecentsShown || mIsPipFocusedInRecent) {
+            return;
+        }
+        mIsPipFocusedInRecent = true;
+        mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+
+        mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+        mPipControlsView.requestFocus();
+        mPipControlsView.startFocusGainAnimation();
+        mRecentsView.setVisibility(hasRecentsFocusable ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Request focus to the PIP Recents overlay.
+     * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
+     * is focused.
+     * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
+     */
+    private void clearFocus() {
+        if (!mIsRecentsShown || !mIsPipFocusedInRecent) {
+            return;
+        }
+        mIsPipFocusedInRecent = false;
+        mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
+        mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
+        mPipControlsView.startFocusLoseAnimation();
+        if (mCallback != null) {
+            mCallback.onRecentsFocused();
+        }
+    }
+
+    public void setCallback(Callback listener) {
+        mCallback = listener;
+        mPipControlsView.setListener(mCallback != null ? mPipControlsViewListener : null);
+    }
+
+    /**
+     * Called when Recents is resumed.
+     * PIPed activity will be resized accordingly and overlay will show available buttons.
+     */
+    public void onRecentsResumed() {
+        if (!mPipManager.isPipShown()) {
+            return;
+        }
+        mIsRecentsShown = true;
+        mIsPipFocusedInRecent = true;
+        mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+        // Overlay view will be added after the resize animation ends, if any.
+    }
+
+    /**
+     * Called when Recents is paused.
+     * PIPed activity will be resized accordingly and overlay will hide available buttons.
+     */
+    public void onRecentsPaused() {
+        mIsRecentsShown = false;
+        mIsPipFocusedInRecent = false;
+        removePipRecentsOverlayView();
+
+        if (mPipManager.isPipShown()) {
+            mPipManager.resizePinnedStack(STATE_PIP_OVERLAY);
+        }
+    }
+
+    /**
+     * Returns {@code true} if recents is shown.
+     */
+    boolean isRecentsShown() {
+        return mIsRecentsShown;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index f432808..c6e4356 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -35,11 +35,11 @@
     private static final Typeface MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL);
 
     private final Context mContext;
-    private final LayoutInflater mInflater;
+    protected final LayoutInflater mInflater;
     private final SpTexts mSpTexts;
 
     private Callback mCallback;
-    private Object mSelectedValue;
+    protected Object mSelectedValue;
 
     public SegmentedButtons(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -65,13 +65,21 @@
             final Object tag = c.getTag();
             final boolean selected = Objects.equals(mSelectedValue, tag);
             c.setSelected(selected);
-            c.setTypeface(selected ? MEDIUM : REGULAR);
+            setSelectedStyle(c, selected);
         }
         fireOnSelected(fromClick);
     }
 
+    protected void setSelectedStyle(TextView textView, boolean selected) {
+        textView.setTypeface(selected ? MEDIUM : REGULAR);
+    }
+
+    public Button inflateButton() {
+        return (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+    }
+
     public void addButton(int labelResId, int contentDescriptionResId, Object value) {
-        final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+        final Button b = inflateButton();
         b.setTag(LABEL_RES_KEY, labelResId);
         b.setText(labelResId);
         b.setContentDescription(getResources().getString(contentDescriptionResId));
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 32e1e6d..08257b6 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -24,7 +24,7 @@
     <application android:label="VpnDialogs"
             android:allowBackup="false" >
         <activity android:name=".ConfirmDialog"
-                android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert">
+                android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.DEFAULT"/>
@@ -32,7 +32,7 @@
         </activity>
 
         <activity android:name=".ManageDialog"
-                android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert"
+                android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
                 android:noHistory="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/packages/WallpaperCropper/res/values/styles.xml b/packages/WallpaperCropper/res/values/styles.xml
index 0f9e247..6a56afd 100644
--- a/packages/WallpaperCropper/res/values/styles.xml
+++ b/packages/WallpaperCropper/res/values/styles.xml
@@ -15,7 +15,7 @@
 -->
 
 <resources>
-    <style name="Theme.WallpaperCropper" parent="@*android:style/Theme.Material.DayNight">
+    <style name="Theme.WallpaperCropper" parent="@android:style/Theme.Material.Light">
         <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item>
         <item name="android:windowFullscreen">true</item>
         <item name="android:windowActionBarOverlay">true</item>
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 81f63ae..9ec6e8d 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -380,6 +380,7 @@
             Log.e(RenderScript.LOG_TAG, "Couldn't invoke registerNativeAllocation:" + e);
             throw new RSRuntimeException("Couldn't invoke registerNativeAllocation:" + e);
         }
+        guard.open("destroy");
     }
 
     Allocation(long id, RenderScript rs, Type t, int usage, MipmapControl mips) {
@@ -1915,6 +1916,7 @@
             if (type.getID(rs) == 0) {
                 throw new RSInvalidStateException("Bad Type");
             }
+            // TODO: What if there is an exception after this? The native allocation would leak.
             long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
             if (id == 0) {
                 throw new RSRuntimeException("Allocation creation failed.");
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index 1372ab7..f95af16 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import dalvik.system.CloseGuard;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
@@ -69,6 +70,7 @@
     }
 
     private long mID;
+    final CloseGuard guard = CloseGuard.get();
     private boolean mDestroyed;
     private String mName;
     RenderScript mRS;
@@ -119,6 +121,7 @@
         }
 
         if (shouldDestroy) {
+            guard.close();
             // must include nObjDestroy in the critical section
             ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
             rlock.lock();
@@ -133,8 +136,14 @@
     }
 
     protected void finalize() throws Throwable {
-        helpDestroy();
-        super.finalize();
+        try {
+            if (guard != null) {
+                guard.warnIfOpen();
+            }
+            helpDestroy();
+        } finally {
+            super.finalize();
+        }
     }
 
     /**
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 6efb6d6..50226ac 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -808,6 +808,7 @@
             mSize += mElements[ct].mSize * mArraySizes[ct];
         }
         updateVisibleSubElements();
+        guard.open("destroy");
     }
 
     Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
@@ -827,6 +828,7 @@
         mKind = dk;
         mNormalized = norm;
         mVectorSize = size;
+        guard.open("destroy");
     }
 
     Element(long id, RenderScript rs) {
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 9d8f162..278d309 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -170,6 +170,7 @@
     FileA3D(long id, RenderScript rs, InputStream stream) {
         super(id, rs);
         mInputStream = stream;
+        guard.open("destroy");
     }
 
     private void initEntries() {
diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index 4318b9d..d5ca31e 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -150,6 +150,7 @@
 
     Font(long id, RenderScript rs) {
         super(id, rs);
+        guard.open("destroy");
     }
 
     /**
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 13c8e1c..9e4f905 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -91,6 +91,7 @@
 
     Mesh(long id, RenderScript rs) {
         super(id, rs);
+        guard.open("destroy");
     }
 
     /**
diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java
index 3eb9b75..772021c 100644
--- a/rs/java/android/renderscript/Program.java
+++ b/rs/java/android/renderscript/Program.java
@@ -76,6 +76,7 @@
 
     Program(long id, RenderScript rs) {
         super(id, rs);
+        guard.open("destroy");
     }
 
     /**
diff --git a/rs/java/android/renderscript/Sampler.java b/rs/java/android/renderscript/Sampler.java
index a4edbb5..5c4bae9 100644
--- a/rs/java/android/renderscript/Sampler.java
+++ b/rs/java/android/renderscript/Sampler.java
@@ -51,6 +51,7 @@
 
     Sampler(long id, RenderScript rs) {
         super(id, rs);
+        guard.open("destroy");
     }
 
     /**
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index f6f93cb..fc3280b 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -41,6 +41,7 @@
             mScript = s;
             mSlot = slot;
             mSig = sig;
+            guard.open("destroy");
         }
     }
 
@@ -118,6 +119,7 @@
             super(id, rs);
             mScript = s;
             mSlot = slot;
+            guard.open("destroy");
         }
     }
 
@@ -357,6 +359,19 @@
         super(id, rs);
 
         mInIdsBuffer = new long[1];
+
+        /* The constructors for the derived classes (including ScriptIntrinsic
+         * derived classes and ScriptC derived classes generated by Slang
+         * reflection) seem to be simple enough, so we just put the guard.open()
+         * call here, rather than in the end of the constructor for the derived
+         * class. This, of course, assumes the derived constructor would not
+         * throw any exception after calling this constructor.
+         *
+         * If new derived classes are added with more complicated constructors
+         * that throw exceptions, this call has to be (duplicated and) moved
+         * to the end of each derived class constructor.
+         */
+        guard.open("destroy");
     }
 
     /**
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 9357c3bb..219f16b 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -148,6 +148,8 @@
                                         fieldIDs, values, sizes, depClosures, depFieldIDs);
 
             setID(id);
+
+            guard.open("destroy");
         }
 
         Closure(RenderScript rs, Script.InvokeID invokeID,
@@ -181,6 +183,8 @@
                                               values, sizes);
 
             setID(id);
+
+            guard.open("destroy");
         }
 
         private void retrieveValueAndDependenceInfo(RenderScript rs,
@@ -382,6 +386,7 @@
 
     ScriptGroup(long id, RenderScript rs) {
         super(id, rs);
+        guard.open("destroy");
     }
 
     ScriptGroup(RenderScript rs, String name, List<Closure> closures,
@@ -398,6 +403,7 @@
         }
         long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs);
         setID(id);
+        guard.open("destroy");
     }
 
     /**
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index dc23785..9252898 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -227,6 +227,7 @@
 
     Type(long id, RenderScript rs) {
         super(id, rs);
+        guard.open("destroy");
     }
 
     @Override
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index fd57af6..c38a89e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -446,8 +446,12 @@
             int[] ops) {
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
+        String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return Collections.emptyList();
+        }
         synchronized (this) {
-            Ops pkgOps = getOpsLocked(uid, packageName, false);
+            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false);
             if (pkgOps == null) {
                 return null;
             }
@@ -465,7 +469,7 @@
 
     private void pruneOp(Op op, int uid, String packageName) {
         if (op.time == 0 && op.rejectTime == 0) {
-            Ops ops = getOpsLocked(uid, packageName, false);
+            Ops ops = getOpsRawLocked(uid, packageName, false);
             if (ops != null) {
                 ops.remove(op.op);
                 if (ops.size() <= 0) {
@@ -879,8 +883,12 @@
     public int checkOperation(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
+        String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
         synchronized (this) {
-            if (isOpRestricted(uid, code, packageName)) {
+            if (isOpRestricted(uid, code, resolvedPackageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -891,7 +899,7 @@
                     return uidMode;
                 }
             }
-            Op op = getOpLocked(code, uid, packageName, false);
+            Op op = getOpLocked(code, uid, resolvedPackageName, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -975,6 +983,7 @@
 
     @Override
     public int checkPackage(int uid, String packageName) {
+        Preconditions.checkNotNull(packageName);
         synchronized (this) {
             if (getOpsRawLocked(uid, packageName, true) != null) {
                 return AppOpsManager.MODE_ALLOWED;
@@ -988,26 +997,39 @@
     public int noteProxyOperation(int code, String proxyPackageName,
             int proxiedUid, String proxiedPackageName) {
         verifyIncomingOp(code);
-        final int proxyMode = noteOperationUnchecked(code, Binder.getCallingUid(),
-                proxyPackageName, -1, null);
+        final int proxyUid = Binder.getCallingUid();
+        String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
+        if (resolveProxyPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+        final int proxyMode = noteOperationUnchecked(code, proxyUid,
+                resolveProxyPackageName, -1, null);
         if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
             return proxyMode;
         }
-        return noteOperationUnchecked(code, proxiedUid, proxiedPackageName,
-                Binder.getCallingUid(), proxyPackageName);
+        String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
+        if (resolveProxiedPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+        return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
+                proxyMode, resolveProxyPackageName);
     }
 
     @Override
     public int noteOperation(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        return noteOperationUnchecked(code, uid, packageName, 0, null);
+        String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+        return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
     }
 
     private int noteOperationUnchecked(int code, int uid, String packageName,
             int proxyUid, String proxyPackageName) {
         synchronized (this) {
-            Ops ops = getOpsLocked(uid, packageName, true);
+            Ops ops = getOpsRawLocked(uid, packageName, true);
             if (ops == null) {
                 if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
@@ -1055,16 +1077,20 @@
     public int startOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
+        String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return  AppOpsManager.MODE_IGNORED;
+        }
         ClientState client = (ClientState)token;
         synchronized (this) {
-            Ops ops = getOpsLocked(uid, packageName, true);
+            Ops ops = getOpsRawLocked(uid, resolvedPackageName, true);
             if (ops == null) {
                 if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
-                        + " package " + packageName);
+                        + " package " + resolvedPackageName);
                 return AppOpsManager.MODE_ERRORED;
             }
             Op op = getOpLocked(ops, code, true);
-            if (isOpRestricted(uid, code, packageName)) {
+            if (isOpRestricted(uid, code, resolvedPackageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1074,7 +1100,7 @@
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
-                            + packageName);
+                            + resolvedPackageName);
                     op.rejectTime = System.currentTimeMillis();
                     return uidMode;
                 }
@@ -1082,12 +1108,13 @@
             final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
             if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
                 if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
-                        + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+                        + switchCode + " (" + code + ") uid " + uid + " package "
+                        + resolvedPackageName);
                 op.rejectTime = System.currentTimeMillis();
                 return switchOp.mode;
             }
             if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
-                    + " package " + packageName);
+                    + " package " + resolvedPackageName);
             if (op.nesting == 0) {
                 op.time = System.currentTimeMillis();
                 op.rejectTime = 0;
@@ -1105,9 +1132,16 @@
     public void finishOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
-        ClientState client = (ClientState)token;
+        String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return;
+        }
+        if (!(token instanceof ClientState)) {
+            return;
+        }
+        ClientState client = (ClientState) token;
         synchronized (this) {
-            Op op = getOpLocked(code, uid, packageName, true);
+            Op op = getOpLocked(code, uid, resolvedPackageName, true);
             if (op == null) {
                 return;
             }
@@ -1123,6 +1157,9 @@
 
     @Override
     public int permissionToOpCode(String permission) {
+        if (permission == null) {
+            return AppOpsManager.OP_NONE;
+        }
         return AppOpsManager.permissionToOpCode(permission);
     }
 
@@ -1172,15 +1209,6 @@
         return uidState;
     }
 
-    private Ops getOpsLocked(int uid, String packageName, boolean edit) {
-        if (uid == 0) {
-            packageName = "root";
-        } else if (uid == Process.SHELL_UID) {
-            packageName = "com.android.shell";
-        }
-        return getOpsRawLocked(uid, packageName, edit);
-    }
-
     private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
@@ -1266,7 +1294,7 @@
     }
 
     private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
-        Ops ops = getOpsLocked(uid, packageName, edit);
+        Ops ops = getOpsRawLocked(uid, packageName, edit);
         if (ops == null) {
             return null;
         }
@@ -1324,7 +1352,7 @@
                 if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
-                        Ops ops = getOpsLocked(uid, packageName, true);
+                        Ops ops = getOpsRawLocked(uid, packageName, true);
                         if ((ops != null) && ops.isPrivileged) {
                             return false;
                         }
@@ -1589,7 +1617,7 @@
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
                         synchronized (this) {
-                            Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
+                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false);
                             // Should always be present as the list of PackageOps is generated
                             // from Ops.
                             if (ops != null) {
@@ -2181,6 +2209,7 @@
     @Override
     public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
         checkSystemUid("setUserRestrictions");
+        Preconditions.checkNotNull(restrictions);
         Preconditions.checkNotNull(token);
         final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle);
         for (int i = 0; i < opRestrictions.length; ++i) {
@@ -2395,6 +2424,17 @@
         }
     }
 
+    private static String resolvePackageName(int uid, String packageName)  {
+        if (uid == 0) {
+            return "root";
+        } else if (uid == Process.SHELL_UID) {
+            return "com.android.shell";
+        } else if (uid == Process.SYSTEM_UID && packageName == null) {
+            return "android";
+        }
+        return packageName;
+    }
+
     private static String[] getPackagesForUid(int uid) {
         String[] packageNames = null;
         try {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 799a763..0a814ab 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -92,6 +92,7 @@
     private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60;
     private static final int MESSAGE_TIMEOUT_BIND = 100;
     private static final int MESSAGE_TIMEOUT_UNBIND = 101;
+    private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200;
     private static final int MESSAGE_USER_SWITCHED = 300;
     private static final int MESSAGE_USER_UNLOCKED = 301;
     private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
@@ -599,8 +600,8 @@
             sendEnableMsg(true);
         }
         return true;
-
     }
+
     public boolean enable() {
         if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
             (!checkIfCallerIsForegroundUser())) {
@@ -763,9 +764,8 @@
             sendEnableMsg(mQuietEnableExternal);
         } else if (!isNameAndAddressSet()) {
             if (DBG) Slog.d(TAG, "Getting adapter name and address");
-            enable();
-            waitForOnOff(true, false);
-            disable(true);
+            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+            mHandler.sendMessage(getMsg);
         }
     }
 
@@ -1076,6 +1076,8 @@
     private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
 
     private class BluetoothHandler extends Handler {
+        boolean mGetNameAddressOnly = false;
+
         public BluetoothHandler(Looper looper) {
             super(looper);
         }
@@ -1084,6 +1086,37 @@
         public void handleMessage(Message msg) {
             if (DBG) Slog.d (TAG, "Message: " + msg.what);
             switch (msg.what) {
+                case MESSAGE_GET_NAME_AND_ADDRESS:
+                    if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
+                    synchronized(mConnection) {
+                        if ((mBluetooth == null) && (!mBinding)) {
+                            if (DBG) Slog.d(TAG, "Binding to service to get name and address");
+                            mGetNameAddressOnly = true;
+                            Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
+                            mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
+                            Intent i = new Intent(IBluetooth.class.getName());
+                            if (!doBind(i, mConnection,
+                                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                                UserHandle.CURRENT)) {
+                                mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+                            } else {
+                                mBinding = true;
+                            }
+                        } else if (mBluetooth != null) {
+                            try {
+                                storeNameAndAddress(mBluetooth.getName(),
+                                                    mBluetooth.getAddress());
+                            } catch (RemoteException re) {
+                                Slog.e(TAG, "Unable to grab names", re);
+                            }
+                            if (mGetNameAddressOnly && !mEnable) {
+                                unbindAndFinish();
+                            }
+                            mGetNameAddressOnly = false;
+                        }
+                    }
+                    break;
+
                 case MESSAGE_ENABLE:
                     if (DBG) {
                         Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
@@ -1177,6 +1210,12 @@
                         mBluetoothBinder = service;
                         mBluetooth = IBluetooth.Stub.asInterface(service);
 
+                        if (!isNameAndAddressSet()) {
+                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+                            mHandler.sendMessage(getMsg);
+                            if (mGetNameAddressOnly) return;
+                        }
+
                         try {
                             boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
                                 Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
@@ -1187,15 +1226,6 @@
                             Slog.e(TAG,"Unable to call configHciSnoopLog", e);
                         }
 
-                        if (!isNameAndAddressSet()) {
-                            try {
-                                storeNameAndAddress(mBluetooth.getName(),
-                                                    mBluetooth.getAddress());
-                            } catch (RemoteException re) {
-                                Slog.e(TAG, "Unable to grab names", re);
-                            }
-                        }
-
                         //Register callback object
                         try {
                             mBluetooth.registerCallback(mBluetoothCallback);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index bb32303..8a0a62a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -782,7 +782,12 @@
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
-            return readUserDataInternal(accounts, account, key);
+            synchronized (accounts.cacheLock) {
+                if (!accountExistsCacheLocked(accounts, account)) {
+                    return null;
+                }
+                return readUserDataInternalLocked(accounts, account, key);
+            }
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -1869,44 +1874,57 @@
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
-            setUserdataInternal(accounts, account, key, value);
+            synchronized (accounts.cacheLock) {
+                if (!accountExistsCacheLocked(accounts, account)) {
+                    return;
+                }
+                setUserdataInternalLocked(accounts, account, key, value);
+            }
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    private void setUserdataInternal(UserAccounts accounts, Account account, String key,
+    private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
+        if (accounts.accountCache.containsKey(account.type)) {
+            for (Account acc : accounts.accountCache.get(account.type)) {
+                if (acc.name.equals(account.name)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
             String value) {
         if (account == null || key == null) {
             return;
         }
-        synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
-            try {
-                long accountId = getAccountIdLocked(db, account);
-                if (accountId < 0) {
+        final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            long accountId = getAccountIdLocked(db, account);
+            if (accountId < 0) {
+                return;
+            }
+            long extrasId = getExtrasIdLocked(db, accountId, key);
+            if (extrasId < 0) {
+                extrasId = insertExtraLocked(db, accountId, key, value);
+                if (extrasId < 0) {
                     return;
                 }
-                long extrasId = getExtrasIdLocked(db, accountId, key);
-                if (extrasId < 0 ) {
-                    extrasId = insertExtraLocked(db, accountId, key, value);
-                    if (extrasId < 0) {
-                        return;
-                    }
-                } else {
-                    ContentValues values = new ContentValues();
-                    values.put(EXTRAS_VALUE, value);
-                    if (1 != db.update(CE_TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
-                        return;
-                    }
-
+            } else {
+                ContentValues values = new ContentValues();
+                values.put(EXTRAS_VALUE, value);
+                if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+                    return;
                 }
-                writeUserDataIntoCacheLocked(accounts, db, account, key, value);
-                db.setTransactionSuccessful();
-            } finally {
-                db.endTransaction();
             }
+            writeUserDataIntoCacheLocked(accounts, db, account, key, value);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
         }
     }
 
@@ -2500,21 +2518,31 @@
                     userId);
             return;
         }
-
         final int pid = Binder.getCallingPid();
         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
         options.putInt(AccountManager.KEY_CALLER_UID, uid);
         options.putInt(AccountManager.KEY_CALLER_PID, pid);
 
+        // Check to see if the Password should be included to the caller.
+        String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
+        boolean isPasswordForwardingAllowed = isPermitted(
+                callerPkg, uid, Manifest.permission.GET_PASSWORD_PRIVILEGED);
+
         int usrId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(usrId);
             logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
                     TABLE_ACCOUNTS, uid);
-            new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
-                    null /* accountName */, false /* authDetailsRequired */,
-                    true /* updateLastAuthenticationTime */) {
+            new StartAccountSession(
+                    accounts,
+                    response,
+                    accountType,
+                    expectActivityLaunch,
+                    null /* accountName */,
+                    false /* authDetailsRequired */,
+                    true /* updateLastAuthenticationTime */,
+                    isPasswordForwardingAllowed) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
@@ -2537,12 +2565,21 @@
     /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
     private abstract class StartAccountSession extends Session {
 
-        public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
-                String accountType, boolean expectActivityLaunch, String accountName,
-                boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
+        private final boolean mIsPasswordForwardingAllowed;
+
+        public StartAccountSession(
+                UserAccounts accounts,
+                IAccountManagerResponse response,
+                String accountType,
+                boolean expectActivityLaunch,
+                String accountName,
+                boolean authDetailsRequired,
+                boolean updateLastAuthenticationTime,
+                boolean isPasswordForwardingAllowed) {
             super(accounts, response, accountType, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
                     updateLastAuthenticationTime);
+            mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
         }
 
         @Override
@@ -2555,6 +2592,10 @@
                 checkKeyIntent(
                         Binder.getCallingUid(),
                         intent);
+                // Omit passwords if the caller isn't permitted to see them.
+                if (!mIsPasswordForwardingAllowed) {
+                    result.remove(AccountManager.KEY_PASSWORD);
+                }
             }
             IAccountManagerResponse response;
             if (mExpectActivityLaunch && result != null
@@ -2901,6 +2942,12 @@
         }
 
         int userId = UserHandle.getCallingUserId();
+
+        // Check to see if the Password should be included to the caller.
+        String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
+        boolean isPasswordForwardingAllowed = isPermitted(
+                callerPkg, uid, Manifest.permission.GET_PASSWORD_PRIVILEGED);
+
         long identityToken = clearCallingIdentity();
         try {
             UserAccounts accounts = getUserAccounts(userId);
@@ -2911,7 +2958,8 @@
                     expectActivityLaunch,
                     account.name,
                     false /* authDetailsRequired */,
-                    true /* updateLastCredentialTime */) {
+                    true /* updateLastCredentialTime */,
+                    isPasswordForwardingAllowed) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
@@ -5262,17 +5310,16 @@
         }
     }
 
-    protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
-        synchronized (accounts.cacheLock) {
-            HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
-            if (userDataForAccount == null) {
-                // need to populate the cache for this account
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-                userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
-                accounts.userDataCache.put(account, userDataForAccount);
-            }
-            return userDataForAccount.get(key);
+    protected String readUserDataInternalLocked(
+            UserAccounts accounts, Account account, String key) {
+        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();
+            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
+            accounts.userDataCache.put(account, userDataForAccount);
         }
+        return userDataForAccount.get(key);
     }
 
     protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a95c874..1da6a20 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9827,7 +9827,8 @@
     public void updateLockTaskPackages(int userId, String[] packages) {
         final int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
-            throw new SecurityException("updateLockTaskPackage called from non-system process");
+            enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+                    "updateLockTaskPackages()");
         }
         synchronized (this) {
             if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
@@ -18044,6 +18045,9 @@
             ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
         int changes = 0;
 
+        if (mWindowManager != null) {
+            mWindowManager.deferSurfaceLayout();
+        }
         if (values != null) {
             Configuration newConfig = new Configuration(mConfiguration);
             changes = newConfig.updateFrom(values);
@@ -18144,6 +18148,20 @@
                             null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
+            // Update the configuration with WM first and check if any of the stacks need to be
+            // resized due to the configuration change. If so, resize the stacks now and do any
+            // relaunches if necessary. This way we don't need to relaunch again below in
+            // ensureActivityConfigurationLocked().
+            if (mWindowManager != null) {
+                final int[] resizedStacks = mWindowManager.setNewConfiguration(mConfiguration);
+                if (resizedStacks != null) {
+                    for (int stackId : resizedStacks) {
+                        final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
+                        mStackSupervisor.resizeStackLocked(
+                                stackId, newBounds, null, null, false, false);
+                    }
+                }
+            }
         }
 
         boolean kept = true;
@@ -18165,11 +18183,9 @@
                         !PRESERVE_WINDOWS);
             }
         }
-
-        if (values != null && mWindowManager != null) {
-            mWindowManager.setNewConfiguration(mConfiguration);
+        if (mWindowManager != null) {
+            mWindowManager.continueSurfaceLayout();
         }
-
         return kept;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d570be9..9be6b43 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -23,8 +23,6 @@
 import android.os.UserHandle;
 import android.util.DebugUtils;
 
-import com.android.internal.util.ArrayUtils;
-
 import java.io.PrintWriter;
 
 class ActivityManagerShellCommand extends ShellCommand {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b297938..4ec1f61 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4574,6 +4574,8 @@
         }
 
         r.configChangeFlags = 0;
+        r.deferRelaunchUntilPaused = false;
+        r.preserveWindowOnDeferredRelaunch = false;
     }
 
     boolean willActivityBeVisibleLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0d70e99..811b48f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2738,12 +2738,21 @@
 
     // Called when WindowManager has finished animating the launchingBehind activity to the back.
     void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
-        r.mLaunchTaskBehind = false;
         final TaskRecord task = r.task;
-        task.setLastThumbnailLocked(task.stack.screenshotActivitiesLocked(r));
+        final ActivityStack stack = task.stack;
+
+        r.mLaunchTaskBehind = false;
+        task.setLastThumbnailLocked(stack.screenshotActivitiesLocked(r));
         mRecentTasks.addLocked(task);
         mService.notifyTaskStackChangedLocked();
         mWindowManager.setAppVisibility(r.appToken, false);
+
+        // When launching tasks behind, update the last active time of the top task after the new
+        // task has been shown briefly
+        final ActivityRecord top = stack.topActivity();
+        if (top != null) {
+            top.task.touchActiveTime();
+        }
     }
 
     void scheduleLaunchTaskBehindComplete(IBinder token) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index b157070..e32d1d1 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -285,6 +285,7 @@
         mCallingPackage = info.packageName;
         setIntent(_intent, info);
         setMinDimensions(info);
+        touchActiveTime();
     }
 
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -315,6 +316,7 @@
         taskType = APPLICATION_ACTIVITY_TYPE;
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         lastTaskDescription = _taskDescription;
+        touchActiveTime();
     }
 
     private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1ed7070..8f8afd5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -103,7 +103,6 @@
     private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
 
     // Brightness animation ramp rate in brightness units per second.
-    private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
     private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40;
 
     private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;
@@ -244,6 +243,9 @@
     private boolean mAppliedDimming;
     private boolean mAppliedLowPower;
 
+    // Brightness ramp rate fast.
+    private final int mBrightnessRampRateFast;
+
     // The controller for the automatic brightness level.
     private AutomaticBrightnessController mAutomaticBrightnessController;
 
@@ -303,6 +305,9 @@
         mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
 
+        mBrightnessRampRateFast = resources.getInteger(
+                com.android.internal.R.integer.config_brightness_ramp_rate_fast);
+
         int lightSensorRate = resources.getInteger(
                 com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
         long brighteningLightDebounce = resources.getInteger(
@@ -698,7 +703,7 @@
         if (!mPendingScreenOff) {
             if (state == Display.STATE_ON || state == Display.STATE_DOZE) {
                 animateScreenBrightness(brightness,
-                        slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
+                        slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : mBrightnessRampRateFast);
             } else {
                 animateScreenBrightness(brightness, 0);
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 9cfb590..d339f69 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -24,7 +24,6 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -35,7 +34,6 @@
 import android.net.NetworkTemplate;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
-import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.util.Log;
@@ -88,12 +86,10 @@
         pw.println("    Adds a UID to the whitelist for restrict background usage.");
         pw.println("  add restrict-background-blacklist UID");
         pw.println("    Adds a UID to the blacklist for restrict background usage.");
-        pw.println("  get metered-network ID");
-        pw.println("    Checks whether the given non-mobile network is metered or not.");
         pw.println("  get restrict-background");
         pw.println("    Gets the global restrict background usage status.");
-        pw.println("  list metered-networks [BOOLEAN]");
-        pw.println("    Lists all non-mobile networks and whether they are metered or not.");
+        pw.println("  list wifi-networks [BOOLEAN]");
+        pw.println("    Lists all saved wifi networks and whether they are metered or not.");
         pw.println("    If a boolean argument is passed, filters just the metered (or unmetered)");
         pw.println("    networks.");
         pw.println("  list restrict-background-whitelist");
@@ -105,7 +101,7 @@
         pw.println("  remove restrict-background-blacklist UID");
         pw.println("    Removes a UID from the blacklist for restrict background usage.");
         pw.println("  set metered-network ID BOOLEAN");
-        pw.println("    Toggles whether the given non-mobile network is metered.");
+        pw.println("    Toggles whether the given wi-fi network is metered.");
         pw.println("  set restrict-background BOOLEAN");
         pw.println("    Sets the global restrict background usage status.");
     }
@@ -118,8 +114,6 @@
             return -1;
         }
         switch(type) {
-            case "metered-network":
-                return getMeteredWifiNetwork();
             case "restrict-background":
                 return getRestrictBackground();
         }
@@ -152,8 +146,8 @@
             return -1;
         }
         switch(type) {
-            case "metered-networks":
-                return listMeteredWifiNetworks();
+            case "wifi-networks":
+                return listWifiNetworks();
             case "restrict-background-whitelist":
                 return listRestrictBackgroundWhitelist();
             case "restrict-background-blacklist":
@@ -284,7 +278,7 @@
         return 0;
     }
 
-    private int listMeteredWifiNetworks() throws RemoteException {
+    private int listWifiNetworks() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         final String arg = getNextArg();
         final Boolean filter = arg == null ? null : Boolean.valueOf(arg);
@@ -299,23 +293,6 @@
         return 0;
     }
 
-    private int getMeteredWifiNetwork() throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
-        final String id = getNextArg();
-        if (id == null) {
-            pw.println("Error: didn't specify ID");
-            return -1;
-        }
-        final List<NetworkPolicy> policies = getWifiPolicies();
-        for (NetworkPolicy policy: policies) {
-            if (id.equals(getNetworkId(policy))) {
-                pw.println(policy.metered);
-                return 0;
-            }
-        }
-        return 0;
-    }
-
     private int setMeteredWifiNetwork() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         final String id = getNextArg();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 022b10f..124d7f1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -120,6 +120,7 @@
 import android.widget.Toast;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
@@ -226,7 +227,7 @@
     private VrManagerInternal mVrManagerInternal;
 
     final IBinder mForegroundToken = new Binder();
-    private WorkerHandler mHandler;
+    private Handler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
 
@@ -572,33 +573,9 @@
         public void clearEffects() {
             synchronized (mNotificationList) {
                 if (DBG) Slog.d(TAG, "clearEffects");
-
-                // sound
-                mSoundNotificationKey = null;
-
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
-                    if (player != null) {
-                        player.stopAsync();
-                    }
-                } catch (RemoteException e) {
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-
-                // vibrate
-                mVibrateNotificationKey = null;
-                identity = Binder.clearCallingIdentity();
-                try {
-                    mVibrator.cancel();
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-
-                // light
-                mLights.clear();
-                updateLightsLocked();
+                clearSoundLocked();
+                clearVibrateLocked();
+                clearLightsLocked();
             }
         }
 
@@ -658,6 +635,36 @@
         }
     };
 
+    private void clearSoundLocked() {
+        mSoundNotificationKey = null;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+            if (player != null) {
+                player.stopAsync();
+            }
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void clearVibrateLocked() {
+        mVibrateNotificationKey = null;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mVibrator.cancel();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void clearLightsLocked() {
+        // light
+        mLights.clear();
+        updateLightsLocked();
+    }
+
     private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -863,6 +870,26 @@
         super(context);
     }
 
+    @VisibleForTesting
+    void setAudioManager(AudioManager audioMananger) {
+        mAudioManager = audioMananger;
+    }
+
+    @VisibleForTesting
+    void setVibrator(Vibrator vibrator) {
+        mVibrator = vibrator;
+    }
+
+    @VisibleForTesting
+    void setSystemReady(boolean systemReady) {
+        mSystemReady = systemReady;
+    }
+
+    @VisibleForTesting
+    void setHandler(Handler handler) {
+        mHandler = handler;
+    }
+
     @Override
     public void onStart() {
         Resources resources = getContext().getResources();
@@ -2492,12 +2519,14 @@
         return false;
     }
 
-    private void buzzBeepBlinkLocked(NotificationRecord record) {
+    @VisibleForTesting
+    void buzzBeepBlinkLocked(NotificationRecord record) {
         boolean buzz = false;
         boolean beep = false;
         boolean blink = false;
 
         final Notification notification = record.sbn.getNotification();
+        final String key = record.getKey();
 
         // Should this notification make noise, vibe, or use the LED?
         final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
@@ -2521,9 +2550,15 @@
         if (disableEffects != null) {
             ZenLog.traceDisableEffects(record, disableEffects);
         }
+
+        // Remember if this notification already owns the notification channels.
+        boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
+        boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
+
+        // These are set inside the conditional if the notification is allowed to make noise.
+        boolean hasValidVibrate = false;
+        boolean hasValidSound = false;
         if (disableEffects == null
-                && (!(record.isUpdate
-                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
                 && (record.getUserId() == UserHandle.USER_ALL ||
                     record.getUserId() == currentUser ||
                     mUserProfiles.isCurrentProfile(record.getUserId()))
@@ -2532,10 +2567,6 @@
                 && mAudioManager != null) {
             if (DBG) Slog.v(TAG, "Interrupting!");
 
-            sendAccessibilityEvent(notification, record.sbn.getPackageName());
-
-            // sound
-
             // should we use the default notification sound? (indicated either by
             // DEFAULT_SOUND or because notification.sound is pointing at
             // Settings.System.NOTIFICATION_SOUND)
@@ -2545,8 +2576,6 @@
                                    .equals(notification.sound);
 
             Uri soundUri = null;
-            boolean hasValidSound = false;
-
             if (useDefaultSound) {
                 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
 
@@ -2559,88 +2588,105 @@
                 hasValidSound = (soundUri != null);
             }
 
-            if (hasValidSound) {
-                boolean looping =
-                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
-                AudioAttributes audioAttributes = audioAttributesForNotification(notification);
-                mSoundNotificationKey = record.getKey();
-                // do not play notifications if stream volume is 0 (typically because
-                // ringer mode is silent) or if there is a user of exclusive audio focus
-                if ((mAudioManager.getStreamVolume(
-                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
-                            && !mAudioManager.isAudioFocusExclusive()) {
-                    final long identity = Binder.clearCallingIdentity();
-                    try {
-                        final IRingtonePlayer player =
-                                mAudioManager.getRingtonePlayer();
-                        if (player != null) {
-                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
-                                    + " with attributes " + audioAttributes);
-                            player.playAsync(soundUri, record.sbn.getUser(), looping,
-                                    audioAttributes);
-                            beep = true;
-                        }
-                    } catch (RemoteException e) {
-                    } finally {
-                        Binder.restoreCallingIdentity(identity);
-                    }
-                }
-            }
-
-            // vibrate
             // Does the notification want to specify its own vibration?
             final boolean hasCustomVibrate = notification.vibrate != null;
 
             // new in 4.2: if there was supposed to be a sound and we're in vibrate
             // mode, and no other vibration is specified, we fall back to vibration
             final boolean convertSoundToVibration =
-                       !hasCustomVibrate
-                    && hasValidSound
-                    && (mAudioManager.getRingerModeInternal()
-                               == AudioManager.RINGER_MODE_VIBRATE);
+                    !hasCustomVibrate
+                            && hasValidSound
+                            && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
 
             // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
             final boolean useDefaultVibrate =
                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
 
-            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
-                    && !(mAudioManager.getRingerModeInternal()
-                            == AudioManager.RINGER_MODE_SILENT)) {
-                mVibrateNotificationKey = record.getKey();
+            hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
+                    hasCustomVibrate;
 
-                if (useDefaultVibrate || convertSoundToVibration) {
-                    // Escalate privileges so we can use the vibrator even if the
-                    // notifying app does not have the VIBRATE permission.
-                    long identity = Binder.clearCallingIdentity();
-                    try {
-                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
-                            useDefaultVibrate ? mDefaultVibrationPattern
-                                : mFallbackVibrationPattern,
-                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
-                                ? 0: -1, audioAttributesForNotification(notification));
-                        buzz = true;
-                    } finally {
-                        Binder.restoreCallingIdentity(identity);
+            // We can alert, and we're allowed to alert, but if the developer asked us to only do
+            // it once, and we already have, then don't.
+            if (!(record.isUpdate
+                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
+
+                sendAccessibilityEvent(notification, record.sbn.getPackageName());
+
+                if (hasValidSound) {
+                    boolean looping =
+                            (notification.flags & Notification.FLAG_INSISTENT) != 0;
+                    AudioAttributes audioAttributes = audioAttributesForNotification(notification);
+                    mSoundNotificationKey = key;
+                    // do not play notifications if stream volume is 0 (typically because
+                    // ringer mode is silent) or if there is a user of exclusive audio focus
+                    if ((mAudioManager.getStreamVolume(
+                            AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
+                            && !mAudioManager.isAudioFocusExclusive()) {
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            final IRingtonePlayer player =
+                                    mAudioManager.getRingtonePlayer();
+                            if (player != null) {
+                                if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+                                        + " with attributes " + audioAttributes);
+                                player.playAsync(soundUri, record.sbn.getUser(), looping,
+                                        audioAttributes);
+                                beep = true;
+                            }
+                        } catch (RemoteException e) {
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
                     }
-                } else if (notification.vibrate.length > 1) {
-                    // If you want your own vibration pattern, you need the VIBRATE
-                    // permission
-                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
-                            notification.vibrate,
-                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
-                                ? 0: -1, audioAttributesForNotification(notification));
-                    buzz = true;
+                }
+
+                if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
+                        == AudioManager.RINGER_MODE_SILENT)) {
+                    mVibrateNotificationKey = key;
+
+                    if (useDefaultVibrate || convertSoundToVibration) {
+                        // Escalate privileges so we can use the vibrator even if the
+                        // notifying app does not have the VIBRATE permission.
+                        long identity = Binder.clearCallingIdentity();
+                        try {
+                            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                                    useDefaultVibrate ? mDefaultVibrationPattern
+                                            : mFallbackVibrationPattern,
+                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+                                            ? 0: -1, audioAttributesForNotification(notification));
+                            buzz = true;
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                    } else if (notification.vibrate.length > 1) {
+                        // If you want your own vibration pattern, you need the VIBRATE
+                        // permission
+                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                                notification.vibrate,
+                                ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+                                        ? 0: -1, audioAttributesForNotification(notification));
+                        buzz = true;
+                    }
                 }
             }
+
+        }
+        // If a notification is updated to remove the actively playing sound or vibrate,
+        // cancel that feedback now
+        if (wasBeep && !hasValidSound) {
+            clearSoundLocked();
+        }
+        if (wasBuzz && !hasValidVibrate) {
+            clearVibrateLocked();
         }
 
         // light
         // release the light
-        boolean wasShowLights = mLights.remove(record.getKey());
+        boolean wasShowLights = mLights.remove(key);
         if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
                 && ((record.getSuppressedVisualEffects()
                 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
-            mLights.add(record.getKey());
+            mLights.add(key);
             updateLightsLocked();
             if (mUseAttentionLight) {
                 mAttentionLight.pulse();
@@ -2654,7 +2700,7 @@
                     & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
                 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
             } else {
-                EventLogTags.writeNotificationAlert(record.getKey(),
+                EventLogTags.writeNotificationAlert(key,
                         buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
                 mHandler.post(mBuzzBeepBlinked);
             }
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 13a96ae..e496132 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -247,10 +247,8 @@
             }
 
             // SetupWizard
-            Intent setupIntent = new Intent(Intent.ACTION_MAIN);
-            setupIntent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
-            PackageParser.Package setupPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    setupIntent, userId);
+            PackageParser.Package setupPackage = getSystemPackageLPr(
+                    mService.mSetupWizardPackage);
             if (setupPackage != null
                     && doesPackageSupportRuntimePermissions(setupPackage)) {
                 grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 58592ff..0a86a84 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -64,6 +64,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN;
@@ -1034,6 +1035,7 @@
 
     final @Nullable String mRequiredVerifierPackage;
     final @Nullable String mRequiredInstallerPackage;
+    final @Nullable String mSetupWizardPackage;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
 
@@ -2529,6 +2531,7 @@
             }
 
             mInstallerService = new PackageInstallerService(context, this);
+            mSetupWizardPackage = getSetupWizardPackageName();
 
             final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
             final ComponentName ephemeralInstallerComponent = getEphemeralInstallerLPr();
@@ -2886,12 +2889,15 @@
         return cur;
     }
 
-    PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
+    private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        final PackageSetting ps = (PackageSetting) p.mExtras;
         if (ps == null) {
             return null;
         }
+        final PackageParser.Package p = ps.pkg;
+        if (p == null) {
+            return null;
+        }
 
         final PermissionsState permissionsState = ps.getPermissionsState();
 
@@ -2961,14 +2967,28 @@
                 false /* requireFullPermission */, false /* checkShell */, "get package info");
         // reader
         synchronized (mPackages) {
-            PackageParser.Package p = mPackages.get(packageName);
+            final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
+            PackageParser.Package p = null;
+            if (matchFactoryOnly) {
+                final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+                if (ps != null) {
+                    return generatePackageInfo(ps, flags, userId);
+                }
+            }
+            if (p == null) {
+                p = mPackages.get(packageName);
+                if (matchFactoryOnly && !isSystemApp(p)) {
+                    return null;
+                }
+            }
             if (DEBUG_PACKAGE_INFO)
                 Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
             if (p != null) {
-                return generatePackageInfo(p, flags, userId);
+                return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
             }
-            if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
-                return generatePackageInfoFromSettingsLPw(packageName, flags, userId);
+            if (!matchFactoryOnly && (flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
+                final PackageSetting ps = mSettings.mPackages.get(packageName);
+                return generatePackageInfo(ps, flags, userId);
             }
         }
         return null;
@@ -3129,8 +3149,7 @@
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
             if (ps.pkg == null) {
-                PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
-                        flags, userId);
+                final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
                 if (pInfo != null) {
                     return pInfo.applicationInfo;
                 }
@@ -3142,31 +3161,6 @@
         return null;
     }
 
-    private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags,
-            int userId) {
-        if (!sUserManager.exists(userId)) return null;
-        PackageSetting ps = mSettings.mPackages.get(packageName);
-        if (ps != null) {
-            PackageParser.Package pkg = ps.pkg;
-            if (pkg == null) {
-                if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0) {
-                    return null;
-                }
-                // Only data remains, so we aren't worried about code paths
-                pkg = new PackageParser.Package(packageName);
-                pkg.applicationInfo.packageName = packageName;
-                pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
-                pkg.applicationInfo.privateFlags = ps.pkgPrivateFlags;
-                pkg.applicationInfo.uid = ps.appId;
-                pkg.applicationInfo.initForUser(userId);
-                pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
-                pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
-            }
-            return generatePackageInfo(pkg, flags, userId);
-        }
-        return null;
-    }
-
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
@@ -5915,11 +5909,11 @@
             if (listUninstalled) {
                 list = new ArrayList<PackageInfo>(mSettings.mPackages.size());
                 for (PackageSetting ps : mSettings.mPackages.values()) {
-                    PackageInfo pi;
+                    final PackageInfo pi;
                     if (ps.pkg != null) {
-                        pi = generatePackageInfo(ps.pkg, flags, userId);
+                        pi = generatePackageInfo(ps, flags, userId);
                     } else {
-                        pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+                        pi = generatePackageInfo(ps, flags, userId);
                     }
                     if (pi != null) {
                         list.add(pi);
@@ -5928,7 +5922,8 @@
             } else {
                 list = new ArrayList<PackageInfo>(mPackages.size());
                 for (PackageParser.Package p : mPackages.values()) {
-                    PackageInfo pi = generatePackageInfo(p, flags, userId);
+                    final PackageInfo pi =
+                            generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
                     if (pi != null) {
                         list.add(pi);
                     }
@@ -5955,11 +5950,11 @@
         if (numMatch == 0) {
             return;
         }
-        PackageInfo pi;
+        final PackageInfo pi;
         if (ps.pkg != null) {
-            pi = generatePackageInfo(ps.pkg, flags, userId);
+            pi = generatePackageInfo(ps, flags, userId);
         } else {
-            pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+            pi = generatePackageInfo(ps, flags, userId);
         }
         // The above might return null in cases of uninstalled apps or install-state
         // skew across users/profiles.
@@ -9704,6 +9699,12 @@
                 // is granted only if it was already granted.
                 allowed = origPermissions.hasInstallPermission(perm);
             }
+            if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0
+                    && pkg.packageName.equals(mSetupWizardPackage)) {
+                // If this permission is to be granted to the system setup wizard and
+                // this app is a setup wizard, then it gets the permission.
+                allowed = true;
+            }
         }
         return allowed;
     }
@@ -16626,6 +16627,21 @@
                 set, comp, userId);
     }
 
+    private @Nullable String getSetupWizardPackageName() {
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
+
+        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+                MATCH_SYSTEM_ONLY | MATCH_DISABLED_COMPONENTS, UserHandle.myUserId());
+        if (matches.size() == 1) {
+            return matches.get(0).getComponentInfo().packageName;
+        } else {
+            Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size()
+                    + ": matches=" + matches);
+            return null;
+        }
+    }
+
     @Override
     public void setApplicationEnabledSetting(String appPackageName,
             int newState, int flags, int userId, String callingPackage) {
diff --git a/services/core/java/com/android/server/pm/ShortcutBackupAgent.java b/services/core/java/com/android/server/pm/ShortcutBackupAgent.java
deleted file mode 100644
index a0fbc37..0000000
--- a/services/core/java/com/android/server/pm/ShortcutBackupAgent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.pm;
-
-import android.app.backup.BlobBackupHelper;
-
-public class ShortcutBackupAgent extends BlobBackupHelper {
-    private static final String TAG = "ShortcutBackupAgent";
-    private static final int BLOB_VERSION = 1;
-
-    public ShortcutBackupAgent(int currentBlobVersion, String... keys) {
-        super(currentBlobVersion, keys);
-    }
-
-    @Override
-    protected byte[] getBackupPayload(String key) {
-        throw new RuntimeException("not implemented yet"); // todo
-    }
-
-    @Override
-    protected void applyRestoredPayload(String key, byte[] payload) {
-        throw new RuntimeException("not implemented yet"); // todo
-    }
-}
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 7699f30..c6d66fe 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -20,6 +20,10 @@
 import android.content.pm.ShortcutInfo;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -27,6 +31,7 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -43,15 +48,16 @@
     private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_PACKAGE_USER_ID = "package-user";
 
     private final int mOwnerUserId;
 
     /**
      * Package name -> IDs.
      */
-    final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
+    final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
 
-    public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+    private ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
             @UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
         super(launcherUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
         mOwnerUserId = ownerUserId;
@@ -59,7 +65,7 @@
 
     public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
             @UserIdInt int launcherUserId) {
-        this(launcherUserId, packageName, launcherUserId, null);
+        this(ownerUserId, packageName, launcherUserId, null);
     }
 
     @Override
@@ -67,16 +73,38 @@
         return mOwnerUserId;
     }
 
+    /**
+     * Called when the new package can't receive the backup, due to signature or version mismatch.
+     */
+    @Override
+    protected void onRestoreBlocked(ShortcutService s) {
+        final ArrayList<PackageWithUser> pinnedPackages =
+                new ArrayList<>(mPinnedShortcuts.keySet());
+        mPinnedShortcuts.clear();
+        for (int i = pinnedPackages.size() - 1; i >= 0; i--) {
+            final PackageWithUser pu = pinnedPackages.get(i);
+            s.getPackageShortcutsLocked(pu.packageName, pu.userId)
+                    .refreshPinnedFlags(s);
+        }
+    }
+
+    @Override
+    protected void onRestored(ShortcutService s) {
+        // Nothing to do.
+    }
+
     public void pinShortcuts(@NonNull ShortcutService s, @UserIdInt int packageUserId,
             @NonNull String packageName, @NonNull List<String> ids) {
         final ShortcutPackage packageShortcuts =
                 s.getPackageShortcutsLocked(packageName, packageUserId);
 
+        final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName);
+
         final int idSize = ids.size();
         if (idSize == 0) {
-            mPinnedShortcuts.remove(packageName);
+            mPinnedShortcuts.remove(pu);
         } else {
-            final ArraySet<String> prevSet = mPinnedShortcuts.get(packageName);
+            final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
 
             // Pin shortcuts.  Make sure only pin the ones that were visible to the caller.
             // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
@@ -93,7 +121,7 @@
                     newSet.add(id);
                 }
             }
-            mPinnedShortcuts.put(packageName, newSet);
+            mPinnedShortcuts.put(pu, newSet);
         }
         packageShortcuts.refreshPinnedFlags(s);
     }
@@ -101,12 +129,13 @@
     /**
      * Return the pinned shortcut IDs for the publisher package.
      */
-    public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName) {
-        return mPinnedShortcuts.get(packageName);
+    public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
+            @UserIdInt int packageUserId) {
+        return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
     }
 
-    boolean cleanUpPackage(String packageName) {
-        return mPinnedShortcuts.remove(packageName) != null;
+    boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
+        return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
     }
 
     /**
@@ -126,9 +155,15 @@
         getPackageInfo().saveToXml(out);
 
         for (int i = 0; i < size; i++) {
+            final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
+
+            if (forBackup && (pu.userId != getOwnerUserId())) {
+                continue; // Target package on a different user, skip. (i.e. work profile)
+            }
+
             out.startTag(null, TAG_PACKAGE);
-            ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
-                    mPinnedShortcuts.keyAt(i));
+            ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName);
+            ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId);
 
             final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
             final int idSize = ids.size();
@@ -157,8 +192,6 @@
         final ShortcutLauncher ret = new ShortcutLauncher(launcherUserId, launcherPackageName,
                 launcherUserId);
 
-        ShortcutPackageInfo spi = null;
-
         ArraySet<String> ids = null;
         final int outerDepth = parser.getDepth();
         int type;
@@ -172,13 +205,17 @@
             if (depth == outerDepth + 1) {
                 switch (tag) {
                     case ShortcutPackageInfo.TAG_ROOT:
-                        spi = ShortcutPackageInfo.loadFromXml(parser);
+                        ret.getPackageInfo().loadFromXml(parser, fromBackup);
                         continue;
                     case TAG_PACKAGE: {
                         final String packageName = ShortcutService.parseStringAttribute(parser,
                                 ATTR_PACKAGE_NAME);
+                        final int packageUserId = fromBackup ? ownerUserId
+                                : ShortcutService.parseIntAttribute(parser,
+                                ATTR_PACKAGE_USER_ID, ownerUserId);
                         ids = new ArraySet<>();
-                        ret.mPinnedShortcuts.put(packageName, ids);
+                        ret.mPinnedShortcuts.put(
+                                PackageWithUser.of(packageUserId, packageName), ids);
                         continue;
                     }
                 }
@@ -186,17 +223,17 @@
             if (depth == outerDepth + 2) {
                 switch (tag) {
                     case TAG_PIN: {
-                        ids.add(ShortcutService.parseStringAttribute(parser,
-                                ATTR_VALUE));
+                        if (ids == null) {
+                            Slog.w(TAG, TAG_PIN + " in invalid place");
+                        } else {
+                            ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE));
+                        }
                         continue;
                     }
                 }
             }
             ShortcutService.warnForInvalidTag(depth, tag);
         }
-        if (spi != null) {
-            ret.replacePackageInfo(spi);
-        }
         return ret;
     }
 
@@ -208,6 +245,8 @@
         pw.print(getPackageName());
         pw.print("  Package user: ");
         pw.print(getPackageUserId());
+        pw.print("  Owner user: ");
+        pw.print(getOwnerUserId());
         pw.println();
 
         getPackageInfo().dump(s, pw, prefix + "  ");
@@ -217,10 +256,14 @@
         for (int i = 0; i < size; i++) {
             pw.println();
 
+            final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
+
             pw.print(prefix);
             pw.print("  ");
             pw.print("Package: ");
-            pw.println(mPinnedShortcuts.keyAt(i));
+            pw.print(pu.packageName);
+            pw.print("  User: ");
+            pw.println(pu.userId);
 
             final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
             final int idSize = ids.size();
@@ -233,4 +276,9 @@
             }
         }
     }
+
+    @VisibleForTesting
+    ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
+        return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 5916202..1076a7a 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -26,6 +26,8 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -34,6 +36,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
 
@@ -83,7 +86,7 @@
      */
     private long mLastResetTime;
 
-    public ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) {
+    private ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) {
         super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
     }
 
@@ -97,6 +100,19 @@
         return getPackageUserId();
     }
 
+    @Override
+    protected void onRestoreBlocked(ShortcutService s) {
+        // Can't restore due to version/signature mismatch.  Remove all shortcuts.
+        mShortcuts.clear();
+    }
+
+    @Override
+    protected void onRestored(ShortcutService s) {
+        // Because some launchers may not have been restored (e.g. allowBackup=false),
+        // we need to re-calculate the pinned shortcuts.
+        refreshPinnedFlags(s);
+    }
+
     /**
      * Note this does *not* provide a correct view to the calling launcher.
      */
@@ -229,20 +245,26 @@
                 s.getUserShortcutsLocked(getPackageUserId()).getAllLaunchers();
 
         for (int l = launchers.size() - 1; l >= 0; l--) {
+            // Note even if a launcher that hasn't been installed can still pin shortcuts.
+
             final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
             final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
-                    getPackageName());
+                    getPackageName(), getPackageUserId());
 
             if (pinned == null || pinned.size() == 0) {
                 continue;
             }
             for (int i = pinned.size() - 1; i >= 0; i--) {
-                final ShortcutInfo si = mShortcuts.get(pinned.valueAt(i));
+                final String id = pinned.valueAt(i);
+                final ShortcutInfo si = mShortcuts.get(id);
                 if (si == null) {
-                    s.wtf("Shortcut not found");
-                } else {
-                    si.addFlags(ShortcutInfo.FLAG_PINNED);
+                    // This happens if a launcher pinned shortcuts from this package, then backup&
+                    // restored, but this package doesn't allow backing up.
+                    // In that case the launcher ends up having a dangling pinned shortcuts.
+                    // That's fine, when the launcher is restored, we'll fix it.
+                    continue;
                 }
+                si.addFlags(ShortcutInfo.FLAG_PINNED);
             }
         }
 
@@ -312,11 +334,15 @@
     public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
             @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
             @Nullable String callingLauncher, int launcherUserId) {
+        if (getPackageInfo().isShadow()) {
+            // Restored and the app not installed yet, so don't return any.
+            return;
+        }
 
         // Set of pinned shortcuts by the calling launcher.
         final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
-                : s.getLauncherShortcuts(callingLauncher, getPackageUserId(), launcherUserId)
-                    .getPinnedShortcutIds(getPackageName());
+                : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
+                    .getPinnedShortcutIds(getPackageName(), getPackageUserId());
 
         for (int i = 0; i < mShortcuts.size(); i++) {
             final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -328,7 +354,8 @@
                     || ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
             if (!si.isDynamic()) {
                 if (!si.isPinned()) {
-                    s.wtf("Shortcut not pinned here");
+                    s.wtf("Shortcut not pinned: package " + getPackageName()
+                            + ", user=" + getPackageUserId() + ", id=" + si.getId());
                     continue;
                 }
                 if (!isPinnedByCaller) {
@@ -479,7 +506,6 @@
                 ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
         ret.mLastResetTime =
                 ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
-        ShortcutPackageInfo spi = null;
 
         final int outerDepth = parser.getDepth();
         int type;
@@ -493,7 +519,7 @@
             if (depth == outerDepth + 1) {
                 switch (tag) {
                     case ShortcutPackageInfo.TAG_ROOT:
-                        spi = ShortcutPackageInfo.loadFromXml(parser);
+                        ret.getPackageInfo().loadFromXml(parser, fromBackup);
                         continue;
                     case TAG_SHORTCUT:
                         final ShortcutInfo si = parseShortcut(parser, packageName);
@@ -505,9 +531,6 @@
             }
             ShortcutService.warnForInvalidTag(depth, tag);
         }
-        if (spi != null) {
-            ret.replacePackageInfo(spi);
-        }
         return ret;
     }
 
@@ -567,4 +590,9 @@
                 intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
                 iconRes, bitmapPath);
     }
+
+    @VisibleForTesting
+    List<ShortcutInfo> getAllShortcutsForTest() {
+        return new ArrayList<>(mShortcuts.values());
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 5f706b8..2c45890 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -67,10 +67,6 @@
         return mIsShadow;
     }
 
-    public boolean isInstalled() {
-        return !mIsShadow;
-    }
-
     public void setShadow(boolean shadow) {
         mIsShadow = shadow;
     }
@@ -79,14 +75,24 @@
         return mVersionCode;
     }
 
-    public boolean canRestoreTo(PackageInfo target) {
+    public boolean hasSignatures() {
+        return mSigHashes.size() > 0;
+    }
+
+    public boolean canRestoreTo(ShortcutService s, PackageInfo target) {
+        if (!s.shouldBackupApp(target)) {
+            // "allowBackup" was true when backed up, but now false.
+            Slog.w(TAG, "Can't restore: package no longer allows backup");
+            return false;
+        }
         if (target.versionCode < mVersionCode) {
-            Slog.w(TAG, String.format("Package current version %d < backed up version %d",
+            Slog.w(TAG, String.format(
+                    "Can't restore: package current version %d < backed up version %d",
                     target.versionCode, mVersionCode));
             return false;
         }
         if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
-            Slog.w(TAG, "Package signature mismtach");
+            Slog.w(TAG, "Can't restore: Package signature mismatch");
             return false;
         }
         return true;
@@ -106,6 +112,11 @@
     }
 
     public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
+        if (mIsShadow) {
+            s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName()
+                    + ", user=" + pkg.getOwnerUserId());
+            return;
+        }
         // Note use mUserId here, rather than userId.
         final PackageInfo pi = s.getPackageInfoWithSignatures(
                 pkg.getPackageName(), pkg.getPackageUserId());
@@ -132,15 +143,17 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    public static ShortcutPackageInfo loadFromXml(XmlPullParser parser)
+    public void loadFromXml(XmlPullParser parser, boolean fromBackup)
             throws IOException, XmlPullParserException {
 
         final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
-        final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
+
+        // When restoring from backup, it's always shadow.
+        final boolean shadow =
+                fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
 
         final ArrayList<byte[]> hashes = new ArrayList<>();
 
-
         final int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -163,7 +176,11 @@
             }
             ShortcutService.warnForInvalidTag(depth, tag);
         }
-        return new ShortcutPackageInfo(versionCode, hashes, shadow);
+
+        // Successfully loaded; replace the feilds.
+        mVersionCode = versionCode;
+        mIsShadow = shadow;
+        mSigHashes = hashes;
     }
 
     public void dump(ShortcutService s, PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index de2709d..f31dd17 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -16,6 +16,8 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
 
@@ -25,10 +27,12 @@
 import java.io.IOException;
 
 abstract class ShortcutPackageItem {
+    private static final String TAG = ShortcutService.TAG;
+
     private final int mPackageUserId;
     private final String mPackageName;
 
-    private ShortcutPackageInfo mPackageInfo;
+    private final ShortcutPackageInfo mPackageInfo;
 
     protected ShortcutPackageItem(int packageUserId, @NonNull String packageName,
             @NonNull ShortcutPackageInfo packageInfo) {
@@ -61,25 +65,62 @@
         return mPackageInfo;
     }
 
-    /**
-     * Should be only used when loading from a file.o
-     */
-    protected void replacePackageInfo(@NonNull ShortcutPackageInfo packageInfo) {
-        mPackageInfo = Preconditions.checkNotNull(packageInfo);
-    }
-
     public void refreshPackageInfoAndSave(ShortcutService s) {
+        if (mPackageInfo.isShadow()) {
+            return; // Don't refresh for shadow user.
+        }
         mPackageInfo.refresh(s, this);
         s.scheduleSaveUser(getOwnerUserId());
     }
 
-    public void ensureNotShadowAndSave(ShortcutService s) {
-        if (mPackageInfo.isShadow()) {
-            mPackageInfo.setShadow(false);
-            s.scheduleSaveUser(getOwnerUserId());
+    public void attemptToRestoreIfNeededAndSave(ShortcutService s) {
+        if (!mPackageInfo.isShadow()) {
+            return; // Already installed, nothing to do.
         }
+        if (!s.isPackageInstalled(mPackageName, mPackageUserId)) {
+            if (ShortcutService.DEBUG) {
+                Slog.d(TAG, String.format("Package still not installed: %s user=%d",
+                        mPackageName, mPackageUserId));
+            }
+            return; // Not installed, no need to restore yet.
+        }
+        if (!mPackageInfo.hasSignatures()) {
+            s.wtf("Attempted to restore package " + mPackageName + ", user=" + mPackageUserId
+                    + " but signatures not found in the restore data.");
+            onRestoreBlocked(s);
+            return;
+        }
+
+        final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
+        if (!mPackageInfo.canRestoreTo(s, pi)) {
+            // Package is now installed, but can't restore.  Let the subclass do the cleanup.
+            onRestoreBlocked(s);
+            return;
+        }
+        if (ShortcutService.DEBUG) {
+            Slog.d(TAG, String.format("Restored package: %s/%d on user %d", mPackageName,
+                    mPackageUserId, getOwnerUserId()));
+        }
+
+        onRestored(s);
+
+        // Now the package is not shadow.
+        mPackageInfo.setShadow(false);
+
+        s.scheduleSaveUser(mPackageUserId);
     }
 
+    /**
+     * Called when the new package can't be restored because it has a lower version number
+     * or different signatures.
+     */
+    protected abstract void onRestoreBlocked(ShortcutService s);
+
+    /**
+     * Called when the new package is successfully restored.
+     */
+    protected abstract void onRestored(ShortcutService s);
+
     public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException;
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 76a2dfa..7aefcb8 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -46,6 +46,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
@@ -101,6 +102,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -112,17 +114,16 @@
  *
  * - Scan and remove orphan bitmaps (just in case).
  *
- * - Backup & restore
- *
  * - Detect when already registered instances are passed to APIs again, which might break
  *   internal bitmap handling.
+ *
+ * - Add more call stats.
  */
 public class ShortcutService extends IShortcutService.Stub {
     static final String TAG = "ShortcutService";
 
     static final boolean DEBUG = false; // STOPSHIP if true
     static final boolean DEBUG_LOAD = false; // STOPSHIP if true
-    static final boolean ENABLE_DEBUG_COMMAND = true; // STOPSHIP if true
 
     @VisibleForTesting
     static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -262,6 +263,26 @@
             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
             | PackageManager.MATCH_UNINSTALLED_PACKAGES;
 
+    // Stats
+    @VisibleForTesting
+    interface Stats {
+        int GET_DEFAULT_HOME = 0;
+        int GET_PACKAGE_INFO = 1;
+        int GET_PACKAGE_INFO_WITH_SIG = 2;
+        int GET_APPLICATION_INFO = 3;
+        int LAUNCHER_PERMISSION_CHECK = 4;
+
+        int COUNT = LAUNCHER_PERMISSION_CHECK + 1;
+    }
+
+    final Object mStatLock = new Object();
+
+    @GuardedBy("mStatLock")
+    private final int[] mCountStats = new int[Stats.COUNT];
+
+    @GuardedBy("mStatLock")
+    private final long[] mDurationStats = new long[Stats.COUNT];
+
     public ShortcutService(Context context) {
         this(context, BackgroundThread.get().getLooper());
     }
@@ -278,6 +299,13 @@
         mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
     }
 
+    void logDurationStat(int statId, long start) {
+        synchronized (mStatLock) {
+            mCountStats[statId]++;
+            mDurationStats[statId] += (System.currentTimeMillis() - start);
+        }
+    }
+
     /**
      * System service lifecycle.
      */
@@ -822,7 +850,7 @@
 
     @GuardedBy("mLock")
     @NonNull
-    boolean isUserLoadedLocked(@UserIdInt int userId) {
+    private boolean isUserLoadedLocked(@UserIdInt int userId) {
         return mUsers.get(userId) != null;
     }
 
@@ -841,19 +869,27 @@
         return userPackages;
     }
 
+    void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
+        for (int i = mUsers.size() - 1; i >= 0; i--) {
+            c.accept(mUsers.valueAt(i));
+        }
+    }
+
     /** Return the per-user per-package state. */
     @GuardedBy("mLock")
     @NonNull
     ShortcutPackage getPackageShortcutsLocked(
             @NonNull String packageName, @UserIdInt int userId) {
-        return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
+        return getUserShortcutsLocked(userId).getPackageShortcuts(this, packageName);
     }
 
     @GuardedBy("mLock")
     @NonNull
-    ShortcutLauncher getLauncherShortcuts(
-            @NonNull String packageName, @UserIdInt int userId, @UserIdInt int launcherUserId) {
-        return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName, launcherUserId);
+    ShortcutLauncher getLauncherShortcutsLocked(
+            @NonNull String packageName, @UserIdInt int ownerUserId,
+            @UserIdInt int launcherUserId) {
+        return getUserShortcutsLocked(ownerUserId)
+                .getLauncherShortcuts(this, packageName, launcherUserId);
     }
 
     // === Caller validation ===
@@ -1212,8 +1248,6 @@
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
-            ps.ensureNotShadowAndSave(this);
-
             // Throttling.
             if (!ps.tryApiCall(this)) {
                 return false;
@@ -1249,8 +1283,6 @@
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
-            ps.ensureNotShadowAndSave(this);
-
             // Throttling.
             if (!ps.tryApiCall(this)) {
                 return false;
@@ -1288,8 +1320,6 @@
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
-            ps.ensureNotShadowAndSave(this);
-
             // Throttling.
             if (!ps.tryApiCall(this)) {
                 return false;
@@ -1422,15 +1452,17 @@
     @VisibleForTesting
     boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
         synchronized (mLock) {
-            long start = System.currentTimeMillis();
+            final long start = System.currentTimeMillis();
 
             final ShortcutUser user = getUserShortcutsLocked(userId);
 
             final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
 
             // Default launcher from package manager.
+            final long startGetHomeActivitiesAsUser = System.currentTimeMillis();
             final ComponentName defaultLauncher = injectPackageManagerInternal()
                     .getHomeActivitiesAsUser(allHomeCandidates, userId);
+            logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
 
             ComponentName detected;
             if (defaultLauncher != null) {
@@ -1473,10 +1505,8 @@
                     lastPriority = ri.priority;
                 }
             }
-            final long end = System.currentTimeMillis();
-            if (DEBUG) {
-                Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
-            }
+            logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
+
             if (detected != null) {
                 if (DEBUG) {
                     Slog.v(TAG, "Detected launcher: " + detected);
@@ -1492,10 +1522,17 @@
 
     // === House keeping ===
 
+    /**
+     * 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) {
-
-        // TODO Don't remove shadow packages' information.
+        if (isPackageInstalled(packageName, packageUserId)) {
+            wtf("Package " + packageName + " is still installed for user " + packageUserId);
+            return;
+        }
 
         final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
 
@@ -1504,7 +1541,7 @@
 
         // First, remove the package from the package list (if the package is a publisher).
         if (packageUserId == owningUserId) {
-            if (mUser.getPackages().remove(packageName) != null) {
+            if (mUser.removePackage(packageName) != null) {
                 doNotify = true;
             }
         }
@@ -1515,12 +1552,12 @@
         // Then remove pinned shortcuts from all launchers.
         final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
         for (int i = launchers.size() - 1; i >= 0; i--) {
-            launchers.valueAt(i).cleanUpPackage(packageName);
+            launchers.valueAt(i).cleanUpPackage(packageName, packageUserId);
         }
         // Now there may be orphan shortcuts because we removed pinned shortucts at the previous
         // step.  Remove them too.
-        for (int i = mUser.getPackages().size() - 1; i >= 0; i--) {
-            mUser.getPackages().valueAt(i).refreshPinnedFlags(this);
+        for (int i = mUser.getAllPackages().size() - 1; i >= 0; i--) {
+            mUser.getAllPackages().valueAt(i).refreshPinnedFlags(this);
         }
 
         scheduleSaveUser(owningUserId);
@@ -1539,6 +1576,7 @@
      * Entry point from {@link LauncherApps}.
      */
     private class LocalService extends ShortcutServiceInternal {
+
         @Override
         public List<ShortcutInfo> getShortcuts(int launcherUserId,
                 @NonNull String callingPackage, long changedSince,
@@ -1551,13 +1589,16 @@
                             : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
 
             synchronized (mLock) {
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
                 if (packageName != null) {
                     getShortcutsInnerLocked(launcherUserId,
                             callingPackage, packageName, changedSince,
                             componentName, queryFlags, userId, ret, cloneFlag);
                 } else {
                     final ArrayMap<String, ShortcutPackage> packages =
-                            getUserShortcutsLocked(userId).getPackages();
+                            getUserShortcutsLocked(userId).getAllPackages();
                     for (int i = packages.size() - 1; i >= 0; i--) {
                         getShortcutsInnerLocked(launcherUserId,
                                 callingPackage, packages.keyAt(i), changedSince,
@@ -1601,6 +1642,9 @@
             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()),
@@ -1616,13 +1660,16 @@
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
 
             synchronized (mLock) {
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
                 final ShortcutInfo si = getShortcutInfoLocked(
                         launcherUserId, callingPackage, packageName, shortcutId, userId);
                 return si != null && si.isPinned();
             }
         }
 
-        public ShortcutInfo getShortcutInfoLocked(
+        private ShortcutInfo getShortcutInfoLocked(
                 int launcherUserId, @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
             Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1646,9 +1693,8 @@
 
             synchronized (mLock) {
                 final ShortcutLauncher launcher =
-                        getLauncherShortcuts(callingPackage, userId, launcherUserId);
-
-                launcher.ensureNotShadowAndSave(ShortcutService.this);
+                        getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
+                launcher.attemptToRestoreIfNeededAndSave(ShortcutService.this);
 
                 launcher.pinShortcuts(
                         ShortcutService.this, userId, packageName, shortcutIds);
@@ -1665,6 +1711,9 @@
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
 
             synchronized (mLock) {
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
                 // Make sure the shortcut is actually visible to the launcher.
                 final ShortcutInfo si = getShortcutInfoLocked(
                         launcherUserId, callingPackage, packageName, shortcutId, userId);
@@ -1690,6 +1739,9 @@
             Preconditions.checkNotNull(shortcut, "shortcut");
 
             synchronized (mLock) {
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
                 final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
                         shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
                 return (shortcutInfo != null && shortcutInfo.hasIconResource())
@@ -1704,6 +1756,9 @@
             Preconditions.checkNotNull(shortcutIn, "shortcut");
 
             synchronized (mLock) {
+                getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+                        .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
                 final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
                         shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
                 if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
@@ -1757,28 +1812,28 @@
      * perform cleanup.
      */
     @VisibleForTesting
-    void cleanupGonePackages(@UserIdInt int userId) {
+    void cleanupGonePackages(@UserIdInt int ownerUserId) {
         if (DEBUG) {
-            Slog.d(TAG, "cleanupGonePackages() userId=" + userId);
+            Slog.d(TAG, "cleanupGonePackages() ownerUserId=" + ownerUserId);
         }
         final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
 
         synchronized (mLock) {
-            final ShortcutUser user = getUserShortcutsLocked(userId);
+            final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
 
             user.forAllPackageItems(spi -> {
                 if (spi.getPackageInfo().isShadow()) {
                     return; // Don't delete shadow information.
                 }
                 if (isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
-                    return;
+                    return; // Package not gone.
                 }
                 gonePackages.add(PackageWithUser.of(spi));
             });
             if (gonePackages.size() > 0) {
                 for (int i = gonePackages.size() - 1; i >= 0; i--) {
                     final PackageWithUser pu = gonePackages.get(i);
-                    cleanUpPackageLocked(pu.packageName, userId, pu.userId);
+                    cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
                 }
             }
         }
@@ -1789,7 +1844,8 @@
             Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
         }
         synchronized (mLock) {
-            getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId);
+            forEachLoadedUserLocked(user ->
+                    user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
         }
     }
 
@@ -1798,18 +1854,20 @@
             Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
                     packageName, userId));
         }
-
         synchronized (mLock) {
-            getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId);
+            forEachLoadedUserLocked(user ->
+                    user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
         }
     }
 
-    private void handlePackageRemoved(String packageName, @UserIdInt int userId) {
+    private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
         if (DEBUG) {
-            Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId));
+            Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
+                    packageUserId));
         }
         synchronized (mLock) {
-            cleanUpPackageLocked(packageName, userId, userId);
+            forEachLoadedUserLocked(user ->
+                cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
         }
     }
 
@@ -1836,6 +1894,7 @@
     @VisibleForTesting
     PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
             boolean getSignatures) {
+        final long start = System.currentTimeMillis();
         final long token = injectClearCallingIdentity();
         try {
             return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
@@ -1847,11 +1906,16 @@
             return null;
         } finally {
             injectRestoreCallingIdentity(token);
+
+            logDurationStat(
+                    (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO),
+                    start);
         }
     }
 
     @VisibleForTesting
     ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
+        final long start = System.currentTimeMillis();
         final long token = injectClearCallingIdentity();
         try {
             return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
@@ -1861,6 +1925,8 @@
             return null;
         } finally {
             injectRestoreCallingIdentity(token);
+
+            logDurationStat(Stats.GET_APPLICATION_INFO, start);
         }
     }
 
@@ -1869,7 +1935,7 @@
         return (ai != null) && ((ai.flags & flags) == flags);
     }
 
-    private boolean isPackageInstalled(String packageName, int userId) {
+    boolean isPackageInstalled(String packageName, int userId) {
         return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
     }
 
@@ -1879,8 +1945,12 @@
         return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
     }
 
+    boolean shouldBackupApp(PackageInfo pi) {
+        return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
+    }
+
     @Override
-    public byte[] getBackupPayload(@UserIdInt int userId) throws RemoteException {
+    public byte[] getBackupPayload(@UserIdInt int userId) {
         enforceSystem();
         if (DEBUG) {
             Slog.d(TAG, "Backing up user " + userId);
@@ -1908,7 +1978,7 @@
     }
 
     @Override
-    public void applyRestore(byte[] payload, @UserIdInt int userId) throws RemoteException {
+    public void applyRestore(byte[] payload, @UserIdInt int userId) {
         enforceSystem();
         if (DEBUG) {
             Slog.d(TAG, "Restoring user " + userId);
@@ -1923,6 +1993,15 @@
         }
         synchronized (mLock) {
             mUsers.put(userId, user);
+
+            // Then purge all the save images.
+            final File bitmapPath = getUserBitmapFilePath(userId);
+            final boolean success = FileUtils.deleteContents(bitmapPath);
+            if (!success) {
+                Slog.w(TAG, "Failed to delete " + bitmapPath);
+            }
+
+            saveUserLocked(userId);
         }
     }
 
@@ -1974,9 +2053,19 @@
             pw.print("  Icon format: ");
             pw.print(mIconPersistFormat);
             pw.print("  Icon quality: ");
-            pw.print(mIconPersistQuality);
+            pw.println(mIconPersistQuality);
             pw.println();
 
+            pw.println("  Stats:");
+            synchronized (mStatLock) {
+                final String p = "     ";
+                dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
+                dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
+
+                dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
+                dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
+                dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
+            }
 
             for (int i = 0; i < mUsers.size(); i++) {
                 pw.println();
@@ -1991,6 +2080,15 @@
         return tobj.format("%Y-%m-%d %H:%M:%S");
     }
 
+    private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) {
+        pw.print(prefix);
+        final int count = mCountStats[statId];
+        final long dur = mDurationStats[statId];
+        pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
+                label, count, dur,
+                (count == 0 ? 0 : ((double) dur) / count)));
+    }
+
     // === Shell support ===
 
     @Override
@@ -2202,9 +2300,10 @@
     }
 
     final void wtf(String message) {
-        Slog.wtf(TAG, message, /* exception= */ null);
+        wtf( message, /* exception= */ null);
     }
 
+    // Injection point.
     void wtf(String message, Exception e) {
         Slog.wtf(TAG, message, e);
     }
@@ -2275,7 +2374,7 @@
             final ShortcutUser user = mUsers.get(userId);
             if (user == null) return null;
 
-            final ShortcutPackage pkg = user.getPackages().get(packageName);
+            final ShortcutPackage pkg = user.getAllPackages().get(packageName);
             if (pkg == null) return null;
 
             return pkg.findShortcutById(shortcutId);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 487558f..593f607 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -53,8 +53,8 @@
             this.packageName = Preconditions.checkNotNull(packageName);
         }
 
-        public static PackageWithUser of(int launcherUserId, String packageName) {
-            return new PackageWithUser(launcherUserId, packageName);
+        public static PackageWithUser of(int userId, String packageName) {
+            return new PackageWithUser(userId, packageName);
         }
 
         public static PackageWithUser of(ShortcutPackageItem spi) {
@@ -78,12 +78,12 @@
 
         @Override
         public String toString() {
-            return String.format("{Launcher: %d, %s}", userId, packageName);
+            return String.format("{Package: %d, %s}", userId, packageName);
         }
     }
 
     @UserIdInt
-    final int mUserId;
+    private final int mUserId;
 
     private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
 
@@ -95,10 +95,18 @@
         mUserId = userId;
     }
 
-    public ArrayMap<String, ShortcutPackage> getPackages() {
+    public int getUserId() {
+        return mUserId;
+    }
+
+    public ArrayMap<String, ShortcutPackage> getAllPackages() {
         return mPackages;
     }
 
+    public ShortcutPackage removePackage(@NonNull String packageName) {
+        return mPackages.remove(packageName);
+    }
+
     public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
         return mLaunchers;
     }
@@ -113,22 +121,26 @@
         return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName));
     }
 
-    public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
+    public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) {
         ShortcutPackage ret = mPackages.get(packageName);
         if (ret == null) {
             ret = new ShortcutPackage(mUserId, packageName);
             mPackages.put(packageName, ret);
+        } else {
+            ret.attemptToRestoreIfNeededAndSave(s);
         }
         return ret;
     }
 
-    public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName,
+    public ShortcutLauncher getLauncherShortcuts(ShortcutService s, @NonNull String packageName,
             @UserIdInt int launcherUserId) {
         final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
         ShortcutLauncher ret = mLaunchers.get(key);
         if (ret == null) {
             ret = new ShortcutLauncher(mUserId, packageName, launcherUserId);
             mLaunchers.put(key, ret);
+        } else {
+            ret.attemptToRestoreIfNeededAndSave(s);
         }
         return ret;
     }
@@ -148,14 +160,6 @@
         }
     }
 
-    public void unshadowPackage(ShortcutService s, @NonNull String packageName,
-            @UserIdInt int packageUserId) {
-        forPackageItem(packageName, packageUserId, spi -> {
-            Slog.i(TAG, String.format("Restoring for %s, user=%d", packageName, packageUserId));
-            spi.ensureNotShadowAndSave(s);
-        });
-    }
-
     public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId,
             Consumer<ShortcutPackageItem> callback) {
         forAllPackageItems(spi -> {
@@ -166,6 +170,13 @@
         });
     }
 
+    public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName,
+            @UserIdInt int packageUserId) {
+        forPackageItem(packageName, packageUserId, spi -> {
+            spi.attemptToRestoreIfNeededAndSave(s);
+        });
+    }
+
     public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
         out.startTag(null, TAG_ROOT);
@@ -229,7 +240,7 @@
                                 s, parser, userId, fromBackup);
 
                         // Don't use addShortcut(), we don't need to save the icon.
-                        ret.getPackages().put(shortcuts.getPackageName(), shortcuts);
+                        ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
                         continue;
                     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 88f4190..3ff46ac 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1941,6 +1941,7 @@
                     long now = System.currentTimeMillis();
                     userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
                     userInfo.partial = true;
+                    userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
                     userData = new UserData();
                     userData.info = userInfo;
                     mUsers.put(userId, userData);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 747e31e..14d0457 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3107,10 +3107,8 @@
                 return -1;
             }
         } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
-            if (down) {
-                if (repeatCount == 0) {
-                    toggleKeyboardShortcutsMenu(event.getDeviceId());
-                }
+            if (down && repeatCount == 0 && !isKeyguardLocked()) {
+                toggleKeyboardShortcutsMenu(event.getDeviceId());
             }
         } else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
             if (down) {
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index 1363fb9..eb926c1 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -227,10 +227,11 @@
         return userIds;
     }
 
-    private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
+    public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
+            String serviceName, String permissionName) {
+
         ArraySet<ComponentName> installed = new ArraySet<>();
-        PackageManager pm = mContext.getPackageManager();
-        Intent queryIntent = new Intent(mServiceName);
+        Intent queryIntent = new Intent(serviceName);
         List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                 queryIntent,
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
@@ -241,10 +242,10 @@
                 ServiceInfo info = resolveInfo.serviceInfo;
 
                 ComponentName component = new ComponentName(info.packageName, info.name);
-                if (!mServicePermission.equals(info.permission)) {
+                if (!permissionName.equals(info.permission)) {
                     Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
                             + ": it does not require the permission "
-                            + mServicePermission);
+                            + permissionName);
                     continue;
                 }
                 installed.add(component);
@@ -253,6 +254,11 @@
         return installed;
     }
 
+    private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
+        return loadComponentNames(mContext.getPackageManager(), userId, mServiceName,
+                mServicePermission);
+    }
+
     private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
             int userId) {
         final ContentResolver cr = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index aa6f59e..c572e76 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -16,16 +16,23 @@
 package com.android.server.vr;
 
 import android.app.AppOpsManager;
+import android.app.NotificationManager;
 import android.annotation.NonNull;
-import android.content.Context;
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.vr.IVrListener;
 import android.service.vr.VrListenerService;
 import android.util.ArraySet;
@@ -38,7 +45,9 @@
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
 
+import java.lang.StringBuilder;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Objects;
 import java.util.Set;
 
@@ -53,8 +62,8 @@
  *  hardware/libhardware/modules/vr
  * <p/>
  * In general applications may enable or disable VR mode by calling
- * {@link android.app.Activity#setVrMode)}.  An application may also implement a service to be run
- * while in VR mode by implementing {@link android.service.vr.VrListenerService}.
+ * {@link android.app.Activity#setVrModeEnabled)}.  An application may also implement a service to
+ * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}.
  *
  * @see {@link android.service.vr.VrListenerService}
  * @see {@link com.android.server.vr.VrManagerInternal}
@@ -74,13 +83,18 @@
     private final IBinder mOverlayToken = new Binder();
 
     // State protected by mLock
-    private boolean mVrModeEnabled = false;
+    private boolean mVrModeEnabled;
     private final Set<VrStateListener> mListeners = new ArraySet<>();
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
     private Context mContext;
     private ComponentName mCurrentVrModeComponent;
     private int mCurrentVrModeUser;
+    private boolean mWasDefaultGranted;
+    private boolean mGuard;
+    private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
+    private String mPreviousNotificationPolicyAccessPackage;
+    private String mPreviousManageOverlayPackage;
 
     private static final BinderChecker sBinderChecker = new BinderChecker() {
         @Override
@@ -239,62 +253,271 @@
      *
      * @return {@code true} if the component/user combination specified is valid.
      */
-    private boolean updateCurrentVrServiceLocked(boolean enabled,
-            @NonNull ComponentName component, int userId, ComponentName calling) {
+    private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component,
+            int userId, ComponentName calling) {
 
         boolean sendUpdatedCaller = false;
+        final long identity = Binder.clearCallingIdentity();
+        try {
 
-        boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
-                EnabledComponentsObserver.NO_ERROR);
+            boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
+                    EnabledComponentsObserver.NO_ERROR);
 
-        // Always send mode change events.
-        changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
+            // Always send mode change events.
+            changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
 
-        if (!enabled || !validUserComponent) {
-            // Unbind whatever is running
-            if (mCurrentVrService != null) {
-                Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
-                        mCurrentVrService.getUserId());
-                mCurrentVrService.disconnect();
-                mCurrentVrService = null;
-            }
-        } else {
-            if (mCurrentVrService != null) {
-                // Unbind any running service that doesn't match the component/user selection
-                if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+            if (!enabled || !validUserComponent) {
+                // Unbind whatever is running
+                if (mCurrentVrService != null) {
                     Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
-                        mCurrentVrService.getUserId());
+                            mCurrentVrService.getUserId());
+                    mCurrentVrService.disconnect();
+                    disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+                            new UserHandle(mCurrentVrService.getUserId()));
+                    mCurrentVrService = null;
+                }
+            } else {
+                if (mCurrentVrService != null) {
+                    // Unbind any running service that doesn't match the component/user selection
+                    if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+                        Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() +
+                                " for user " + mCurrentVrService.getUserId());
+                        disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+                                new UserHandle(mCurrentVrService.getUserId()));
+                        createAndConnectService(component, userId);
+                        enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+                                new UserHandle(mCurrentVrService.getUserId()));
+                        sendUpdatedCaller = true;
+                    }
+                    // The service with the correct component/user is bound
+                } else {
+                    // Nothing was previously running, bind a new service
                     createAndConnectService(component, userId);
+                    enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+                            new UserHandle(mCurrentVrService.getUserId()));
                     sendUpdatedCaller = true;
                 }
-                // The service with the correct component/user is bound
-            } else {
-                // Nothing was previously running, bind a new service
-                createAndConnectService(component, userId);
+            }
+
+            if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent))  {
+                mCurrentVrModeComponent = calling;
+                mCurrentVrModeUser = userId;
                 sendUpdatedCaller = true;
             }
+
+            if (mCurrentVrService != null && sendUpdatedCaller) {
+                final ComponentName c = mCurrentVrModeComponent;
+                mCurrentVrService.sendEvent(new PendingEvent() {
+                    @Override
+                    public void runEvent(IInterface service) throws RemoteException {
+                        IVrListener l = (IVrListener) service;
+                        l.focusedActivityChanged(c);
+                    }
+                });
+            }
+
+            return validUserComponent;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Enable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
+     * component package and user.
+     *
+     * @param component the component whose package should be enabled.
+     * @param userId the user that owns the given component.
+     */
+    private void enableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
+        if (mGuard) {
+            // Impossible
+            throw new IllegalStateException("Enabling permissions without disabling.");
+        }
+        mGuard = true;
+
+        PackageManager pm = mContext.getPackageManager();
+
+        String pName = component.getPackageName();
+        if (pm == null) {
+            Slog.e(TAG, "Couldn't set implied permissions for " + pName +
+                ", PackageManager isn't running");
+            return;
+        }
+
+        ApplicationInfo info = null;
+        try {
+            info = pm.getApplicationInfo(pName, PackageManager.GET_META_DATA);
+        } catch (NameNotFoundException e) {
+        }
+
+        if (info == null) {
+            Slog.e(TAG, "Couldn't set implied permissions for " + pName + ", no such package.");
+            return;
+        }
+
+        if (!(info.isSystemApp() || info.isUpdatedSystemApp())) {
+            return; // Application is not pre-installed, avoid setting implied permissions
+        }
+
+        mWasDefaultGranted = true;
+
+        grantOverlayAccess(pName, userId);
+        grantNotificationPolicyAccess(pName);
+        grantNotificationListenerAccess(pName, userId);
+    }
+
+    /**
+     * Disable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
+     * component package and user.
+     *
+     * @param component the component whose package should be disabled.
+     * @param userId the user that owns the given component.
+     */
+    private void disableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
+        if (!mGuard) {
+            // Impossible
+            throw new IllegalStateException("Disabling permissions without enabling.");
+        }
+        mGuard = false;
+
+        PackageManager pm = mContext.getPackageManager();
+
+        if (pm == null) {
+            Slog.e(TAG, "Couldn't remove implied permissions for " + component +
+                ", PackageManager isn't running");
+            return;
+        }
+
+        String pName = component.getPackageName();
+        if (mWasDefaultGranted) {
+            revokeOverlayAccess(userId);
+            revokeNotificationPolicyAccess(pName);
+            revokeNotificiationListenerAccess();
+            mWasDefaultGranted = false;
+        }
+
+    }
+
+    private void grantOverlayAccess(String pkg, UserHandle userId) {
+        PackageManager pm = mContext.getPackageManager();
+        boolean prev = (PackageManager.PERMISSION_GRANTED ==
+                pm.checkPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, pkg));
+        mPreviousManageOverlayPackage = null;
+        if (!prev) {
+            pm.grantRuntimePermission(pkg, android.Manifest.permission.SYSTEM_ALERT_WINDOW,
+                    userId);
+            mPreviousManageOverlayPackage = pkg;
+        }
+    }
+
+    private void revokeOverlayAccess(UserHandle userId) {
+        PackageManager pm = mContext.getPackageManager();
+        if (mPreviousManageOverlayPackage != null) {
+            pm.revokeRuntimePermission(mPreviousManageOverlayPackage,
+                    android.Manifest.permission.SYSTEM_ALERT_WINDOW, userId);
+            mPreviousManageOverlayPackage = null;
+        }
+    }
+
+
+    private void grantNotificationPolicyAccess(String pkg) {
+        NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+        boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
+        mPreviousNotificationPolicyAccessPackage = null;
+        if (!prev) {
+            mPreviousNotificationPolicyAccessPackage = pkg;
+            nm.setNotificationPolicyAccessGranted(pkg, true);
+        }
+    }
+
+    private void revokeNotificationPolicyAccess(String pkg) {
+        NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+        if (mPreviousNotificationPolicyAccessPackage != null) {
+            nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
+            mPreviousNotificationPolicyAccessPackage = null;
+        }
+    }
+
+    private void grantNotificationListenerAccess(String pkg, UserHandle userId) {
+        PackageManager pm = mContext.getPackageManager();
+        ArraySet<ComponentName> possibleServices = EnabledComponentsObserver.loadComponentNames(pm,
+                userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE,
+                android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
+        ContentResolver resolver = mContext.getContentResolver();
+
+        ArraySet<String> current = getCurrentNotifListeners(resolver);
+
+        mPreviousToggledListenerSettings.clear();
+
+        for (ComponentName c : possibleServices) {
+            String flatName = c.flattenToString();
+            if (Objects.equals(c.getPackageName(), pkg)
+                    && !current.contains(flatName)) {
+                mPreviousToggledListenerSettings.add(flatName);
+                current.add(flatName);
+            }
         }
 
-        if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent))  {
-            mCurrentVrModeComponent = calling;
-            mCurrentVrModeUser = userId;
-            sendUpdatedCaller = true;
+        if (current.size() > 0) {
+            String flatSettings = formatSettings(current);
+            Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+                    flatSettings);
         }
-
-        if (mCurrentVrService != null && sendUpdatedCaller) {
-            final ComponentName c = mCurrentVrModeComponent;
-            mCurrentVrService.sendEvent(new PendingEvent() {
-                @Override
-                public void runEvent(IInterface service) throws RemoteException {
-                    IVrListener l = (IVrListener) service;
-                    l.focusedActivityChanged(c);
-                }
-            });
-        }
-
-        return validUserComponent;
     }
 
+    private void revokeNotificiationListenerAccess() {
+        if (mPreviousToggledListenerSettings.isEmpty()) {
+            return;
+        }
+
+        ContentResolver resolver = mContext.getContentResolver();
+        ArraySet<String> current = getCurrentNotifListeners(resolver);
+
+        current.removeAll(mPreviousToggledListenerSettings);
+        mPreviousToggledListenerSettings.clear();
+
+        String flatSettings = formatSettings(current);
+        Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+                flatSettings);
+    }
+
+    private ArraySet<String> getCurrentNotifListeners(ContentResolver resolver) {
+        String flat = Settings.Secure.getString(resolver,
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+        ArraySet<String> current = new ArraySet<>();
+        if (flat != null) {
+            String[] allowed = flat.split(":");
+            for (String s : allowed) {
+                current.add(s);
+            }
+        }
+        return current;
+    }
+
+    private static String formatSettings(Collection<String> c) {
+        if (c == null || c.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder b = new StringBuilder();
+        boolean start = true;
+        for (String s : c) {
+            if ("".equals(s)) {
+                continue;
+            }
+            if (!start) {
+                b.append(':');
+            }
+            b.append(s);
+            start = false;
+        }
+        return b.toString();
+    }
+
+
+
     private void createAndConnectService(@NonNull ComponentName component, int userId) {
         mCurrentVrService = VrManagerService.create(mContext, component, userId);
         mCurrentVrService.connect();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 1c5556f..fb3c6ec 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1285,6 +1285,7 @@
                 wallpaper.imageWallpaperPending = false;
                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
                     wallpaper.wallpaperId = makeWallpaperIdLocked();
+                    notifyCallbacksLocked(wallpaper);
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index cf27b97..aace5e7 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -589,7 +589,7 @@
     void overridePointerIconLw(int touchSource) {
         mTouchSource = touchSource;
         if (isFromSource(InputDevice.SOURCE_MOUSE)) {
-            InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRAB);
+            InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRABBING);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1f03c04..eea0ca0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -425,7 +425,7 @@
         if (mFullscreen
                 || !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
                 || displayContent == null
-                || displayContent.getDockedStackLocked() != null) {
+                || displayContent.getDockedStackVisibleForUserLocked() != null) {
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 0bf7102..bb0ec4c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -128,6 +128,10 @@
     // in which case a second window animation would cause jitter.
     private boolean mFreezeMovementAnimations = false;
 
+    // Temporary storage for the new bounds that should be used after the configuration change.
+    // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
+    private final Rect mBoundsAfterRotation = new Rect();
+
     TaskStack(WindowManagerService service, int stackId) {
         mService = service;
         mStackId = stackId;
@@ -343,28 +347,28 @@
             setBounds(mTmpRect2);
         } else {
             mLastUpdateDisplayInfoRotation = newRotation;
-            updateBoundsAfterRotation();
+            updateBoundsAfterRotation(true);
         }
     }
 
-    void onConfigurationChanged() {
+    boolean onConfigurationChanged() {
         mLastConfigChangedRotation = getDisplayInfo().rotation;
-        updateBoundsAfterRotation();
+        return updateBoundsAfterRotation(false);
     }
 
-    void updateBoundsAfterRotation() {
+    boolean updateBoundsAfterRotation(boolean scheduleResize) {
         if (mLastConfigChangedRotation != mLastUpdateDisplayInfoRotation) {
             // We wait for the rotation values after configuration change and display info. update
             // to be equal before updating the bounds due to rotation change otherwise things might
             // get out of alignment...
-            return;
+            return false;
         }
 
         final int newRotation = getDisplayInfo().rotation;
 
         if (mRotation == newRotation) {
             // Nothing to do here if the rotation didn't change
-            return;
+            return false;
         }
 
         mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
@@ -373,11 +377,22 @@
             snapDockedStackAfterRotation(mTmpRect2);
         }
 
-        // Post message to inform activity manager of the bounds change simulating
-        // a one-way call. We do this to prevent a deadlock between window manager
-        // lock and activity manager lock been held.
-        mService.mH.obtainMessage(
-                RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget();
+        if (scheduleResize) {
+            // Post message to inform activity manager of the bounds change simulating
+            // a one-way call. We do this to prevent a deadlock between window manager
+            // lock and activity manager lock been held.
+            mService.mH.obtainMessage(RESIZE_STACK, mStackId,
+                    0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget();
+        } else {
+            mBoundsAfterRotation.set(mTmpRect2);
+        }
+
+        return true;
+    }
+
+    void getBoundsForNewConfiguration(Rect outBounds) {
+        outBounds.set(mBoundsAfterRotation);
+        mBoundsAfterRotation.setEmpty();
     }
 
     /**
@@ -869,6 +884,10 @@
         }
     }
 
+    boolean isAdjustedForMinimizedDock() {
+        return mMinimizeAmount != 0f;
+    }
+
     private boolean adjustForIME(final WindowState imeWin) {
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dcb4a63..14ae74f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -125,6 +125,7 @@
 import com.android.internal.app.IAssistScreenshotReceiver;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IShortcutService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
@@ -515,6 +516,8 @@
 
     private final SparseIntArray mTmpTaskIds = new SparseIntArray();
 
+    private final ArrayList<Integer> mChangedStackList = new ArrayList();
+
     boolean mForceResizableTasks = false;
 
     int getDragLayerLocked() {
@@ -3398,7 +3401,8 @@
             }
         }
 
-        if (isStackVisibleLocked(DOCKED_STACK_ID)
+        if ((isStackVisibleLocked(DOCKED_STACK_ID)
+                && !mStackIdToStack.get(DOCKED_STACK_ID).isAdjustedForMinimizedDock())
                 || isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
             // We don't let app affect the system orientation when in freeform or docked mode since
             // they don't occupy the entire display and their request can conflict with other apps.
@@ -3580,7 +3584,7 @@
     }
 
     @Override
-    public void setNewConfiguration(Configuration config) {
+    public int[] setNewConfiguration(Configuration config) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setNewConfiguration()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3592,16 +3596,30 @@
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
-            onConfigurationChanged();
-            mWindowPlacerLocked.performSurfacePlacement();
+            return onConfigurationChanged();
         }
     }
 
-    private void onConfigurationChanged() {
+    @Override
+    public Rect getBoundsForNewConfiguration(int stackId) {
+        synchronized(mWindowMap) {
+            final TaskStack stack = mStackIdToStack.get(stackId);
+            final Rect outBounds = new Rect();
+            stack.getBoundsForNewConfiguration(outBounds);
+            return outBounds;
+        }
+    }
+
+    private int[] onConfigurationChanged() {
+        mChangedStackList.clear();
         for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
             final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
-            stack.onConfigurationChanged();
+            if (stack.onConfigurationChanged()) {
+                mChangedStackList.add(stack.mStackId);
+            }
         }
+        return mChangedStackList.isEmpty() ?
+                null : ArrayUtils.convertToIntArray(mChangedStackList);
     }
 
     @Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0f59302..659450e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -169,7 +169,7 @@
      * visual content.
      */
     private static final int DEFAULT_SYSTEM_THEME =
-            com.android.internal.R.style.Theme_Material_DayNight_DarkActionBar;
+            com.android.internal.R.style.Theme_Material_Light_DarkActionBar;
 
     private final int mFactoryTestMode;
     private Timer mProfilerSnapshotTimer;
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index e27f69e..a881408 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -57,6 +57,17 @@
 
     public static final int HWADDR_LEN = 16;
     public static final int MAX_OPTION_LEN = 255;
+
+    /**
+     * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
+     * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
+     * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500
+     * because in general it is risky to assume that the hardware is able to send/receive packets
+     * larger than 1500 bytes even if the network supports it.
+     */
+    private static final int MIN_MTU = 1280;
+    private static final int MAX_MTU = 1500;
+
     /**
      * IP layer definitions.
      */
@@ -917,7 +928,7 @@
                             break;
                         case DHCP_MTU:
                             expectedLen = 2;
-                            mtu = Short.valueOf(packet.getShort());
+                            mtu = packet.getShort();
                             break;
                         case DHCP_DOMAIN_NAME:
                             expectedLen = optionLen;
@@ -1106,6 +1117,8 @@
         results.serverAddress = mServerIdentifier;
         results.vendorInfo = mVendorInfo;
         results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
+        results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
+
         return results;
     }
 
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index c25fae3..d10834a 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -605,6 +605,10 @@
                 }
             }
             newLp.setDomains(mDhcpResults.domains);
+
+            if (mDhcpResults.mtu != 0) {
+                newLp.setMtu(mDhcpResults.mtu);
+            }
         }
 
         // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index 876d95b..f8eaf7d 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -261,7 +261,7 @@
 
     private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
             String domains, String serverAddress, String vendorInfo, int leaseDuration,
-            boolean hasMeteredHint, DhcpResults dhcpResults) throws Exception {
+            boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception {
         assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
         assertEquals(v4Address(gateway), dhcpResults.gateway);
 
@@ -277,6 +277,7 @@
         assertEquals(vendorInfo, dhcpResults.vendorInfo);
         assertEquals(leaseDuration, dhcpResults.leaseDuration);
         assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
+        assertEquals(mtu, dhcpResults.mtu);
     }
 
     @SmallTest
@@ -310,7 +311,7 @@
         assertTrue(offerPacket instanceof DhcpOfferPacket);  // Implicitly checks it's non-null.
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
-                null, "192.168.144.3", null, 7200, false, dhcpResults);
+                null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
     }
 
     @SmallTest
@@ -342,10 +343,70 @@
         assertTrue(offerPacket instanceof DhcpOfferPacket);  // Implicitly checks it's non-null.
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
-                null, "192.168.43.1", "ANDROID_METERED", 3600, true, dhcpResults);
+                null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults);
         assertTrue(dhcpResults.hasMeteredHint());
     }
 
+    private byte[] mtuBytes(int mtu) {
+        // 0x1a02: option 26, length 2. 0xff: no more options.
+        if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
+            throw new IllegalArgumentException(
+                String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
+        }
+        String hexString = String.format("1a02%04xff", mtu);
+        return HexEncoding.decode(hexString.toCharArray(), false);
+    }
+
+    private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
+        if (mtuBytes != null) {
+            packet.position(packet.capacity() - mtuBytes.length);
+            packet.put(mtuBytes);
+            packet.clear();
+        }
+        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+        assertTrue(offerPacket instanceof DhcpOfferPacket);  // Implicitly checks it's non-null.
+        DhcpResults dhcpResults = offerPacket.toDhcpResults();
+        assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
+                null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
+    }
+
+    @SmallTest
+    public void testMtu() throws Exception {
+        final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+            // IP header.
+            "451001480000000080118849c0a89003c0a89ff7" +
+            // UDP header.
+            "004300440134dcfa" +
+            // BOOTP header.
+            "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
+            // MAC address.
+            "30766ff2a90c00000000000000000000" +
+            // Server name.
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            // File.
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            // Options
+            "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
+            "3a0400000e103b040000189cff00000000"
+        ).toCharArray(), false));
+
+        checkMtu(packet, 0, null);
+        checkMtu(packet, 0, mtuBytes(1501));
+        checkMtu(packet, 1500, mtuBytes(1500));
+        checkMtu(packet, 1499, mtuBytes(1499));
+        checkMtu(packet, 1280, mtuBytes(1280));
+        checkMtu(packet, 0, mtuBytes(1279));
+        checkMtu(packet, 0, mtuBytes(576));
+        checkMtu(packet, 0, mtuBytes(68));
+        checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
+        checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
+        checkMtu(packet, 0, mtuBytes(-1));
+    }
+
     @SmallTest
     public void testBadHwaddrLength() throws Exception {
         final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
@@ -453,7 +514,7 @@
         assertTrue(offerPacket instanceof DhcpOfferPacket);
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
-                null, "1.1.1.1", null, 43200, false, dhcpResults);
+                null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
     }
 
     @SmallTest
@@ -484,7 +545,7 @@
         assertTrue(offerPacket instanceof DhcpOfferPacket);
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
-                "domain123.co.uk", "192.0.2.254", null, 49094, false, dhcpResults);
+                "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
     }
 
     @SmallTest
@@ -518,7 +579,7 @@
         assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
-                "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults);
+                "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
     }
 
     @SmallTest
@@ -554,7 +615,7 @@
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("10.15.122.242/16", "10.15.200.23",
                 "209.129.128.3,209.129.148.3,209.129.128.6",
-                "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults);
+                "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
     }
 
     @SmallTest
@@ -621,7 +682,7 @@
         assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
         DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
-                null, "192.171.189.2", null, 28800, false, dhcpResults);
+                null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
     }
 
     @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
new file mode 100644
index 0000000..83a59fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -0,0 +1,541 @@
+/*
+ * 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.notification;
+
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.Vibrator;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BuzzBeepBlinkTest extends AndroidTestCase {
+
+    @Mock AudioManager mAudioManager;
+    @Mock Vibrator mVibrator;
+    @Mock android.media.IRingtonePlayer mRingtonePlayer;
+    @Mock Handler mHandler;
+
+    private NotificationManagerService mService;
+    private String mPkg = "com.android.server.notification";
+    private int mId = 1001;
+    private int mOtherId = 1002;
+    private String mTag = null;
+    private int mUid = 1000;
+    private int mPid = 2000;
+    private int mScore = 10;
+    private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+    @Override
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
+        when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
+        when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+
+        mService = new NotificationManagerService(getContext());
+        mService.setAudioManager(mAudioManager);
+        mService.setVibrator(mVibrator);
+        mService.setSystemReady(true);
+        mService.setHandler(mHandler);
+    }
+
+    //
+    // Convenience functions for creating notification records
+    //
+
+    private NotificationRecord getNoisyOtherNotification() {
+        return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+                true /* noisy */, true /* buzzy*/);
+    }
+
+    private NotificationRecord getBeepyNotification() {
+        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/);
+    }
+
+    private NotificationRecord getBeepyOnceNotification() {
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
+                true /* noisy */, false /* buzzy*/);
+    }
+
+    private NotificationRecord getQuietNotification() {
+        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+                false /* noisy */, false /* buzzy*/);
+    }
+
+    private NotificationRecord getQuietOtherNotification() {
+        return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+                false /* noisy */, false /* buzzy*/);
+    }
+
+    private NotificationRecord getQuietOnceNotification() {
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
+                false /* noisy */, false /* buzzy*/);
+    }
+
+    private NotificationRecord getInsistentBeepyNotification() {
+        return getNotificationRecord(mId, true /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/);
+    }
+
+    private NotificationRecord getBuzzyNotification() {
+        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+                false /* noisy */, true /* buzzy*/);
+    }
+
+    private NotificationRecord getBuzzyOnceNotification() {
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
+                false /* noisy */, true /* buzzy*/);
+    }
+
+    private NotificationRecord getInsistentBuzzyNotification() {
+        return getNotificationRecord(mId, true /* insistent */, false /* once */,
+                false /* noisy */, true /* buzzy*/);
+    }
+
+    private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+            boolean noisy, boolean buzzy) {
+        final Builder builder = new Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setOnlyAlertOnce(once);
+
+        int defaults = 0;
+        if (noisy) {
+            defaults |= Notification.DEFAULT_SOUND;
+        }
+        if (buzzy) {
+            defaults |= Notification.DEFAULT_VIBRATE;
+        }
+        builder.setDefaults(defaults);
+
+        Notification n = builder.build();
+        if (insistent) {
+            n.flags |= Notification.FLAG_INSISTENT;
+        }
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid,
+                mScore, n, mUser, System.currentTimeMillis());
+        return new NotificationRecord(getContext(), sbn);
+    }
+
+    //
+    // Convenience functions for interacting with mocks
+    //
+
+    private void verifyNeverBeep() throws RemoteException {
+        verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+                anyBoolean(), (AudioAttributes) anyObject());
+    }
+
+    private void verifyBeep() throws RemoteException {
+        verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+                eq(true), (AudioAttributes) anyObject());
+    }
+
+    private void verifyBeepLooped() throws RemoteException {
+        verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+                eq(false), (AudioAttributes) anyObject());
+    }
+
+    private void verifyNeverStopAudio() throws RemoteException {
+        verify(mRingtonePlayer, never()).stopAsync();
+    }
+
+    private void verifyStopAudio() throws RemoteException {
+        verify(mRingtonePlayer, times(1)).stopAsync();
+    }
+
+    private void verifyNeverVibrate() {
+        verify(mVibrator, never()).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+                anyInt(), (AudioAttributes) anyObject());
+    }
+
+    private void verifyVibrate() {
+        verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+                eq(-1), (AudioAttributes) anyObject());
+    }
+
+    private void verifyVibrateLooped() {
+        verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+                eq(0), (AudioAttributes) anyObject());
+    }
+
+    private void verifyStopVibrate() {
+        verify(mVibrator, times(1)).cancel();
+    }
+
+    private void verifyNeverStopVibrate() throws RemoteException {
+        verify(mVibrator, never()).cancel();
+    }
+
+    @SmallTest
+    public void testBeep() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+        verifyNeverVibrate();
+    }
+
+    //
+    // Tests
+    //
+
+    @SmallTest
+    public void testBeepInsistently() throws Exception {
+        NotificationRecord r = getInsistentBeepyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeep();
+    }
+
+    @SmallTest
+    public void testNoInterruptionForMin() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setImportance(Ranking.IMPORTANCE_MIN, "foo");
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        verifyNeverVibrate();
+    }
+
+    @SmallTest
+    public void testNoInterruptionForIntercepted() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setIntercepted(true);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        verifyNeverVibrate();
+    }
+
+    @SmallTest
+    public void testBeepTwice() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mRingtonePlayer);
+
+        // update should beep
+        r.isUpdate = true;
+        mService.buzzBeepBlinkLocked(r);
+        verifyBeepLooped();
+    }
+
+    @SmallTest
+    public void testHonorAlertOnlyOnceForBeep() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord s = getBeepyOnceNotification();
+        s.isUpdate = true;
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mRingtonePlayer);
+
+        // update should not beep
+        mService.buzzBeepBlinkLocked(s);
+        verifyNeverBeep();
+    }
+
+    @SmallTest
+    public void testNoisyUpdateDoesNotCancelAudio() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+        r.isUpdate = true;
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverStopAudio();
+    }
+
+    @SmallTest
+    public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord s = getBeepyOnceNotification();
+        s.isUpdate = true;
+
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(s);
+
+        verifyNeverStopAudio();
+    }
+
+    @SmallTest
+    public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord s = getQuietNotification();
+        s.isUpdate = true;
+        NotificationRecord other = getNoisyOtherNotification();
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(other); // this takes the audio stream
+        Mockito.reset(mRingtonePlayer);
+
+        // should not stop noise, since we no longer own it
+        mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
+        verifyNeverStopAudio();
+    }
+
+    @SmallTest
+    public void testQuietInterloperDoesNotCancelAudio() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord other = getQuietOtherNotification();
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mRingtonePlayer);
+
+        // should not stop noise, since it does not own it
+        mService.buzzBeepBlinkLocked(other);
+        verifyNeverStopAudio();
+    }
+
+    @SmallTest
+    public void testQuietUpdateCancelsAudio() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord s = getQuietNotification();
+        s.isUpdate = true;
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mRingtonePlayer);
+
+        // quiet update should stop making noise
+        mService.buzzBeepBlinkLocked(s);
+        verifyStopAudio();
+    }
+
+    @SmallTest
+    public void testQuietOnceUpdateCancelsAudio() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord s = getQuietOnceNotification();
+        s.isUpdate = true;
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mRingtonePlayer);
+
+        // stop making noise - this is a weird corner case, but quiet should override once
+        mService.buzzBeepBlinkLocked(s);
+        verifyStopAudio();
+    }
+
+    @SmallTest
+    public void testDemoteSoundToVibrate() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        // the phone is quiet
+        when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        verifyVibrate();
+    }
+
+    @SmallTest
+    public void testDemotInsistenteSoundToVibrate() throws Exception {
+        NotificationRecord r = getInsistentBeepyNotification();
+
+        // the phone is quiet
+        when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyVibrateLooped();
+    }
+
+    @SmallTest
+    public void testVibrate() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        verifyVibrate();
+    }
+
+    @SmallTest
+    public void testInsistenteVibrate() throws Exception {
+        NotificationRecord r = getInsistentBuzzyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+        verifyVibrateLooped();
+    }
+
+    @SmallTest
+    public void testVibratTwice() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mVibrator);
+
+        // update should vibrate
+        r.isUpdate = true;
+        mService.buzzBeepBlinkLocked(r);
+        verifyVibrate();
+    }
+
+    @SmallTest
+    public void testHonorAlertOnlyOnceForBuzz() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+        NotificationRecord s = getBuzzyOnceNotification();
+        s.isUpdate = true;
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mVibrator);
+
+        // update should not beep
+        mService.buzzBeepBlinkLocked(s);
+        verifyNeverVibrate();
+    }
+
+    @SmallTest
+    public void testNoisyUpdateDoesNotCancelVibrate() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+        r.isUpdate = true;
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverStopVibrate();
+    }
+
+    @SmallTest
+    public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+        NotificationRecord s = getBuzzyOnceNotification();
+        s.isUpdate = true;
+
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(s);
+
+        verifyNeverStopVibrate();
+    }
+
+    @SmallTest
+    public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+        NotificationRecord s = getQuietNotification();
+        s.isUpdate = true;
+        NotificationRecord other = getNoisyOtherNotification();
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(other); // this takes the vibrate stream
+        Mockito.reset(mVibrator);
+
+        // should not stop vibrate, since we no longer own it
+        mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
+        verifyNeverStopVibrate();
+    }
+
+    @SmallTest
+    public void testQuietInterloperDoesNotCancelVibrate() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+        NotificationRecord other = getQuietOtherNotification();
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mVibrator);
+
+        // should not stop noise, since it does not own it
+        mService.buzzBeepBlinkLocked(other);
+        verifyNeverStopVibrate();
+    }
+
+    @SmallTest
+    public void testQuietUpdateCancelsVibrate() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+        NotificationRecord s = getQuietNotification();
+        s.isUpdate = true;
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+
+        // quiet update should stop making noise
+        mService.buzzBeepBlinkLocked(s);
+        verifyStopVibrate();
+    }
+
+    @SmallTest
+    public void testQuietOnceUpdateCancelsvibrate() throws Exception {
+        NotificationRecord r = getBuzzyNotification();
+        NotificationRecord s = getQuietOnceNotification();
+        s.isUpdate = true;
+
+        // set up internal state
+        mService.buzzBeepBlinkLocked(r);
+        Mockito.reset(mVibrator);
+
+        // stop making noise - this is a weird corner case, but quiet should override once
+        mService.buzzBeepBlinkLocked(s);
+        verifyStopVibrate();
+    }
+
+    @SmallTest
+    public void testQuietUpdateCancelsDemotedVibrate() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        NotificationRecord s = getQuietNotification();
+
+        // the phone is quiet
+        when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        // quiet update should stop making noise
+        mService.buzzBeepBlinkLocked(s);
+        verifyStopVibrate();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
deleted file mode 100644
index c44ffa4..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
+++ /dev/null
@@ -1,271 +0,0 @@
-
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.pm;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.PersistableBundle;
-import android.test.AndroidTestCase;
-
-import com.android.internal.util.Preconditions;
-import com.android.server.testutis.TestUtils;
-
-/**
- * Tests for {@link ShortcutInfo}.
-
- m FrameworksServicesTests &&
- adb install \
-   -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
- adb shell am instrument -e class com.android.server.pm.ShortcutInfoTest \
-   -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
-
- */
-public class ShortcutInfoTest extends AndroidTestCase {
-
-    public void testMissingMandatoryFields() {
-        TestUtils.assertExpectException(
-                IllegalArgumentException.class,
-                "ID must be provided",
-                () -> new ShortcutInfo.Builder(mContext).build());
-        TestUtils.assertExpectException(
-                IllegalArgumentException.class,
-                "title must be provided",
-                () -> new ShortcutInfo.Builder(mContext).setId("id").build()
-                        .enforceMandatoryFields());
-        TestUtils.assertExpectException(
-                NullPointerException.class,
-                "Intent must be provided",
-                () -> new ShortcutInfo.Builder(mContext).setId("id").setTitle("x").build()
-                        .enforceMandatoryFields());
-    }
-
-    private ShortcutInfo parceled(ShortcutInfo si) {
-        Parcel p = Parcel.obtain();
-        p.writeParcelable(si, 0);
-        p.setDataPosition(0);
-        ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader());
-        p.recycle();
-        return si2;
-    }
-
-    private Intent makeIntent(String action, Object... bundleKeysAndValues) {
-        final Intent intent = new Intent(action);
-        intent.replaceExtras(ShortcutManagerTest.makeBundle(bundleKeysAndValues));
-        return intent;
-    }
-
-    public void testParcel() {
-        ShortcutInfo si = parceled(new ShortcutInfo.Builder(getContext())
-                .setId("id")
-                .setTitle("title")
-                .setIntent(makeIntent("action"))
-                .build());
-        assertEquals(getContext().getPackageName(), si.getPackageName());
-        assertEquals("id", si.getId());
-        assertEquals("title", si.getTitle());
-        assertEquals("action", si.getIntent().getAction());
-
-        PersistableBundle pb = new PersistableBundle();
-        pb.putInt("k", 1);
-
-        si = new ShortcutInfo.Builder(getContext())
-                .setId("id")
-                .setActivityComponent(new ComponentName("a", "b"))
-                .setIcon(Icon.createWithContentUri("content://a.b.c/"))
-                .setTitle("title")
-                .setText("text")
-                .setIntent(makeIntent("action", "key", "val"))
-                .setWeight(123)
-                .setExtras(pb)
-                .build();
-        si.addFlags(ShortcutInfo.FLAG_PINNED);
-        si.setBitmapPath("abc");
-        si.setIconResourceId(456);
-
-        si = parceled(si);
-
-        assertEquals(getContext().getPackageName(), si.getPackageName());
-        assertEquals("id", si.getId());
-        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
-        assertEquals("content://a.b.c/", si.getIcon().getUriString());
-        assertEquals("title", si.getTitle());
-        assertEquals("text", si.getText());
-        assertEquals("action", si.getIntent().getAction());
-        assertEquals("val", si.getIntent().getStringExtra("key"));
-        assertEquals(123, si.getWeight());
-        assertEquals(1, si.getExtras().getInt("k"));
-
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
-        assertEquals("abc", si.getBitmapPath());
-        assertEquals(456, si.getIconResourceId());
-    }
-
-    public void testClone() {
-        PersistableBundle pb = new PersistableBundle();
-        pb.putInt("k", 1);
-        ShortcutInfo sorig = new ShortcutInfo.Builder(getContext())
-                .setId("id")
-                .setActivityComponent(new ComponentName("a", "b"))
-                .setIcon(Icon.createWithContentUri("content://a.b.c/"))
-                .setTitle("title")
-                .setText("text")
-                .setIntent(makeIntent("action", "key", "val"))
-                .setWeight(123)
-                .setExtras(pb)
-                .build();
-        sorig.addFlags(ShortcutInfo.FLAG_PINNED);
-        sorig.setBitmapPath("abc");
-        sorig.setIconResourceId(456);
-
-        ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
-
-        assertEquals(getContext().getPackageName(), si.getPackageName());
-        assertEquals("id", si.getId());
-        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
-        assertEquals("content://a.b.c/", si.getIcon().getUriString());
-        assertEquals("title", si.getTitle());
-        assertEquals("text", si.getText());
-        assertEquals("action", si.getIntent().getAction());
-        assertEquals("val", si.getIntent().getStringExtra("key"));
-        assertEquals(123, si.getWeight());
-        assertEquals(1, si.getExtras().getInt("k"));
-
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
-        assertEquals("abc", si.getBitmapPath());
-        assertEquals(456, si.getIconResourceId());
-
-        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
-
-        assertEquals(getContext().getPackageName(), si.getPackageName());
-        assertEquals("id", si.getId());
-        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
-        assertEquals(null, si.getIcon());
-        assertEquals("title", si.getTitle());
-        assertEquals("text", si.getText());
-        assertEquals("action", si.getIntent().getAction());
-        assertEquals("val", si.getIntent().getStringExtra("key"));
-        assertEquals(123, si.getWeight());
-        assertEquals(1, si.getExtras().getInt("k"));
-
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
-        assertEquals(null, si.getBitmapPath());
-        assertEquals(0, si.getIconResourceId());
-
-        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
-
-        assertEquals(getContext().getPackageName(), si.getPackageName());
-        assertEquals("id", si.getId());
-        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
-        assertEquals(null, si.getIcon());
-        assertEquals("title", si.getTitle());
-        assertEquals("text", si.getText());
-        assertEquals(null, si.getIntent());
-        assertEquals(123, si.getWeight());
-        assertEquals(1, si.getExtras().getInt("k"));
-
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
-        assertEquals(null, si.getBitmapPath());
-        assertEquals(0, si.getIconResourceId());
-
-        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
-
-        assertEquals(getContext().getPackageName(), si.getPackageName());
-        assertEquals("id", si.getId());
-        assertEquals(null, si.getActivityComponent());
-        assertEquals(null, si.getIcon());
-        assertEquals(null, si.getTitle());
-        assertEquals(null, si.getText());
-        assertEquals(null, si.getIntent());
-        assertEquals(0, si.getWeight());
-        assertEquals(null, si.getExtras());
-
-        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
-        assertEquals(null, si.getBitmapPath());
-        assertEquals(0, si.getIconResourceId());
-    }
-
-
-    public void testCopyNonNullFieldsFrom() {
-        PersistableBundle pb = new PersistableBundle();
-        pb.putInt("k", 1);
-        ShortcutInfo sorig = new ShortcutInfo.Builder(getContext())
-                .setId("id")
-                .setActivityComponent(new ComponentName("a", "b"))
-                .setIcon(Icon.createWithContentUri("content://a.b.c/"))
-                .setTitle("title")
-                .setText("text")
-                .setIntent(makeIntent("action", "key", "val"))
-                .setWeight(123)
-                .setExtras(pb)
-                .build();
-        sorig.addFlags(ShortcutInfo.FLAG_PINNED);
-        sorig.setBitmapPath("abc");
-        sorig.setIconResourceId(456);
-
-        ShortcutInfo si;
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setActivityComponent(new ComponentName("x", "y")).build());
-        assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
-        assertEquals("content://x.y.z/", si.getIcon().getUriString());
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setTitle("xyz").build());
-        assertEquals("xyz", si.getTitle());
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setText("xxx").build());
-        assertEquals("xxx", si.getText());
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setIntent(makeIntent("action2")).build());
-        assertEquals("action2", si.getIntent().getAction());
-        assertEquals(null, si.getIntent().getStringExtra("key"));
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setIntent(makeIntent("action3", "key", "x")).build());
-        assertEquals("action3", si.getIntent().getAction());
-        assertEquals("x", si.getIntent().getStringExtra("key"));
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setWeight(999).build());
-        assertEquals(999, si.getWeight());
-
-
-        PersistableBundle pb2 = new PersistableBundle();
-        pb2.putInt("x", 99);
-
-        si = sorig.clone(/* flags=*/ 0);
-        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id")
-                .setExtras(pb2).build());
-        assertEquals(99, si.getExtras().getInt("x"));
-    }
-}
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 5d29242..0e2a80c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -57,7 +57,9 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -76,6 +78,7 @@
 import com.android.server.pm.ShortcutService.ConfigConstants;
 import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.testutis.TestUtils;
 
 import libcore.io.IoUtils;
 
@@ -96,6 +99,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Tests for ShortcutService and ShortcutManager.
@@ -107,10 +111,8 @@
  -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
 
  * TODO: Add checks with assertAllNotHaveIcon()
- *
- * TODO: separate, detailed tests for ShortcutInfo (CTS?) *
- *
- * TODO: Cross-user test (do in CTS?)
+ * TODO: Detailed test for hasShortcutPermissionInner().
+ * TODO: Add tests for the command line functions too.
  */
 @SmallTest
 public class ShortcutManagerTest extends InstrumentationTestCase {
@@ -122,6 +124,8 @@
      */
     private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
 
+    private static final boolean DUMP_ON_TEARDOWN = false; // DO NOT SUBMIT WITH true
+
     // public for mockito
     public class BaseContext extends MockContext {
         @Override
@@ -261,7 +265,8 @@
         @Override
         boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
             // Sort of hack; do a simpler check.
-            return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage);
+            return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage)
+                    || LAUNCHER_3.equals(callingPackage) || LAUNCHER_4.equals(callingPackage);
         }
 
         @Override
@@ -394,7 +399,7 @@
 
     private Map<String, PackageInfo> mInjectedPackages;
 
-    private ArrayList<PackageWithUser> mUninstalledPackages;
+    private Set<PackageWithUser> mUninstalledPackages;
 
     private PackageManager mMockPackageManager;
     private PackageManagerInternal mMockPackageManagerInternal;
@@ -409,12 +414,21 @@
     private static final String CALLING_PACKAGE_3 = "com.android.test.3";
     private static final int CALLING_UID_3 = 10003;
 
+    private static final String CALLING_PACKAGE_4 = "com.android.test.4";
+    private static final int CALLING_UID_4 = 10004;
+
     private static final String LAUNCHER_1 = "com.android.launcher.1";
     private static final int LAUNCHER_UID_1 = 10011;
 
     private static final String LAUNCHER_2 = "com.android.launcher.2";
     private static final int LAUNCHER_UID_2 = 10012;
 
+    private static final String LAUNCHER_3 = "com.android.launcher.3";
+    private static final int LAUNCHER_UID_3 = 10013;
+
+    private static final String LAUNCHER_4 = "com.android.launcher.4";
+    private static final int LAUNCHER_UID_4 = 10014;
+
     private static final int USER_0 = UserHandle.USER_SYSTEM;
     private static final int USER_10 = 10;
     private static final int USER_11 = 11;
@@ -438,6 +452,13 @@
 
     private static final int MAX_ICON_DIMENSION_LOWRAM = 32;
 
+    private static final ShortcutQuery QUERY_ALL = new ShortcutQuery();
+
+    static {
+        QUERY_ALL.setQueryFlags(
+                ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -457,10 +478,19 @@
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1);
         addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2);
         addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3);
+        addPackage(CALLING_PACKAGE_4, CALLING_UID_4, 10);
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4);
         addPackage(LAUNCHER_2, LAUNCHER_UID_2, 5);
+        addPackage(LAUNCHER_3, LAUNCHER_UID_3, 6);
+        addPackage(LAUNCHER_4, LAUNCHER_UID_4, 10);
 
-        mUninstalledPackages = new ArrayList<>();
+        // CALLING_PACKAGE_3 / LAUNCHER_3 are not backup target.
+        updatePackageInfo(CALLING_PACKAGE_3,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+        updatePackageInfo(LAUNCHER_3,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+        mUninstalledPackages = new HashSet<>();
 
         mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
 
@@ -475,6 +505,13 @@
         setCaller(CALLING_PACKAGE_1);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        if (DUMP_ON_TEARDOWN) dumpsysOnLogcat("Teardown");
+
+        super.tearDown();
+    }
+
     private Context getTestContext() {
         return getInstrumentation().getContext();
     }
@@ -504,6 +541,10 @@
         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++){
@@ -529,10 +570,24 @@
         mInjectedPackages.put(packageName, genPackage(packageName, uid, version, signatures));
     }
 
+    private void updatePackageInfo(String packageName, Consumer<PackageInfo> c) {
+        c.accept(mInjectedPackages.get(packageName));
+    }
+
     private void uninstallPackage(int userId, String packageName) {
+        if (ENABLE_DUMP) {
+            Log.i(TAG, "Unnstall package " + packageName + " / " + userId);
+        }
         mUninstalledPackages.add(PackageWithUser.of(userId, packageName));
     }
 
+    private void installPackage(int userId, String packageName) {
+        if (ENABLE_DUMP) {
+            Log.i(TAG, "Install package " + packageName + " / " + userId);
+        }
+        mUninstalledPackages.remove(PackageWithUser.of(userId, packageName));
+    }
+
     PackageInfo getInjectedPackageInfo(String packageName, @UserIdInt int userId,
             boolean getSignatures) {
         final PackageInfo pi = mInjectedPackages.get(packageName);
@@ -591,14 +646,22 @@
 
     /** For debugging */
     private void dumpsysOnLogcat() {
-        if (!ENABLE_DUMP) return;
+        dumpsysOnLogcat("");
+    }
+
+    private void dumpsysOnLogcat(String message) {
+        dumpsysOnLogcat(message, false);
+    }
+
+    private void dumpsysOnLogcat(String message, boolean force) {
+        if (force || !ENABLE_DUMP) return;
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         final PrintWriter pw = new PrintWriter(out);
         mService.dumpInner(pw);
         pw.close();
 
-        Log.e(TAG, "Dumping ShortcutService:");
+        Log.e(TAG, "Dumping ShortcutService: " + message);
         for (String line : out.toString().split("\n")) {
             Log.e(TAG, line);
         }
@@ -608,9 +671,13 @@
      * For debugging, dump arbitrary file on logcat.
      */
     private void dumpFileOnLogcat(String path) {
+        dumpFileOnLogcat(path, "");
+    }
+
+    private void dumpFileOnLogcat(String path, String message) {
         if (!ENABLE_DUMP) return;
 
-        Log.i(TAG, "Dumping file: " + path);
+        Log.i(TAG, "Dumping file: " + path + " " + message);
         final StringBuilder sb = new StringBuilder();
         try (BufferedReader br = new BufferedReader(new FileReader(path))) {
             String line;
@@ -636,10 +703,14 @@
      * For debugging, dump per-user state file on logcat.
      */
     private void dumpUserFile(int userId) {
+        dumpUserFile(userId, "");
+    }
+
+    private void dumpUserFile(int userId, String message) {
         mService.saveDirtyInfo();
         dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
                 + "/user-" + userId
-                + "/" + ShortcutService.FILENAME_USER_PACKAGES);
+                + "/" + ShortcutService.FILENAME_USER_PACKAGES, message);
     }
 
     private void waitOnMainThread() throws Throwable {
@@ -1055,11 +1126,13 @@
         return i;
     }
 
-    /**
-     * Wrap a set in an ArraySet just to get a better toString.
-     */
-    private <T> Set<T> set(Set<T> in) {
-        return new ArraySet<T>(in);
+    private ShortcutInfo parceled(ShortcutInfo si) {
+        Parcel p = Parcel.obtain();
+        p.writeParcelable(si, 0);
+        p.setDataPosition(0);
+        ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader());
+        p.recycle();
+        return si2;
     }
 
     /**
@@ -1320,8 +1393,6 @@
 
         // Still 2 calls left.
         assertEquals(2, mManager.getRemainingCallCount());
-
-        // TODO Make sure pinned shortcuts won't be deleted.
     }
 
     public void testDeleteAllDynamicShortcuts() {
@@ -1351,8 +1422,6 @@
 
         // Still 1 call left
         assertEquals(1, mManager.getRemainingCallCount());
-
-        // TODO Make sure pinned shortcuts won't be deleted.
     }
 
     public void testThrottling() {
@@ -1877,9 +1946,6 @@
         });
     }
 
-    // TODO: updateShortcuts()
-    // TODO: getPinnedShortcuts()
-
     // === Test for launcher side APIs ===
 
     private static ShortcutQuery buildQuery(long changedSince,
@@ -1893,6 +1959,20 @@
         return q;
     }
 
+    private static ShortcutQuery buildAllQuery(String packageName) {
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setPackage(packageName);
+        q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+        return q;
+    }
+
+    private static ShortcutQuery buildPinnedQuery(String packageName) {
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setPackage(packageName);
+        q.setQueryFlags(ShortcutQuery.FLAG_GET_PINNED);
+        return q;
+    }
+
     public void testGetShortcuts() {
 
         // Set up shortcuts.
@@ -3052,6 +3132,7 @@
 
         // Remove CALLING_PACKAGE_2
         reset(c0);
+        uninstallPackage(USER_0, CALLING_PACKAGE_2);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0, USER_0);
 
         // Should get a callback with an empty list.
@@ -3299,9 +3380,9 @@
         // Check the registered packages.
         dumpsysOnLogcat();
         assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3326,13 +3407,14 @@
         mService.saveDirtyInfo();
 
         // Nonexistent package.
+        uninstallPackage(USER_0, "abc");
         mService.cleanUpPackageLocked("abc", USER_0, USER_0);
 
         // No changes.
         assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3357,12 +3439,13 @@
         mService.saveDirtyInfo();
 
         // Remove a package.
+        uninstallPackage(USER_0, CALLING_PACKAGE_1);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0);
 
         assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3387,12 +3470,13 @@
         mService.saveDirtyInfo();
 
         // Remove a launcher.
+        uninstallPackage(USER_10, LAUNCHER_1);
         mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10);
 
         assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3414,12 +3498,13 @@
         mService.saveDirtyInfo();
 
         // Remove a package.
+        uninstallPackage(USER_10, CALLING_PACKAGE_2);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10);
 
         assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(CALLING_PACKAGE_1),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3441,12 +3526,13 @@
         mService.saveDirtyInfo();
 
         // Remove the other launcher from user 10 too.
+        uninstallPackage(USER_10, LAUNCHER_2);
         mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10);
 
         assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(CALLING_PACKAGE_1),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3468,12 +3554,13 @@
         mService.saveDirtyInfo();
 
         // More remove.
+        uninstallPackage(USER_10, CALLING_PACKAGE_1);
         mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10);
 
         assertEquals(makeSet(CALLING_PACKAGE_2),
-                set(user0.getPackages().keySet()));
+                set(user0.getAllPackages().keySet()));
         assertEquals(makeSet(),
-                set(user10.getPackages().keySet()));
+                set(user10.getAllPackages().keySet()));
         assertEquals(
                 makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
                         PackageWithUser.of(USER_0, LAUNCHER_2)),
@@ -3494,97 +3581,6 @@
         mService.saveDirtyInfo();
     }
 
-
-    public void testSaveAndLoadUser_forBackup() {
-        // Create some shortcuts.
-        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
-        });
-        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
-        });
-        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
-        });
-        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
-        });
-
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
-
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
-
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
-
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
-
-        // Pin some.
-
-        runWithCaller(LAUNCHER_1, USER_0, () -> {
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
-                    list("s1"), HANDLE_USER_0);
-
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
-                    list("s2"), UserHandle.of(USER_P0));
-
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
-                    list("s3"), HANDLE_USER_0);
-        });
-
-        runWithCaller(LAUNCHER_1, USER_P0, () -> {
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
-                    list("s2"), HANDLE_USER_0);
-
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
-                    list("s3"), UserHandle.of(USER_P0));
-
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
-                    list("s1"), HANDLE_USER_0);
-        });
-
-        runWithCaller(LAUNCHER_1, USER_10, () -> {
-            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
-                    list("s3"), HANDLE_USER_10);
-        });
-
-        // Check the state.
-
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
-
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
-
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
-
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
-        assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
-        assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
-
-        // Make sure all the information is persisted.
-        mService.saveDirtyInfo();
-        initService();
-        mService.handleUnlockUser(USER_0);
-        mService.handleUnlockUser(USER_P0);
-        mService.handleUnlockUser(USER_10);
-    }
-
     public void testHandleGonePackage_crossProfile() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -3841,13 +3837,9 @@
         assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
     }
 
-    // TODO Detailed test for hasShortcutPermissionInner().
-
-    // TODO Add tests for the command line functions too.
-
     private void checkCanRestoreTo(boolean expected, ShortcutPackageInfo spi,
             int version, String... signatures) {
-        assertEquals(expected, spi.canRestoreTo(genPackage(
+        assertEquals(expected, spi.canRestoreTo(mService, genPackage(
                 "dummy", /* uid */ 0, version, signatures)));
     }
 
@@ -3919,6 +3911,7 @@
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
 
+        uninstallPackage(USER_0, CALLING_PACKAGE_1);
         mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
 
@@ -3929,6 +3922,7 @@
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
         assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
 
+        uninstallPackage(USER_10, CALLING_PACKAGE_2);
         mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
 
@@ -3961,7 +3955,1163 @@
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
     }
 
-    public void testHandlePackageUpdate() {
-        // TODO: Make sure unshadow is called.
+    private void backupAndRestore() {
+        int prevUid = mInjectedCallingUid;
+
+        mInjectedCallingUid = Process.SYSTEM_UID; // Only system can call it.
+
+        dumpsysOnLogcat("Before backup");
+
+        final byte[] payload =  mService.getBackupPayload(USER_0);
+        if (ENABLE_DUMP) {
+            final String xml = new String(payload);
+            Log.i(TAG, "Backup payload:");
+            for (String line : xml.split("\n")) {
+                Log.i(TAG, line);
+            }
+        }
+
+        // Before doing anything else, uninstall all packages.
+        for (int userId : list(USER_0, USER_P0)) {
+            for (String pkg : list(CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3,
+                    LAUNCHER_1, LAUNCHER_2, LAUNCHER_3)) {
+                uninstallPackage(userId, pkg);
+            }
+        }
+
+        initService();
+        mService.applyRestore(payload, USER_0);
+
+        // handleUnlockUser will perform the gone package check, but it shouldn't remove
+        // shadow information.
+        mService.handleUnlockUser(USER_0);
+
+        dumpsysOnLogcat("After restore");
+
+        mInjectedCallingUid = prevUid;
+    }
+
+    private void prepareCrossProfileDataSet() {
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+                    makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+                    makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+        });
+        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+                    makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+        });
+        runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list()));
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+                    makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("x1"), makeShortcut("x2"), makeShortcut("x3"),
+                    makeShortcut("x4"), makeShortcut("x5"), makeShortcut("x6"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s1", "s2"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s1", "s2", "s3"), HANDLE_USER_0);
+
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s4"), HANDLE_USER_P0);
+        });
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s2", "s3"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s2", "s3", "s4"), HANDLE_USER_0);
+
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s5"), HANDLE_USER_P0);
+        });
+        runWithCaller(LAUNCHER_3, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5"), HANDLE_USER_0);
+
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s6"), HANDLE_USER_P0);
+        });
+        runWithCaller(LAUNCHER_4, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list(), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_4, list(), HANDLE_USER_0);
+        });
+
+        // Launcher on a managed profile is referring ot user 0!
+        runWithCaller(LAUNCHER_1, USER_P0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s4"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4", "s5"), HANDLE_USER_0);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5", "s6"),
+                    HANDLE_USER_0);
+
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s4", "s1"), HANDLE_USER_P0);
+        });
+        runWithCaller(LAUNCHER_1, USER_10, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("x4", "x5"), HANDLE_USER_10);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("x4", "x5", "x6"), HANDLE_USER_10);
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("x4", "x5", "x6", "x1"),
+                    HANDLE_USER_10);
+        });
+    }
+
+    private void prepareForBackupTest() {
+
+        prepareCrossProfileDataSet();
+
+        backupAndRestore();
+    }
+
+    private void assertExistsAndShadow(ShortcutPackageItem spi) {
+        assertNotNull(spi);
+        assertTrue(spi.getPackageInfo().isShadow());
+    }
+
+    /**
+     * Make sure the backup data doesn't have the following information:
+     * - Launchers on other users.
+     * - Non-backup app information.
+     *
+     * But restores all other infomation.
+     *
+     * It also omits the following pieces of information, but that's tested in
+     * {@link #testShortcutInfoSaveAndLoad_forBackup}.
+     * - Unpinned dynamic shortcuts
+     * - Bitmaps
+     */
+    public void testBackupAndRestore() {
+        prepareForBackupTest();
+
+        checkBackupAndRestore_success();
+    }
+
+    public void testBackupAndRestore_backupRestoreTwice() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        dumpsysOnLogcat("Before second backup");
+
+        backupAndRestore();
+
+        dumpsysOnLogcat("After second backup");
+
+        checkBackupAndRestore_success();
+    }
+
+    public void testBackupAndRestore_backupRestoreMultiple() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        // This also shouldn't affect the result.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(
+                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+                    makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+        });
+
+        backupAndRestore();
+
+        checkBackupAndRestore_success();
+    }
+
+    public void testBackupAndRestore_restoreToNewVersion() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
+        addPackage(LAUNCHER_1, LAUNCHER_UID_1, 5);
+
+        checkBackupAndRestore_success();
+    }
+
+    public void testBackupAndRestore_restoreToSuperSetSignatures() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1, "sigx", CALLING_PACKAGE_1);
+        addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4, LAUNCHER_1, "sigy");
+
+        checkBackupAndRestore_success();
+    }
+
+    private void checkBackupAndRestore_success() {
+        // Make sure non-system user is not restored.
+        final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
+        assertEquals(0, userP0.getAllPackages().size());
+        assertEquals(0, userP0.getAllLaunchers().size());
+
+        // Make sure only "allowBackup" apps are restored, and are shadow.
+        final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
+        assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_1));
+        assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_2));
+        assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_1)));
+        assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_2)));
+
+        assertNull(user0.getAllPackages().get(CALLING_PACKAGE_3));
+        assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
+        assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
+
+        installPackage(USER_0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2");
+        });
+
+        installPackage(USER_0, LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+                    "s1");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    /* empty, not restored */ );
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty, not restored */ );
+
+            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_2);
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3");
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+                    "s1");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s1", "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty, not restored */ );
+
+            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+        });
+
+        // 3 shouldn't be backed up, so no pinned shortcuts.
+        installPackage(USER_0, CALLING_PACKAGE_3);
+        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        // Launcher on a different profile shouldn't be restored.
+        runWithCaller(LAUNCHER_1, USER_P0, () -> {
+            assertEquals(0,
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)
+                    .size());
+            assertEquals(0,
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)
+                            .size());
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* wasn't restored, so still empty */ );
+        });
+
+        // Package on a different profile, no restore.
+        installPackage(USER_P0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        // Restore launcher 2 on user 0.
+        installPackage(USER_0, LAUNCHER_2);
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+                    "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* wasn't restored, so still empty */ );
+
+            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+        });
+
+
+        // Restoration of launcher2 shouldn't affect other packages; so do the same checks and
+        // make sure they still have the same result.
+        installPackage(USER_0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2");
+        });
+
+        installPackage(USER_0, LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+                    "s1");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s1", "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* wasn't restored, so still empty */ );
+
+            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_2);
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3");
+        });
+    }
+
+    public void testBackupAndRestore_publisherLowerVersion() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
+
+        checkBackupAndRestore_publisherNotRestored();
+    }
+
+    public void testBackupAndRestore_publisherWrongSignature() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
+
+        checkBackupAndRestore_publisherNotRestored();
+    }
+
+    public void testBackupAndRestore_publisherNoLongerBackupTarget() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        updatePackageInfo(CALLING_PACKAGE_1,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+        checkBackupAndRestore_publisherNotRestored();
+    }
+
+    private void checkBackupAndRestore_publisherNotRestored() {
+        installPackage(USER_0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_2);
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3");
+        });
+
+        installPackage(USER_0, LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s1", "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+        installPackage(USER_0, LAUNCHER_2);
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_3);
+        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s1", "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+    }
+
+    public void testBackupAndRestore_launcherLowerVersion() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
+
+        checkBackupAndRestore_launcherNotRestored();
+    }
+
+    public void testBackupAndRestore_launcherWrongSignature() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
+
+        checkBackupAndRestore_launcherNotRestored();
+    }
+
+    public void testBackupAndRestore_launcherNoLongerBackupTarget() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        updatePackageInfo(LAUNCHER_1,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+        checkBackupAndRestore_launcherNotRestored();
+    }
+
+    private void checkBackupAndRestore_launcherNotRestored() {
+        installPackage(USER_0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+
+            // s1 was pinned by launcher 1, which is not restored, yet, so we still see "s1" here.
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2");
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_2);
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3");
+        });
+
+        // Now we try to restore launcher 1.  Then we realize it's not restorable, so L1 has no pinned
+        // shortcuts.
+        installPackage(USER_0, LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+
+            // Now CALLING_PACKAGE_1 realizes "s1" is no longer pinned.
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s2");
+        });
+
+        installPackage(USER_0, LAUNCHER_2);
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+                    "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_3);
+        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+                    "s2");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+    }
+
+    public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+        prepareForBackupTest();
+
+        // Note doing a backup & restore again here shouldn't affect the result.
+        backupAndRestore();
+
+        updatePackageInfo(CALLING_PACKAGE_1,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+        updatePackageInfo(LAUNCHER_1,
+                pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+        checkBackupAndRestore_publisherAndLauncherNotRestored();
+    }
+
+    private void checkBackupAndRestore_publisherAndLauncherNotRestored() {
+        installPackage(USER_0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_2);
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3");
+        });
+
+        installPackage(USER_0, LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+        installPackage(USER_0, LAUNCHER_2);
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+
+        // Because launcher 1 wasn't restored, "s1" is no longer pinned.
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertShortcutIds(assertAllPinned(
+                    mManager.getPinnedShortcuts()),
+                    "s2", "s3");
+        });
+
+        installPackage(USER_0, CALLING_PACKAGE_3);
+        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+            assertEquals(0, mManager.getDynamicShortcuts().size());
+            assertEquals(0, mManager.getPinnedShortcuts().size());
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+                    "s2", "s3");
+            assertShortcutIds(assertAllPinned(
+                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    /* empty */);
+        });
+    }
+
+    public void testSaveAndLoad_crossProfile() {
+        prepareCrossProfileDataSet();
+
+        dumpsysOnLogcat("Before save & load");
+
+        mService.saveDirtyInfo();
+        initService();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5", "s6");
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3", "s4");
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5", "s6");
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5");
+        });
+        runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5", "s6");
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5", "s6");
+        });
+        runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts())
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts())
+                    /* empty */);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5", "s6");
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+                    "s1", "s2", "s3", "s4", "s5", "s6");
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_P0, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts())
+                    /* empty */);
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts())
+                    /* empty */);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+                    "x1", "x2", "x3", "x4", "x5", "x6");
+            assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+                    "x4", "x5");
+        });
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+                    "s1");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+                    "s1", "s2");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+                    "s1", "s2", "s3");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+                    "s1", "s4");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+                    /* empty */);
+            TestUtils.assertExpectException(
+                    SecurityException.class, "", () -> {
+                        mLauncherApps.getShortcuts(
+                                buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
+                    });
+        });
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+                    "s2");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+                    "s2", "s3");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+                    "s2", "s3", "s4");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+                    "s2", "s5");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+                    /* empty */);
+        });
+        runWithCaller(LAUNCHER_3, USER_0, () -> {
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+                    "s3");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+                    "s3", "s4");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+                    "s3", "s4", "s5");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+                    "s3", "s6");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+                    /* empty */);
+        });
+        runWithCaller(LAUNCHER_4, USER_0, () -> {
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+                    /* empty */);
+        });
+        runWithCaller(LAUNCHER_1, USER_P0, () -> {
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+                    "s3", "s4");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+                    "s3", "s4", "s5");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+                    "s3", "s4", "s5", "s6");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+                    "s1", "s4");
+            TestUtils.assertExpectException(
+                    SecurityException.class, "you need to be SYSTEM", () -> {
+                        mLauncherApps.getShortcuts(
+                                buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
+                    });
+        });
+        runWithCaller(LAUNCHER_1, USER_10, () -> {
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_10),
+                    "x4", "x5");
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_10)
+                    /* empty */);
+            assertShortcutIds(
+                    mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_10)
+                    /* empty */);
+            TestUtils.assertExpectException(
+                    SecurityException.class, "you need to be SYSTEM", () -> {
+                        mLauncherApps.getShortcuts(
+                                buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0);
+                    });
+            TestUtils.assertExpectException(
+                    SecurityException.class, "you need to be SYSTEM", () -> {
+                        mLauncherApps.getShortcuts(
+                                buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0);
+                    });
+        });
+    }
+
+    // ShortcutInfo tests
+
+    public void testShortcutInfoMissingMandatoryFields() {
+        TestUtils.assertExpectException(
+                IllegalArgumentException.class,
+                "ID must be provided",
+                () -> new ShortcutInfo.Builder(getTestContext()).build());
+        TestUtils.assertExpectException(
+                IllegalArgumentException.class,
+                "title must be provided",
+                () -> new ShortcutInfo.Builder(getTestContext()).setId("id").build()
+                        .enforceMandatoryFields());
+        TestUtils.assertExpectException(
+                NullPointerException.class,
+                "Intent must be provided",
+                () -> new ShortcutInfo.Builder(getTestContext()).setId("id").setTitle("x").build()
+                        .enforceMandatoryFields());
+    }
+
+    public void testShortcutInfoParcel() {
+        ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext())
+                .setId("id")
+                .setTitle("title")
+                .setIntent(makeIntent("action", ShortcutActivity.class))
+                .build());
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals("title", si.getTitle());
+        assertEquals("action", si.getIntent().getAction());
+
+        PersistableBundle pb = new PersistableBundle();
+        pb.putInt("k", 1);
+
+        si = new ShortcutInfo.Builder(getTestContext())
+                .setId("id")
+                .setActivityComponent(new ComponentName("a", "b"))
+                .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+                .setTitle("title")
+                .setText("text")
+                .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setWeight(123)
+                .setExtras(pb)
+                .build();
+        si.addFlags(ShortcutInfo.FLAG_PINNED);
+        si.setBitmapPath("abc");
+        si.setIconResourceId(456);
+
+        si = parceled(si);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+        assertEquals("content://a.b.c/", si.getIcon().getUriString());
+        assertEquals("title", si.getTitle());
+        assertEquals("text", si.getText());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(123, si.getWeight());
+        assertEquals(1, si.getExtras().getInt("k"));
+
+        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals("abc", si.getBitmapPath());
+        assertEquals(456, si.getIconResourceId());
+    }
+
+    public void testShortcutInfoClone() {
+        PersistableBundle pb = new PersistableBundle();
+        pb.putInt("k", 1);
+        ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+                .setId("id")
+                .setActivityComponent(new ComponentName("a", "b"))
+                .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+                .setTitle("title")
+                .setText("text")
+                .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setWeight(123)
+                .setExtras(pb)
+                .build();
+        sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+        sorig.setBitmapPath("abc");
+        sorig.setIconResourceId(456);
+
+        ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+        assertEquals("content://a.b.c/", si.getIcon().getUriString());
+        assertEquals("title", si.getTitle());
+        assertEquals("text", si.getText());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(123, si.getWeight());
+        assertEquals(1, si.getExtras().getInt("k"));
+
+        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals("abc", si.getBitmapPath());
+        assertEquals(456, si.getIconResourceId());
+
+        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+        assertEquals(null, si.getIcon());
+        assertEquals("title", si.getTitle());
+        assertEquals("text", si.getText());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(123, si.getWeight());
+        assertEquals(1, si.getExtras().getInt("k"));
+
+        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(null, si.getBitmapPath());
+        assertEquals(0, si.getIconResourceId());
+
+        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+        assertEquals(null, si.getIcon());
+        assertEquals("title", si.getTitle());
+        assertEquals("text", si.getText());
+        assertEquals(null, si.getIntent());
+        assertEquals(123, si.getWeight());
+        assertEquals(1, si.getExtras().getInt("k"));
+
+        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(null, si.getBitmapPath());
+        assertEquals(0, si.getIconResourceId());
+
+        si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+        assertEquals(getTestContext().getPackageName(), si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(null, si.getActivityComponent());
+        assertEquals(null, si.getIcon());
+        assertEquals(null, si.getTitle());
+        assertEquals(null, si.getText());
+        assertEquals(null, si.getIntent());
+        assertEquals(0, si.getWeight());
+        assertEquals(null, si.getExtras());
+
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
+        assertEquals(null, si.getBitmapPath());
+        assertEquals(0, si.getIconResourceId());
+    }
+
+    public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException {
+        PersistableBundle pb = new PersistableBundle();
+        pb.putInt("k", 1);
+        ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+                .setId("id")
+                .setActivityComponent(new ComponentName("a", "b"))
+                .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+                .setTitle("title")
+                .setText("text")
+                .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setWeight(123)
+                .setExtras(pb)
+                .build();
+        sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+        sorig.setBitmapPath("abc");
+        sorig.setIconResourceId(456);
+
+        ShortcutInfo si;
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setActivityComponent(new ComponentName("x", "y")).build());
+        assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
+        assertEquals("content://x.y.z/", si.getIcon().getUriString());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setTitle("xyz").build());
+        assertEquals("xyz", si.getTitle());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setText("xxx").build());
+        assertEquals("xxx", si.getText());
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setIntent(makeIntent("action2", ShortcutActivity.class)).build());
+        assertEquals("action2", si.getIntent().getAction());
+        assertEquals(null, si.getIntent().getStringExtra("key"));
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build());
+        assertEquals("action3", si.getIntent().getAction());
+        assertEquals("x", si.getIntent().getStringExtra("key"));
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setWeight(999).build());
+        assertEquals(999, si.getWeight());
+
+
+        PersistableBundle pb2 = new PersistableBundle();
+        pb2.putInt("x", 99);
+
+        si = sorig.clone(/* flags=*/ 0);
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setExtras(pb2).build());
+        assertEquals(99, si.getExtras().getInt("x"));
+
+        final long timestamp = si.getLastChangedTimestamp();
+        Thread.sleep(2);
+
+        si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+                .setTitle("xyz").build());
+
+        assertTrue(si.getLastChangedTimestamp() > timestamp);
+    }
+
+    public void testShortcutInfoSaveAndLoad() throws InterruptedException {
+        setCaller(CALLING_PACKAGE_1, USER_0);
+
+        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                getTestContext().getResources(), R.drawable.black_32x32));
+
+        PersistableBundle pb = new PersistableBundle();
+        pb.putInt("k", 1);
+        ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+                .setId("id")
+                .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+                .setIcon(bmp32x32)
+                .setTitle("title")
+                .setText("text")
+                .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setWeight(123)
+                .setExtras(pb)
+                .build();
+
+        mManager.addDynamicShortcut(sorig);
+
+        Thread.sleep(2);
+        final long now = System.currentTimeMillis();
+
+        // Save and load.
+        mService.saveDirtyInfo();
+        initService();
+        mService.handleUnlockUser(USER_0);
+
+        ShortcutInfo si;
+        si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+        assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+        assertEquals(null, si.getIcon());
+        assertEquals("title", si.getTitle());
+        assertEquals("text", si.getText());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(123, si.getWeight());
+        assertEquals(1, si.getExtras().getInt("k"));
+
+        assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags());
+        assertNotNull(si.getBitmapPath()); // Something should be set.
+        assertEquals(0, si.getIconResourceId());
+        assertTrue(si.getLastChangedTimestamp() < now);
+    }
+
+    public void testShortcutInfoSaveAndLoad_forBackup() {
+        setCaller(CALLING_PACKAGE_1, USER_0);
+
+        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                getTestContext().getResources(), R.drawable.black_32x32));
+
+        PersistableBundle pb = new PersistableBundle();
+        pb.putInt("k", 1);
+        ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+                .setId("id")
+                .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+                .setIcon(bmp32x32)
+                .setTitle("title")
+                .setText("text")
+                .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+                .setWeight(123)
+                .setExtras(pb)
+                .build();
+
+        mManager.addDynamicShortcut(sorig);
+
+        // Dynamic shortcuts won't be backed up, so we need to pin it.
+        setCaller(LAUNCHER_1, USER_0);
+        mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id"), HANDLE_USER_0);
+
+        // Do backup & restore.
+        backupAndRestore();
+
+        ShortcutInfo si;
+        si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+        assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+        assertEquals("id", si.getId());
+        assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+        assertEquals(null, si.getIcon());
+        assertEquals("title", si.getTitle());
+        assertEquals("text", si.getText());
+        assertEquals("action", si.getIntent().getAction());
+        assertEquals("val", si.getIntent().getStringExtra("key"));
+        assertEquals(123, si.getWeight());
+        assertEquals(1, si.getExtras().getInt("k"));
+
+        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertNull(si.getBitmapPath()); // No icon.
+        assertEquals(0, si.getIconResourceId());
+    }
+
+    public void testDumpsys_crossProfile() {
+        prepareCrossProfileDataSet();
+        dumpsysOnLogcat("test1", /* force= */ true);
+    }
+
+    public void testDumpsys_withIcons() {
+        testIcons();
+        // Dump after having some icons.
+        dumpsysOnLogcat("test1", /* force= */ true);
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index b56ce73..dbc2b0c 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -170,6 +170,15 @@
     public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 0x100;
 
     /**
+     * Flag indicating that for this {@link PhoneAccount}, emergency video calling is allowed.
+     * <p>
+     * When set, Telecom will allow emergency video calls to be placed.  When not set, Telecom will
+     * convert all outgoing video calls to emergency numbers to audio-only.
+     * @hide
+     */
+    public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
+
+    /**
      * URI scheme for telephone number URIs.
      */
     public static final String SCHEME_TEL = "tel";
@@ -731,6 +740,9 @@
         if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) {
             sb.append("PlaceEmerg ");
         }
+        if (hasCapabilities(CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
+            sb.append("EmergVideo ");
+        }
         if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
             sb.append("SimSub ");
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c69a360..461611d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -252,6 +252,16 @@
     public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
 
     /**
+     * Flag specifying whether WFC over IMS supports the "wifi only" option.  If false, the wifi
+     * calling settings will not include an option for "wifi only".  If true, the wifi calling
+     * settings will include an option for "wifi only"
+     * <p>
+     * By default, it is assumed that WFC supports "wifi only".
+     */
+    public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL =
+            "carrier_wfc_supports_wifi_only_bool";
+
+    /**
      * Default WFC_IMS_mode 0: WIFI_ONLY
      *                      1: CELLULAR_PREFERRED
      *                      2: WIFI_PREFERRED
@@ -628,6 +638,7 @@
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
         sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index ed7351f8..033312b 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -62,7 +62,7 @@
     private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
     private static final String WEARABLE_ACTION_GOOGLE =
             "com.google.android.wearable.action.GOOGLE";
-    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
+    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle
     private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
     private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps
 
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
index ad02d2b..c0583ce 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
@@ -18,6 +18,7 @@
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
@@ -40,6 +41,7 @@
 import java.net.InetAddress;
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Random;
 import java.util.UUID;
 
@@ -53,7 +55,7 @@
     static final int MSG_GENERIC_TRIGGER = 4;
 
     private Random random = new Random();
-    private ArrayList<UUID> loadedModelUuids;
+    private HashSet<UUID> loadedModelUuids;
     private ISoundTriggerService soundTriggerService;
     private SoundTriggerManager soundTriggerManager;
 
@@ -68,7 +70,7 @@
         soundTriggerManager = (SoundTriggerManager) context.getSystemService(
                 Context.SOUND_TRIGGER_SERVICE);
 
-        loadedModelUuids = new ArrayList<UUID>();
+        loadedModelUuids = new HashSet<UUID>();
     }
 
     @Override
@@ -170,6 +172,101 @@
         verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
     }
 
+    /**
+     * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
+     * recognition. Intended to find unexpected errors that occur in unexpected states.
+     */
+    @LargeTest
+    public void testFuzzGenericSoundModel() throws Exception {
+        int numModels = 2;
+
+        final int STATUS_UNLOADED = 0;
+        final int STATUS_LOADED = 1;
+        final int STATUS_STARTED = 2;
+
+        class ModelInfo {
+            int status;
+            GenericSoundModel model;
+
+            public ModelInfo(GenericSoundModel model, int status) {
+                this.status = status;
+                this.model = model;
+            }
+        }
+
+        Random predictableRandom = new Random(100);
+
+        ArrayList modelInfos = new ArrayList<ModelInfo>();
+        for(int i=0; i<numModels; i++) {
+            // Create sound model
+            byte[] data = new byte[1024];
+            predictableRandom.nextBytes(data);
+            UUID modelUuid = UUID.randomUUID();
+            UUID mVendorUuid = UUID.randomUUID();
+            GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
+            ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
+            modelInfos.add(modelInfo);
+        }
+
+        boolean captureTriggerAudio = true;
+        boolean allowMultipleTriggers = true;
+        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
+                null, null);
+        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
+
+
+        int numOperationsToRun = 100;
+        for(int i=0; i<numOperationsToRun; i++) {
+            // Select a random model
+            int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
+            ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
+
+            // Perform a random operation
+            int operation = predictableRandom.nextInt(5);
+
+            if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
+                // Update and start sound model
+                soundTriggerService.updateSoundModel(modelInfo.model);
+                loadedModelUuids.add(modelInfo.model.uuid);
+                modelInfo.status = STATUS_LOADED;
+            } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
+                // Start the sound model
+                int r = soundTriggerService.startRecognition(new ParcelUuid(modelInfo.model.uuid),
+                        spyCallback, config);
+                assertEquals("Could Not Start Recognition with code: " + r,
+                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
+                modelInfo.status = STATUS_STARTED;
+            } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
+                // Send trigger to stub HAL
+                Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
+                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+                out.writeBytes("trig " + modelInfo.model.uuid + "\r\n");
+                out.flush();
+                socket.close();
+
+                // Verify trigger was received
+                verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
+                reset(spyCallback);
+            } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
+                // Stop recognition
+                int r = soundTriggerService.stopRecognition(new ParcelUuid(modelInfo.model.uuid),
+                        spyCallback);
+                assertEquals("Could Not Stop Recognition with code: " + r,
+                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
+                modelInfo.status = STATUS_LOADED;
+            } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
+                // Delete sound model
+                soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.uuid));
+                loadedModelUuids.remove(modelInfo.model.uuid);
+
+                // Confirm it was deleted
+                GenericSoundModel returnedModel =
+                        soundTriggerService.getSoundModel(new ParcelUuid(modelInfo.model.uuid));
+                assertEquals(null, returnedModel);
+                modelInfo.status = STATUS_UNLOADED;
+            }
+        }
+    }
 
     public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
         @Override
diff --git a/tests/VectorDrawableTest/Android.mk b/tests/VectorDrawableTest/Android.mk
index 3d44e33..dd8a4d4 100644
--- a/tests/VectorDrawableTest/Android.mk
+++ b/tests/VectorDrawableTest/Android.mk
@@ -23,6 +23,4 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_SDK_VERSION := current
-
 include $(BUILD_PACKAGE)
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index e648897..7b3beb2 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -18,8 +18,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.test.dynamic" >
 
-    <uses-sdk android:minSdkVersion="21" />
-
     <application
         android:hardwareAccelerated="true"
         android:label="vector"
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
index 4026e5e..8f538ae 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -24,6 +24,7 @@
 import android.widget.Button;
 import android.widget.GridLayout;
 import android.widget.ScrollView;
+import android.widget.TextView;
 
 public class AnimatedVectorDrawableTest extends Activity implements View.OnClickListener {
     private static final String LOGCAT = "AnimatedVectorDrawableTest";
@@ -45,35 +46,51 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         final int[] layerTypes = {View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE};
+        final boolean[] forceOnUi = {false, true};
         super.onCreate(savedInstanceState);
 
         ScrollView scrollView = new ScrollView(this);
         GridLayout container = new GridLayout(this);
         scrollView.addView(container);
-        container.setColumnCount(2);
+        container.setColumnCount(layerTypes.length * forceOnUi.length);
 
+        for (int j = 0; j < layerTypes.length; j++) {
+            for (int k = 0; k < forceOnUi.length; k++) {
+                TextView textView = new TextView(this);
+                String category = "Layer:"
+                        + (layerTypes[j] == View.LAYER_TYPE_SOFTWARE ? "SW" : "HW")
+                        + (forceOnUi[k] == true ? ",forceUI" : "");
+                textView.setText(category);
+                container.addView(textView);
+            }
+        }
         for (int i = 0; i < icon.length; i++) {
             for (int j = 0; j < layerTypes.length; j++) {
-                Button button = new Button(this);
-                button.setWidth(400);
-                button.setHeight(400);
-                button.setLayerType(layerTypes[j], null);
-                button.setBackgroundResource(icon[i]);
-                AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
-                d.registerAnimationCallback(new Animatable2.AnimationCallback() {
-                    @Override
-                    public void onAnimationStart(Drawable drawable) {
-                        Log.v(LOGCAT, "Animator start");
+                for (int k = 0; k < forceOnUi.length; k++) {
+                    Button button = new Button(this);
+                    button.setWidth(300);
+                    button.setHeight(300);
+                    button.setLayerType(layerTypes[j], null);
+                    button.setBackgroundResource(icon[i]);
+                    AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
+                    if (forceOnUi[k] == true) {
+                        d.forceAnimationOnUI();
                     }
+                    d.registerAnimationCallback(new Animatable2.AnimationCallback() {
+                        @Override
+                        public void onAnimationStart(Drawable drawable) {
+                            Log.v(LOGCAT, "Animator start");
+                        }
 
-                    @Override
-                    public void onAnimationEnd(Drawable drawable) {
-                        Log.v(LOGCAT, "Animator end");
-                    }
-                });
+                        @Override
+                        public void onAnimationEnd(Drawable drawable) {
+                            Log.v(LOGCAT, "Animator end");
+                        }
+                    });
 
-                container.addView(button);
-                button.setOnClickListener(this);
+                    container.addView(button);
+                    button.setOnClickListener(this);
+                }
             }
         }
 
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 85d22ff..876a422 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -54,6 +54,7 @@
 	Debug.cpp \
 	Flags.cpp \
 	java/AnnotationProcessor.cpp \
+	java/ClassDefinition.cpp \
 	java/JavaClassGenerator.cpp \
 	java/ManifestClassGenerator.cpp \
 	java/ProguardRules.cpp \
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 496e92e..ba74439 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -64,7 +64,7 @@
     mComment << "\n *";
 }
 
-void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) {
+void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) const {
     if (mHasComments) {
         std::string result = mComment.str();
         for (StringPiece line : util::tokenize<char>(result, '\n')) {
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index fadf584..0fc5b08 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -66,7 +66,7 @@
     /**
      * Writes the comments and annotations to the stream, with the given prefix before each line.
      */
-    void writeToStream(std::ostream* out, const StringPiece& prefix);
+    void writeToStream(std::ostream* out, const StringPiece& prefix) const;
 
 private:
     enum : uint32_t {
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
new file mode 100644
index 0000000..08f2c8b
--- /dev/null
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "java/ClassDefinition.h"
+#include "util/StringPiece.h"
+
+#include <ostream>
+
+namespace aapt {
+
+bool ClassDefinition::empty() const {
+    for (const std::unique_ptr<ClassMember>& member : mMembers) {
+        if (!member->empty()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void ClassDefinition::writeToStream(const StringPiece& prefix, bool final,
+                                    std::ostream* out) const {
+    if (mMembers.empty() && !mCreateIfEmpty) {
+        return;
+    }
+
+    ClassMember::writeToStream(prefix, final, out);
+
+    *out << prefix << "public ";
+    if (mQualifier == ClassQualifier::Static) {
+        *out << "static ";
+    }
+    *out << "final class " << mName << " {\n";
+
+    std::string newPrefix = prefix.toString();
+    newPrefix.append(kIndent);
+
+    for (const std::unique_ptr<ClassMember>& member : mMembers) {
+        member->writeToStream(newPrefix, final, out);
+        *out << "\n";
+    }
+
+    *out << prefix << "}";
+}
+
+constexpr static const char* sWarningHeader =
+        "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+        " *\n"
+        " * This class was automatically generated by the\n"
+        " * aapt tool from the resource data it found. It\n"
+        " * should not be modified by hand.\n"
+        " */\n\n";
+
+bool ClassDefinition::writeJavaFile(const ClassDefinition* def,
+                                    const StringPiece& package,
+                                    bool final,
+                                    std::ostream* out) {
+    *out << sWarningHeader << "package " << package << ";\n\n";
+    def->writeToStream("", final, out);
+    return bool(*out);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
new file mode 100644
index 0000000..53e0f6f
--- /dev/null
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_JAVA_CLASSDEFINITION_H
+#define AAPT_JAVA_CLASSDEFINITION_H
+
+#include "Resource.h"
+#include "java/AnnotationProcessor.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <android-base/macros.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+// The number of attributes to emit per line in a Styleable array.
+constexpr static size_t kAttribsPerLine = 4;
+constexpr static const char* kIndent = "  ";
+
+class ClassMember {
+public:
+    virtual ~ClassMember() = default;
+
+    AnnotationProcessor* getCommentBuilder() {
+        return &mProcessor;
+    }
+
+    virtual bool empty() const = 0;
+
+    virtual void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
+        mProcessor.writeToStream(out, prefix);
+    }
+
+private:
+    AnnotationProcessor mProcessor;
+};
+
+template <typename T>
+class PrimitiveMember : public ClassMember {
+public:
+    PrimitiveMember(const StringPiece& name, const T& val) :
+            mName(name.toString()), mVal(val) {
+    }
+
+    bool empty() const override {
+        return false;
+    }
+
+    void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+        ClassMember::writeToStream(prefix, final, out);
+
+        *out << prefix << "public static " << (final ? "final " : "")
+             << "int " << mName << "=" << mVal << ";";
+    }
+
+private:
+    std::string mName;
+    T mVal;
+
+    DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+};
+
+/**
+ * Specialization for strings so they get the right type and are quoted with "".
+ */
+template <>
+class PrimitiveMember<std::string> : public ClassMember {
+public:
+    PrimitiveMember(const StringPiece& name, const std::string& val) :
+            mName(name.toString()), mVal(val) {
+    }
+
+    bool empty() const override {
+        return false;
+    }
+
+    void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+        ClassMember::writeToStream(prefix, final, out);
+
+        *out << prefix << "public static " << (final ? "final " : "")
+             << "String " << mName << "=\"" << mVal << "\";";
+    }
+
+private:
+    std::string mName;
+    std::string mVal;
+
+    DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+};
+
+using IntMember = PrimitiveMember<uint32_t>;
+using ResourceMember = PrimitiveMember<ResourceId>;
+using StringMember = PrimitiveMember<std::string>;
+
+template <typename T>
+class PrimitiveArrayMember : public ClassMember {
+public:
+    PrimitiveArrayMember(const StringPiece& name) :
+            mName(name.toString()) {
+    }
+
+    void addElement(const T& val) {
+        mElements.push_back(val);
+    }
+
+    bool empty() const override {
+        return false;
+    }
+
+    void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+        ClassMember::writeToStream(prefix, final, out);
+
+        *out << "public static final int[] " << mName << "={";
+
+        const auto begin = mElements.begin();
+        const auto end = mElements.end();
+        for (auto current = begin; current != end; ++current) {
+            if (std::distance(begin, current) % kAttribsPerLine == 0) {
+                *out << "\n" << prefix << kIndent << kIndent;
+            }
+
+            *out << *current;
+            if (std::distance(current, end) > 1) {
+                *out << ", ";
+            }
+        }
+        *out << "\n" << prefix << kIndent <<"};";
+    }
+
+private:
+    std::string mName;
+    std::vector<T> mElements;
+
+    DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
+};
+
+using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
+
+enum class ClassQualifier {
+    None,
+    Static
+};
+
+class ClassDefinition : public ClassMember {
+public:
+    static bool writeJavaFile(const ClassDefinition* def,
+                              const StringPiece& package,
+                              bool final,
+                              std::ostream* out);
+
+    ClassDefinition(const StringPiece& name, ClassQualifier qualifier, bool createIfEmpty) :
+            mName(name.toString()), mQualifier(qualifier), mCreateIfEmpty(createIfEmpty) {
+    }
+
+    void addMember(std::unique_ptr<ClassMember> member) {
+        mMembers.push_back(std::move(member));
+    }
+
+    bool empty() const override;
+    void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override;
+
+private:
+    std::string mName;
+    ClassQualifier mQualifier;
+    bool mCreateIfEmpty;
+    std::vector<std::unique_ptr<ClassMember>> mMembers;
+
+    DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_CLASSDEFINITION_H */
diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h
deleted file mode 100644
index cf92c9a..0000000
--- a/tools/aapt2/java/ClassDefinitionWriter.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_JAVA_CLASSDEFINITION_H
-#define AAPT_JAVA_CLASSDEFINITION_H
-
-#include "Resource.h"
-#include "java/AnnotationProcessor.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-struct ClassDefinitionWriterOptions {
-    bool useFinalQualifier = false;
-    bool forceCreationIfEmpty = false;
-};
-
-/**
- * Writes a class for use in R.java or Manifest.java.
- */
-class ClassDefinitionWriter {
-public:
-    ClassDefinitionWriter(const StringPiece& name, const ClassDefinitionWriterOptions& options) :
-            mName(name.toString()), mOptions(options), mStarted(false) {
-    }
-
-    ClassDefinitionWriter(const StringPiece16& name, const ClassDefinitionWriterOptions& options) :
-            mName(util::utf16ToUtf8(name)), mOptions(options), mStarted(false) {
-    }
-
-    void addIntMember(const StringPiece& name, AnnotationProcessor* processor,
-                      const uint32_t val) {
-        ensureClassDeclaration();
-        if (processor) {
-            processor->writeToStream(&mOut, kIndent);
-        }
-        mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
-             << "int " << name << "=" << val << ";\n";
-    }
-
-    void addStringMember(const StringPiece16& name, AnnotationProcessor* processor,
-                         const StringPiece16& val) {
-        ensureClassDeclaration();
-        if (processor) {
-            processor->writeToStream(&mOut, kIndent);
-        }
-        mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
-             << "String " << name << "=\"" << val << "\";\n";
-    }
-
-    void addResourceMember(const StringPiece& name, AnnotationProcessor* processor,
-                           const ResourceId id) {
-        ensureClassDeclaration();
-        if (processor) {
-            processor->writeToStream(&mOut, kIndent);
-        }
-        mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
-             << "int " << name << "=" << id <<";\n";
-    }
-
-    template <typename Iterator, typename FieldAccessorFunc>
-    void addArrayMember(const StringPiece& name, AnnotationProcessor* processor,
-                        const Iterator begin, const Iterator end, FieldAccessorFunc f) {
-        ensureClassDeclaration();
-        if (processor) {
-            processor->writeToStream(&mOut, kIndent);
-        }
-        mOut << kIndent << "public static final int[] " << name << "={";
-
-        for (Iterator current = begin; current != end; ++current) {
-            if (std::distance(begin, current) % kAttribsPerLine == 0) {
-                mOut << "\n" << kIndent << kIndent;
-            }
-
-            mOut << f(*current);
-            if (std::distance(current, end) > 1) {
-                mOut << ", ";
-            }
-        }
-        mOut << "\n" << kIndent <<"};\n";
-    }
-
-    void writeToStream(std::ostream* out, const StringPiece& prefix,
-                       AnnotationProcessor* processor=nullptr) {
-        if (mOptions.forceCreationIfEmpty) {
-            ensureClassDeclaration();
-        }
-
-        if (!mStarted) {
-            return;
-        }
-
-        if (processor) {
-            processor->writeToStream(out, prefix);
-        }
-
-        std::string result = mOut.str();
-        for (StringPiece line : util::tokenize<char>(result, '\n')) {
-            *out << prefix << line << "\n";
-        }
-        *out << prefix << "}\n";
-    }
-
-private:
-    constexpr static const char* kIndent = "  ";
-
-    // The number of attributes to emit per line in a Styleable array.
-    constexpr static size_t kAttribsPerLine = 4;
-
-    void ensureClassDeclaration() {
-        if (!mStarted) {
-            mStarted = true;
-            mOut << "public static final class " << mName << " {\n";
-        }
-    }
-
-    std::stringstream mOut;
-    std::string mName;
-    ClassDefinitionWriterOptions mOptions;
-    bool mStarted;
-};
-
-} // namespace aapt
-
-#endif /* AAPT_JAVA_CLASSDEFINITION_H */
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 01330dc..2d076c2 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -21,7 +21,7 @@
 #include "ValueVisitor.h"
 
 #include "java/AnnotationProcessor.h"
-#include "java/ClassDefinitionWriter.h"
+#include "java/ClassDefinition.h"
 #include "java/JavaClassGenerator.h"
 #include "process/SymbolTable.h"
 #include "util/StringPiece.h"
@@ -39,16 +39,6 @@
         mContext(context), mTable(table), mOptions(options) {
 }
 
-static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) {
-    *out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
-            " *\n"
-            " * This class was automatically generated by the\n"
-            " * aapt tool from the resource data it found. It\n"
-            " * should not be modified by hand.\n"
-            " */\n\n"
-            "package " << packageNameToGenerate << ";\n\n";
-}
-
 static const std::set<StringPiece16> sJavaIdentifiers = {
     u"abstract", u"assert", u"boolean", u"break", u"byte",
     u"case", u"catch", u"char", u"class", u"const", u"continue",
@@ -110,15 +100,15 @@
     if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
         processor->appendComment(
                 "<p>May be a reference to another resource, in the form\n"
-                        "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
-                        "attribute in the form\n"
-                        "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+                "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
+                "attribute in the form\n"
+                "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
     }
 
     if (typeMask & android::ResTable_map::TYPE_STRING) {
         processor->appendComment(
                 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
-                        "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+                "'\\\\n' or '\\\\uxxxx' for a unicode character;");
     }
 
     if (typeMask & android::ResTable_map::TYPE_INTEGER) {
@@ -128,14 +118,14 @@
     if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
         processor->appendComment(
                 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
-                        "\"<code>false</code>\".");
+                "\"<code>false</code>\".");
     }
 
     if (typeMask & android::ResTable_map::TYPE_COLOR) {
         processor->appendComment(
                 "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
-                        "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
-                        "\"<code>#<i>aarrggbb</i></code>\".");
+                "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
+                "\"<code>#<i>aarrggbb</i></code>\".");
     }
 
     if (typeMask & android::ResTable_map::TYPE_FLOAT) {
@@ -146,33 +136,33 @@
     if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
         processor->appendComment(
                 "<p>May be a dimension value, which is a floating point number appended with a\n"
-                        "unit such as \"<code>14.5sp</code>\".\n"
-                        "Available units are: px (pixels), dp (density-independent pixels),\n"
-                        "sp (scaled pixels based on preferred font size), in (inches), and\n"
-                        "mm (millimeters).");
+                "unit such as \"<code>14.5sp</code>\".\n"
+                "Available units are: px (pixels), dp (density-independent pixels),\n"
+                "sp (scaled pixels based on preferred font size), in (inches), and\n"
+                "mm (millimeters).");
     }
 
     if (typeMask & android::ResTable_map::TYPE_FRACTION) {
         processor->appendComment(
                 "<p>May be a fractional value, which is a floating point number appended with\n"
-                        "either % or %p, such as \"<code>14.5%</code>\".\n"
-                        "The % suffix always means a percentage of the base size;\n"
-                        "the optional %p suffix provides a size relative to some parent container.");
+                "either % or %p, such as \"<code>14.5%</code>\".\n"
+                "The % suffix always means a percentage of the base size;\n"
+                "the optional %p suffix provides a size relative to some parent container.");
     }
 
     if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
         if (typeMask & android::ResTable_map::TYPE_FLAGS) {
             processor->appendComment(
                     "<p>Must be one or more (separated by '|') of the following "
-                            "constant values.</p>");
+                    "constant values.</p>");
         } else {
             processor->appendComment("<p>Must be one of the following constant values.</p>");
         }
 
         processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
-                                         "<colgroup align=\"left\" />\n"
-                                         "<colgroup align=\"left\" />\n"
-                                         "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
+                                 "<colgroup align=\"left\" />\n"
+                                 "<colgroup align=\"left\" />\n"
+                                 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
         for (const Attribute::Symbol& symbol : attr->symbols) {
             std::stringstream line;
             line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
@@ -214,13 +204,15 @@
     }
 }
 
-void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
-                                                     AnnotationProcessor* processor,
-                                                     const StringPiece16& packageNameToGenerate,
-                                                     const std::u16string& entryName,
-                                                     const Styleable* styleable) {
+void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
+                                                    const std::u16string& entryName,
+                                                    const Styleable* styleable,
+                                                    ClassDefinition* outStyleableClassDef) {
     const std::string className = transform(entryName);
 
+    std::unique_ptr<ResourceArrayMember> styleableArrayDef =
+            util::make_unique<ResourceArrayMember>(className);
+
     // This must be sorted by resource ID.
     std::vector<StyleableAttr> sortedAttributes;
     sortedAttributes.reserve(styleable->entries.size());
@@ -230,6 +222,8 @@
         assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
         assert(attr.name && "no name set for Styleable entry");
 
+        // We will need the unmangled, transformed name in the comments and the field,
+        // so create it once and cache it in this StyleableAttr data structure.
         StyleableAttr styleableAttr = {};
         styleableAttr.attrRef = &attr;
         styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
@@ -247,6 +241,8 @@
             mangledReference.name = mangledName;
         }
 
+        // Look up the symbol so that we can write out in the comments what are possible
+        // legal values for this attribute.
         const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
                 mangledReference);
         if (symbol) {
@@ -254,10 +250,11 @@
         }
         sortedAttributes.push_back(std::move(styleableAttr));
     }
+
+    // Sort the attributes by ID.
     std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
 
     const size_t attrCount = sortedAttributes.size();
-
     if (attrCount > 0) {
         // Build the comment string for the Styleable. It includes details about the
         // child attributes.
@@ -267,6 +264,7 @@
         } else {
             styleableComment << "Attributes that can be used with a " << className << ".\n";
         }
+
         styleableComment <<
                 "<p>Includes the following attributes:</p>\n"
                 "<table>\n"
@@ -274,7 +272,7 @@
                 "<colgroup align=\"left\" />\n"
                 "<tr><th>Attribute</th><th>Description</th></tr>\n";
 
-        for (const auto& entry : sortedAttributes) {
+        for (const StyleableAttr& entry : sortedAttributes) {
             const ResourceName& attrName = entry.attrRef->name.value();
             styleableComment << "<tr><td>";
             styleableComment << "<code>{@link #"
@@ -292,21 +290,22 @@
             styleableComment << "</td></tr>\n";
         }
         styleableComment << "</table>\n";
-        for (const auto& entry : sortedAttributes) {
+
+        for (const StyleableAttr& entry : sortedAttributes) {
             styleableComment << "@see #" << entry.fieldName << "\n";
         }
-        processor->appendComment(styleableComment.str());
+
+        styleableArrayDef->getCommentBuilder()->appendComment(styleableComment.str());
     }
 
-    auto accessorFunc = [](const StyleableAttr& a) -> ResourceId {
-        return a.attrRef->id ? a.attrRef->id.value() : ResourceId(0);
-    };
+    // Add the ResourceIds to the array member.
+    for (const StyleableAttr& styleableAttr : sortedAttributes) {
+        styleableArrayDef->addElement(
+                styleableAttr.attrRef->id ? styleableAttr.attrRef->id.value() : ResourceId(0));
+    }
 
-    // First we emit the array containing the IDs of each attribute.
-    outClassDef->addArrayMember(className, processor,
-                                sortedAttributes.begin(),
-                                sortedAttributes.end(),
-                                accessorFunc);
+    // Add the Styleable array to the Styleable class.
+    outStyleableClassDef->addMember(std::move(styleableArrayDef));
 
     // Now we emit the indices into the array.
     for (size_t i = 0; i < attrCount; i++) {
@@ -318,7 +317,10 @@
             packageName = mContext->getCompilationPackage();
         }
 
-        AnnotationProcessor attrProcessor;
+        std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>(
+                sortedAttributes[i].fieldName, i);
+
+        AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
 
         StringPiece16 comment = styleableAttr.attrRef->getComment();
         if (styleableAttr.attribute && comment.empty()) {
@@ -326,8 +328,8 @@
         }
 
         if (!comment.empty()) {
-            attrProcessor.appendComment("<p>\n@attr description");
-            attrProcessor.appendComment(comment);
+            attrProcessor->appendComment("<p>\n@attr description");
+            attrProcessor->appendComment(comment);
         } else {
             std::stringstream defaultComment;
             defaultComment
@@ -335,27 +337,29 @@
                     << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
                     << "attribute's value can be found in the "
                     << "{@link #" << className << "} array.";
-            attrProcessor.appendComment(defaultComment.str());
+            attrProcessor->appendComment(defaultComment.str());
         }
 
-        attrProcessor.appendNewLine();
+        attrProcessor->appendNewLine();
 
         if (styleableAttr.attribute) {
-            addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get());
-            attrProcessor.appendNewLine();
+            addAttributeFormatDoc(attrProcessor, styleableAttr.attribute.get());
+            attrProcessor->appendNewLine();
         }
 
         std::stringstream doclavaName;
         doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
-        attrProcessor.appendComment(doclavaName.str());
-        outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
+        attrProcessor->appendComment(doclavaName.str());
+
+        outStyleableClassDef->addMember(std::move(indexMember));
     }
 }
 
-bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef,
-                                              const StringPiece16& packageNameToGenerate,
-                                              const ResourceTablePackage* package,
-                                              const ResourceTableType* type) {
+bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
+                                               const ResourceTablePackage* package,
+                                               const ResourceTableType* type,
+                                               ClassDefinition* outTypeClassDef) {
+
     for (const auto& entry : type->entries) {
         if (skipSymbol(entry->symbolStatus.state)) {
             continue;
@@ -389,33 +393,41 @@
             return false;
         }
 
-        // Build the comments and annotations for this entry.
-
-        AnnotationProcessor processor;
-        if (entry->symbolStatus.state != SymbolState::kUndefined) {
-            processor.appendComment(entry->symbolStatus.comment);
-        }
-
-        for (const auto& configValue : entry->values) {
-            processor.appendComment(configValue->value->getComment());
-        }
-
-        // If this is an Attribute, append the format Javadoc.
-        if (!entry->values.empty()) {
-            if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
-                // We list out the available values for the given attribute.
-                addAttributeFormatDoc(&processor, attr);
-            }
-        }
-
         if (type->type == ResourceType::kStyleable) {
             assert(!entry->values.empty());
+
             const Styleable* styleable = static_cast<const Styleable*>(
                     entry->values.front()->value.get());
-            writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate,
-                                        unmangledName, styleable);
+
+            // Comments are handled within this method.
+            addMembersToStyleableClass(packageNameToGenerate, unmangledName, styleable,
+                                       outTypeClassDef);
         } else {
-            outClassDef->addResourceMember(transform(unmangledName), &processor, id);
+            std::unique_ptr<ResourceMember> resourceMember =
+                    util::make_unique<ResourceMember>(transform(unmangledName), id);
+
+            // Build the comments and annotations for this entry.
+            AnnotationProcessor* processor = resourceMember->getCommentBuilder();
+
+            // Add the comments from any <public> tags.
+            if (entry->symbolStatus.state != SymbolState::kUndefined) {
+                processor->appendComment(entry->symbolStatus.comment);
+            }
+
+            // Add the comments from all configurations of this entry.
+            for (const auto& configValue : entry->values) {
+                processor->appendComment(configValue->value->getComment());
+            }
+
+            // If this is an Attribute, append the format Javadoc.
+            if (!entry->values.empty()) {
+                if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
+                    // We list out the available values for the given attribute.
+                    addAttributeFormatDoc(processor, attr);
+                }
+            }
+
+            outTypeClassDef->addMember(std::move(resourceMember));
         }
     }
     return true;
@@ -427,9 +439,8 @@
 
 bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
                                   const StringPiece16& outPackageName, std::ostream* out) {
-    generateHeader(outPackageName, out);
 
-    *out << "public final class R {\n";
+    ClassDefinition rClass("R", ClassQualifier::None, true);
 
     for (const auto& package : mTable->packages) {
         for (const auto& type : package->types) {
@@ -437,13 +448,15 @@
                 continue;
             }
 
-            ClassDefinitionWriterOptions classOptions;
-            classOptions.useFinalQualifier = mOptions.useFinal;
-            classOptions.forceCreationIfEmpty =
+            const bool forceCreationIfEmpty =
                     (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
-            ClassDefinitionWriter classDef(toString(type->type), classOptions);
-            bool result = writeEntriesForClass(&classDef, packageNameToGenerate,
-                                               package.get(), type.get());
+
+            std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>(
+                    util::utf16ToUtf8(toString(type->type)), ClassQualifier::Static,
+                    forceCreationIfEmpty);
+
+            bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(),
+                                                classDef.get());
             if (!result) {
                 return false;
             }
@@ -452,26 +465,31 @@
                 // Also include private attributes in this same class.
                 ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
                 if (privType) {
-                    result = writeEntriesForClass(&classDef, packageNameToGenerate,
-                                                  package.get(), privType);
+                    result = addMembersToTypeClass(packageNameToGenerate, package.get(), privType,
+                                                   classDef.get());
                     if (!result) {
                         return false;
                     }
                 }
             }
 
-            AnnotationProcessor processor;
             if (type->type == ResourceType::kStyleable &&
                     mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
                 // When generating a public R class, we don't want Styleable to be part of the API.
                 // It is only emitted for documentation purposes.
-                processor.appendComment("@doconly");
+                AnnotationProcessor* processor = classDef->getCommentBuilder();
+                processor->appendComment("@doconly");
             }
-            classDef.writeToStream(out, "  ", &processor);
+
+            rClass.addMember(std::move(classDef));
         }
     }
 
-    *out << "}\n";
+    if (!ClassDefinition::writeJavaFile(&rClass, util::utf16ToUtf8(outPackageName),
+                                        mOptions.useFinal, out)) {
+        return false;
+    }
+
     out->flush();
     return true;
 }
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 7e46f8c..b594a88 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -28,7 +28,7 @@
 namespace aapt {
 
 class AnnotationProcessor;
-class ClassDefinitionWriter;
+class ClassDefinition;
 
 struct JavaClassGeneratorOptions {
     /*
@@ -70,16 +70,15 @@
     const std::string& getError() const;
 
 private:
-    bool writeEntriesForClass(ClassDefinitionWriter* outClassDef,
-                              const StringPiece16& packageNameToGenerate,
-                              const ResourceTablePackage* package,
-                              const ResourceTableType* type);
+    bool addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
+                               const ResourceTablePackage* package,
+                               const ResourceTableType* type,
+                               ClassDefinition* outTypeClassDef);
 
-    void writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
-                                     AnnotationProcessor* processor,
-                                     const StringPiece16& packageNameToGenerate,
-                                     const std::u16string& entryName,
-                                     const Styleable* styleable);
+    void addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
+                                    const std::u16string& entryName,
+                                    const Styleable* styleable,
+                                    ClassDefinition* outStyleableClassDef);
 
     bool skipSymbol(SymbolState state);
 
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index a9b4c14..be8955e 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -16,7 +16,7 @@
 
 #include "Source.h"
 #include "java/AnnotationProcessor.h"
-#include "java/ClassDefinitionWriter.h"
+#include "java/ClassDefinition.h"
 #include "java/ManifestClassGenerator.h"
 #include "util/Maybe.h"
 #include "xml/XmlDom.h"
@@ -58,8 +58,8 @@
     return result;
 }
 
-static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, const Source& source,
-                        xml::Element* el) {
+static bool writeSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
+                        ClassDefinition* classDef) {
     xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name");
     if (!attr) {
         diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
@@ -72,54 +72,53 @@
         return false;
     }
 
-    AnnotationProcessor processor;
-    processor.appendComment(el->comment);
-    outClassDef->addStringMember(result.value(), &processor, attr->value);
+    std::unique_ptr<StringMember> stringMember = util::make_unique<StringMember>(
+            util::utf16ToUtf8(result.value()), util::utf16ToUtf8(attr->value));
+    stringMember->getCommentBuilder()->appendComment(el->comment);
+
+    classDef->addMember(std::move(stringMember));
     return true;
 }
 
-bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
-                                      xml::XmlResource* res, std::ostream* out) {
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
     xml::Element* el = xml::findRootElement(res->root.get());
     if (!el) {
-        return false;
+        diag->error(DiagMessage(res->file.source) << "no root tag defined");
+        return {};
     }
 
     if (el->name != u"manifest" && !el->namespaceUri.empty()) {
         diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
-        return false;
+        return {};
     }
 
-    *out << "package " << package << ";\n\n"
-         << "public final class Manifest {\n";
+    std::unique_ptr<ClassDefinition> permissionClass =
+            util::make_unique<ClassDefinition>("permission", ClassQualifier::Static, false);
+    std::unique_ptr<ClassDefinition> permissionGroupClass =
+            util::make_unique<ClassDefinition>("permission_group", ClassQualifier::Static, false);
 
     bool error = false;
+
     std::vector<xml::Element*> children = el->getChildElements();
-
-    ClassDefinitionWriterOptions classOptions;
-    classOptions.useFinalQualifier = true;
-    classOptions.forceCreationIfEmpty = false;
-
-    // First write out permissions.
-    ClassDefinitionWriter classDef("permission", classOptions);
     for (xml::Element* childEl : children) {
-        if (childEl->namespaceUri.empty() && childEl->name == u"permission") {
-            error |= !writeSymbol(diag, &classDef, res->file.source, childEl);
+        if (childEl->namespaceUri.empty()) {
+            if (childEl->name == u"permission") {
+                error |= !writeSymbol(res->file.source, diag, childEl, permissionClass.get());
+            } else if (childEl->name == u"permission-group") {
+                error |= !writeSymbol(res->file.source, diag, childEl, permissionGroupClass.get());
+            }
         }
     }
-    classDef.writeToStream(out, "  ");
 
-    // Next write out permission groups.
-    classDef = ClassDefinitionWriter("permission_group", classOptions);
-    for (xml::Element* childEl : children) {
-        if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") {
-            error |= !writeSymbol(diag, &classDef, res->file.source, childEl);
-        }
+    if (error) {
+        return {};
     }
-    classDef.writeToStream(out, "  ");
 
-    *out << "}\n";
-    return !error;
+    std::unique_ptr<ClassDefinition> manifestClass =
+            util::make_unique<ClassDefinition>("Manifest", ClassQualifier::None, false);
+    manifestClass->addMember(std::move(permissionClass));
+    manifestClass->addMember(std::move(permissionGroupClass));
+    return manifestClass;
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index 226ed23..f565289 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -18,6 +18,7 @@
 #define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
 
 #include "Diagnostics.h"
+#include "java/ClassDefinition.h"
 #include "util/StringPiece.h"
 #include "xml/XmlDom.h"
 
@@ -25,10 +26,7 @@
 
 namespace aapt {
 
-struct ManifestClassGenerator {
-    bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res,
-                  std::ostream* out);
-};
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
 
 } // namespace aapt
 
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index fc57ae6f..a9ec318 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -22,6 +22,23 @@
 
 namespace aapt {
 
+static ::testing::AssertionResult getManifestClassText(IAaptContext* context, xml::XmlResource* res,
+                                                       std::string* outStr) {
+    std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
+            context->getDiagnostics(), res);
+    if (!manifestClass) {
+        return ::testing::AssertionFailure() << "manifestClass == nullptr";
+    }
+
+    std::stringstream out;
+    if (!manifestClass->writeJavaFile(manifestClass.get(), "android", true, &out)) {
+        return ::testing::AssertionFailure() << "failed to write java file";
+    }
+
+    *outStr = out.str();
+    return ::testing::AssertionSuccess();
+}
+
 TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
     std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
     std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
@@ -32,11 +49,8 @@
           <permission-group android:name="foo.bar.PERMISSION" />
         </manifest>)EOF");
 
-    std::stringstream out;
-    ManifestClassGenerator generator;
-    ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
-
-    std::string actual = out.str();
+    std::string actual;
+    ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
 
     const size_t permissionClassPos = actual.find("public static final class permission {");
     const size_t permissionGroupClassPos =
@@ -87,11 +101,8 @@
           <permission android:name="android.permission.SECRET" />
         </manifest>)EOF");
 
-    std::stringstream out;
-    ManifestClassGenerator generator;
-    ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
-
-    std::string actual = out.str();
+    std::string actual;
+    ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
 
     EXPECT_NE(std::string::npos, actual.find(
 R"EOF(    /**
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index b84074d..8c10fbb 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -762,9 +762,24 @@
             return true;
         }
 
+        std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
+                mContext->getDiagnostics(), manifestXml);
+
+        if (!manifestClass) {
+            // Something bad happened, but we already logged it, so exit.
+            return false;
+        }
+
+        if (manifestClass->empty()) {
+            // Empty Manifest class, no need to generate it.
+            return true;
+        }
+
+        const std::string packageUtf8 = util::utf16ToUtf8(mContext->getCompilationPackage());
+
         std::string outPath = mOptions.generateJavaClassPath.value();
-        file::appendPath(&outPath,
-                         file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
+        file::appendPath(&outPath, file::packageToPath(packageUtf8));
+
         if (!file::mkdirs(outPath)) {
             mContext->getDiagnostics()->error(
                     DiagMessage() << "failed to create directory '" << outPath << "'");
@@ -780,13 +795,7 @@
             return false;
         }
 
-        ManifestClassGenerator generator;
-        if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
-                                manifestXml, &fout)) {
-            return false;
-        }
-
-        if (!fout) {
+        if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
             mContext->getDiagnostics()->error(
                     DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
             return false;
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index 75e837b..b5ed1b5 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -177,9 +177,10 @@
 
 def check_emoji_availability():
     emoji_fonts = [font[5] for font in _fallback_chain if 'Zsye' in font[1]]
+    assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
+    emoji_font = emoji_fonts[0]
     emoji_chars = _emoji_properties['Emoji']
-    for emoji_font in emoji_fonts:
-        assert_font_supports_all_of_chars(emoji_font, emoji_chars)
+    assert_font_supports_all_of_chars(emoji_font, emoji_chars)
 
 
 def check_emoji_defaults():
@@ -273,11 +274,12 @@
     hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
     check_hyphens(hyphens_dir)
 
-    ucd_path = sys.argv[2]
-    parse_ucd(ucd_path)
-    # Temporarily disable emoji checks for Bug 27785690
-    # check_emoji_availability()
-    # check_emoji_defaults()
+    check_emoji = sys.argv[2]
+    if check_emoji == 'true':
+        ucd_path = sys.argv[3]
+        parse_ucd(ucd_path)
+        check_emoji_availability()
+        check_emoji_defaults()
 
 
 if __name__ == '__main__':
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 7faee1b..f9e008e 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -403,8 +403,15 @@
     }
 
     @Override
-    public void setNewConfiguration(Configuration arg0) throws RemoteException {
+    public int[] setNewConfiguration(Configuration arg0) throws RemoteException {
         // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Rect getBoundsForNewConfiguration(int stackId) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
     }
 
     @Override